From ca9224ed7f8813b2eb9bd4cd70e641308c2d4685 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 21 May 2012 10:58:04 +0200 Subject: [PATCH 001/688] starting scale function --- apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/world.cpp | 5 +++++ apps/openmw/mwworld/world.hpp | 2 ++ 4 files changed, 14 insertions(+) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 5fb503847..f1c50c29a 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -196,4 +196,9 @@ namespace MWWorld { return ""; } + + void adjustScale(float& x, float& y, float& z) + { + } + } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 513dc942b..0fc12bfe2 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -193,6 +193,8 @@ namespace MWWorld virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; ///< @return the enchantment ID if the object is enchanted, otherwise an empty string /// (default implementation: return empty string) + + virtual void adjustScale(float& x, float& y, float& z); }; } diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index eeda92277..fbbeef154 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -611,6 +611,11 @@ namespace MWWorld mPhysics->moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z)); } + void World::scaleObject (Ptr ptr, float x, float y, float z) + { + MWWorld::Class::get(ptr).adjustScale(x,y,z); + } + void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { const int cellSize = 8192; diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 49a3cf029..cb849cc1b 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -222,6 +222,8 @@ namespace MWWorld void moveObject (Ptr ptr, float x, float y, float z); + void scaleObject (Ptr ptr, float x, float y, float z); + void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; ///< Convert cell numbers to position. From 0066ef9b5491604a34a3fb69d27cbc8e61dab421 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Tue, 22 May 2012 21:40:24 -0400 Subject: [PATCH 002/688] Some cleanup --- apps/openmw/mwworld/physicssystem.cpp | 40 ++++++--------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 808c712a0..33024cfde 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -140,8 +140,6 @@ namespace MWWorld iter!=actors.end(); ++iter) { OEngine::Physic::PhysicActor* act = mEngine->getCharacter(iter->first); - //if(iter->first == "player") - // std::cout << "This is player\n"; //dirty stuff to get the camera orientation. Must be changed! Ogre::SceneNode *sceneNode = mRender.getScene()->getSceneNode (iter->first); @@ -151,46 +149,28 @@ namespace MWWorld Ogre::Quaternion yawQuat = yawNode->getOrientation(); Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); - // unused - //Ogre::Quaternion both = yawQuat * pitchQuat; + playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); - playerphysics->ps.viewangles.z = 0; + playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() *-1 + 90; - if(mFreeFly) - { - Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); - - pm_ref.rightmove = -dir1.x; - pm_ref.forwardmove = dir1.z; - pm_ref.upmove = dir1.y; - - - //std::cout << "Current angle" << yawQuat.getYaw().valueDegrees() - 90<< "\n"; - //playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); - //std::cout << "Pitch: " << yawQuat.getPitch() << "Yaw:" << yawQuat.getYaw() << "Roll: " << yawQuat.getRoll() << "\n"; - dir = 0.07*(yawQuat*pitchQuat*dir1); - } - else - { Ogre::Quaternion quat = yawNode->getOrientation(); Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); - pm_ref.rightmove = -dir1.x; - pm_ref.forwardmove = dir1.z; - pm_ref.upmove = dir1.y; + pm_ref.rightmove = -iter->second.x; + pm_ref.forwardmove = -iter->second.y; + pm_ref.upmove = iter->second.z; - dir = 0.025*(quat*dir1); } - //set the walk direction - act->setWalkDirection(btVector3(dir.x,-dir.z,dir.y)); - } + + + mEngine->stepSimulation(dt); } @@ -208,10 +188,6 @@ namespace MWWorld if(it->first == "player"){ coord = playerphysics->ps.origin; - //std::cout << "ZCoord: " << coord.z << "\n"; - //std::cout << "Coord" << coord << "\n"; - //coord = Ogre::Vector3(coord.x, coord.z, coord.y); //x, z, -y - } From 51b6d5cae055a6e24fc16f706b2246950052e55b Mon Sep 17 00:00:00 2001 From: gugus Date: Fri, 25 May 2012 18:23:06 +0200 Subject: [PATCH 003/688] Scale *should* work. (no script instruction yet) --- apps/openmw/mwworld/class.cpp | 5 ++++- apps/openmw/mwworld/class.hpp | 4 +++- apps/openmw/mwworld/world.cpp | 16 ++++++++++++++-- apps/openmw/mwworld/world.hpp | 4 +++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6a6765253..4064a451a 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -203,8 +203,11 @@ namespace MWWorld return ""; } - void adjustScale(float& x, float& y, float& z) + void Class::adjustScale(const MWWorld::Ptr& ptr,float& scale) const { } + void Class::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const + { + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 0ed0b0e0d..a8639daa9 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -197,7 +197,9 @@ namespace MWWorld ///< @return the enchantment ID if the object is enchanted, otherwise an empty string /// (default implementation: return empty string) - virtual void adjustScale(float& x, float& y, float& z); + virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; + + virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; }; } diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index c2926c1a3..6be4a13ba 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -611,9 +611,21 @@ namespace MWWorld mPhysics->moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z)); } - void World::scaleObject (Ptr ptr, float x, float y, float z) + void World::scaleObject (Ptr ptr, float scale) + { + MWWorld::Class::get(ptr).adjustScale(ptr,scale); + + ptr.getCellRef().scale = scale; + scale = scale/ptr.getRefData().getBaseNode()->getScale().x; + ptr.getRefData().getBaseNode()->setScale(scale,scale,scale); + mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); + /// \todo cell change for non-player ref + + //mRendering->moveObject (ptr, Ogre::Vector3 (x, y, z)); + } + + void World::rotateObject (Ptr ptr,float x,float y,float z) { - MWWorld::Class::get(ptr).adjustScale(x,y,z); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index cc6a665fc..e684ca39e 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -222,7 +222,9 @@ namespace MWWorld void moveObject (Ptr ptr, float x, float y, float z); - void scaleObject (Ptr ptr, float x, float y, float z); + void scaleObject (Ptr ptr, float scale); + + void rotateObject (Ptr ptr,float x,float y,float z); void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; ///< Convert cell numbers to position. From 77084b27c07b5948717638d6a8c4f8d93bca9259 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 28 May 2012 16:01:35 +0200 Subject: [PATCH 004/688] some work for rotation/scaling --- apps/openmw/mwscript/statsextensions.cpp | 16 ++++++++++++++++ apps/openmw/mwworld/world.cpp | 10 +++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index d23519281..6e9b1e583 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -500,6 +500,22 @@ namespace MWScript } }; + template + class OpSetScale : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Float scale = runtime[0].mInteger; + runtime.pop(); + + MWBase::Environment::get().getWorld()->scaleObject(ptr,scale); + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 6be4a13ba..2a3332593 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -619,13 +619,17 @@ namespace MWWorld scale = scale/ptr.getRefData().getBaseNode()->getScale().x; ptr.getRefData().getBaseNode()->setScale(scale,scale,scale); mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); - /// \todo cell change for non-player ref - - //mRendering->moveObject (ptr, Ogre::Vector3 (x, y, z)); } void World::rotateObject (Ptr ptr,float x,float y,float z) { + MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); + + ptr.getRefData().getPosition().rot[0] = x; + ptr.getRefData().getPosition().rot[0] = y; + ptr.getRefData().getPosition().rot[0] = z; + //ptr.getRefData().getBaseNode()->rotate(ptr.getRefData().getBaseNode()->get + //mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const From 26d6c9453c3d1d80936189cce56a7eb38a148946 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 29 May 2012 10:15:29 +0200 Subject: [PATCH 005/688] more work on rotation --- apps/openmw/mwworld/world.cpp | 44 ++++++++++++++++++++++++++++++++--- apps/openmw/mwworld/world.hpp | 4 +++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 2a3332593..116710a1e 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -621,17 +621,55 @@ namespace MWWorld mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); } - void World::rotateObject (Ptr ptr,float x,float y,float z) + void World::rotateObject (Ptr ptr,float x,float y,float z,bool WorldAxis) { MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); - ptr.getRefData().getPosition().rot[0] = x; + /*ptr.getRefData().getPosition().rot[0] = x; ptr.getRefData().getPosition().rot[0] = y; - ptr.getRefData().getPosition().rot[0] = z; + ptr.getRefData().getPosition().rot[0] = z;*/ + if(WorldAxis) + { + ptr.getRefData().getBaseNode()->rotate(Ogre::Vector3::UNIT_X,Ogre::Degree(x)); + ptr.getRefData().getBaseNode()->rotate(Ogre::Vector3::UNIT_X,Ogre::Degree(y)); + ptr.getRefData().getBaseNode()->rotate(Ogre::Vector3::UNIT_X,Ogre::Degree(z)); + } + else + { + Ogre::Matrix3 axis = ptr.getRefData().getBaseNode()->getLocalAxes(); + Ogre::Vector3 xAxis = axis.GetColumn(0); + Ogre::Vector3 yAxis = axis.GetColumn(1); + Ogre::Vector3 zAxis = axis.GetColumn(2); + ptr.getRefData().getBaseNode()->rotate(xAxis,Ogre::Degree(x)); + ptr.getRefData().getBaseNode()->rotate(yAxis,Ogre::Degree(y)); + ptr.getRefData().getBaseNode()->rotate(zAxis,Ogre::Degree(z)); + } + Ogre::Matrix3 rot; + ptr.getRefData().getBaseNode()->getOrientation().ToRotationMatrix(rot); + Ogre::Radian rx,ry,rz; + rot.ToEulerAnglesXYZ(rx,ry,rz); + + ptr.getRefData().getPosition().rot[0] = rx.valueRadians(); + ptr.getRefData().getPosition().rot[0] = ry.valueRadians(); + ptr.getRefData().getPosition().rot[0] = rz.valueRadians(); //ptr.getRefData().getBaseNode()->rotate(ptr.getRefData().getBaseNode()->get //mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); } + void setObjectRotation (Ptr ptr,float x,float y,float z) + { + MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); + + ptr.getRefData().getPosition().rot[0] = Ogre::Degree(x).valueRadians(); + ptr.getRefData().getPosition().rot[0] = Ogre::Degree(y).valueRadians(); + ptr.getRefData().getPosition().rot[0] = Ogre::Degree(z).valueRadians(); + + Ogre::Quaternion rotx(Ogre::Degree(x),Ogre::Vector3::UNIT_X); + Ogre::Quaternion roty(Ogre::Degree(y),Ogre::Vector3::UNIT_Y); + Ogre::Quaternion rotz(Ogre::Degree(z),Ogre::Vector3::UNIT_Z); + ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); + } + void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { const int cellSize = 8192; diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index e684ca39e..6f810e976 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -224,7 +224,9 @@ namespace MWWorld void scaleObject (Ptr ptr, float scale); - void rotateObject (Ptr ptr,float x,float y,float z); + void rotateObject (Ptr ptr,float x,float y,float z,bool WorldAxis); + + void setObjectRotation (Ptr ptr,float x,float y,float z); void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; ///< Convert cell numbers to position. From d8051095d669c552123f4662ea501c9cd021e6bd Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 29 May 2012 10:36:12 +0200 Subject: [PATCH 006/688] rotation are updated in the physis system --- apps/openmw/mwworld/physicssystem.cpp | 13 +++++++++++-- apps/openmw/mwworld/world.cpp | 8 ++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 808c712a0..c4e34a540 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -288,17 +288,26 @@ namespace MWWorld void PhysicsSystem::rotateObject (const std::string& handle, const Ogre::Quaternion& rotation) { - if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow // start positions others than 0, 0, 0 act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); } + if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) + { + body->setWorldTransform(btTransform(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w),body->getWorldTransform().getOrigin())); + } } void PhysicsSystem::scaleObject (const std::string& handle, float scale) { - + if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) + { + // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow + // start positions others than 0, 0, 0 + //body->setWorldTransform(btTransform().se + } } bool PhysicsSystem::toggleCollisionMode() diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 116710a1e..69da98096 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -625,9 +625,6 @@ namespace MWWorld { MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); - /*ptr.getRefData().getPosition().rot[0] = x; - ptr.getRefData().getPosition().rot[0] = y; - ptr.getRefData().getPosition().rot[0] = z;*/ if(WorldAxis) { ptr.getRefData().getBaseNode()->rotate(Ogre::Vector3::UNIT_X,Ogre::Degree(x)); @@ -652,11 +649,13 @@ namespace MWWorld ptr.getRefData().getPosition().rot[0] = rx.valueRadians(); ptr.getRefData().getPosition().rot[0] = ry.valueRadians(); ptr.getRefData().getPosition().rot[0] = rz.valueRadians(); + + mPhysics->rotateObject(Class::get(ptr).getId(ptr),ptr.getRefData().getBaseNode()->getOrientation()); //ptr.getRefData().getBaseNode()->rotate(ptr.getRefData().getBaseNode()->get //mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); } - void setObjectRotation (Ptr ptr,float x,float y,float z) + void World::setObjectRotation (Ptr ptr,float x,float y,float z) { MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); @@ -668,6 +667,7 @@ namespace MWWorld Ogre::Quaternion roty(Ogre::Degree(y),Ogre::Vector3::UNIT_Y); Ogre::Quaternion rotz(Ogre::Degree(z),Ogre::Vector3::UNIT_Z); ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); + mPhysics->rotateObject(Class::get(ptr).getId(ptr),ptr.getRefData().getBaseNode()->getOrientation()); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const From f83ffecae5cf5433069f355f5d41b3c87386c845 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 29 May 2012 15:57:13 +0200 Subject: [PATCH 007/688] SetAngle and SetScale script instruction --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++- apps/openmw/mwscript/statsextensions.cpp | 38 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 0920c72f8..bb8f8a214 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -146,4 +146,6 @@ op 0x200014f: ForceGreeting op 0x2000150: ForceGreeting, explicit reference op 0x2000151: ToggleFullHelp op 0x2000152: Goodbye -opcodes 0x2000153-0x3ffffff unused +op 0x2000153: SetScale +op 0x2000154: SetAngle +opcodes 0x2000155-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 6e9b1e583..c68febec3 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -516,6 +516,35 @@ namespace MWScript } }; + template + class OpSetAngle : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float angle = runtime[0].mInteger; + runtime.pop(); + + if(axis == "X") + { + MWBase::Environment::get().getWorld()->setObjectRotation(ptr,angle,0,0); + } + if(axis == "Y") + { + MWBase::Environment::get().getWorld()->setObjectRotation(ptr,0,angle,0); + } + if(axis == "Z") + { + MWBase::Environment::get().getWorld()->setObjectRotation(ptr,0,0,angle); + } + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; @@ -562,6 +591,9 @@ namespace MWScript const int opcodeModDisposition = 0x200014d; const int opcodeModDispositionExplicit = 0x200014e; + const int opcodeSetScale = 0x2000153; + const int opcodeSetAngle = 0x2000154; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -644,6 +676,9 @@ namespace MWScript extensions.registerInstruction("moddisposition","l",opcodeModDisposition, opcodeModDispositionExplicit); extensions.registerFunction("getpcrank",'l',"/S",opcodeGetPCRank,opcodeGetPCRankExplicit); + + extensions.registerInstruction("setscale","/l",opcodeSetScale); + extensions.registerInstruction("setangle","/Sl",opcodeSetAngle); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -715,6 +750,9 @@ namespace MWScript interpreter.installSegment5(opcodeModDispositionExplicit,new OpModDisposition); interpreter.installSegment3(opcodeGetPCRank,new OpGetPCRank); interpreter.installSegment3(opcodeGetPCRankExplicit,new OpGetPCRank); + + interpreter.installSegment5(opcodeSetScale,new OpSetScale); + interpreter.installSegment5(opcodeSetAngle,new OpSetAngle); } } } From a711a3ebe1b67886a58b004ddc13e5bbfc446fa2 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 29 May 2012 16:45:43 +0200 Subject: [PATCH 008/688] Various fixes --- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/statsextensions.cpp | 10 +++++++--- apps/openmw/mwworld/physicssystem.cpp | 4 ---- apps/openmw/mwworld/world.cpp | 1 + libs/openengine/bullet/physic.cpp | 24 ++++++++++++++++++++---- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index bb8f8a214..86cf73117 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -147,5 +147,7 @@ op 0x2000150: ForceGreeting, explicit reference op 0x2000151: ToggleFullHelp op 0x2000152: Goodbye op 0x2000153: SetScale -op 0x2000154: SetAngle +op 0x2000154: SetScale, explicit reference +op 0x2000155: SetAngle +op 0x2000156: SetAngle, explicit reference opcodes 0x2000155-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index c68febec3..83bfacf40 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -592,7 +592,9 @@ namespace MWScript const int opcodeModDispositionExplicit = 0x200014e; const int opcodeSetScale = 0x2000153; - const int opcodeSetAngle = 0x2000154; + const int opcodeSetScaleExplicit = 0x2000154; + const int opcodeSetAngle = 0x2000155; + const int opcodeSetAngleExplicit = 0x2000156; void registerExtensions (Compiler::Extensions& extensions) { @@ -677,8 +679,8 @@ namespace MWScript opcodeModDispositionExplicit); extensions.registerFunction("getpcrank",'l',"/S",opcodeGetPCRank,opcodeGetPCRankExplicit); - extensions.registerInstruction("setscale","/l",opcodeSetScale); - extensions.registerInstruction("setangle","/Sl",opcodeSetAngle); + extensions.registerInstruction("setscale","l",opcodeSetScale,opcodeSetScaleExplicit); + extensions.registerInstruction("setangle","Sl",opcodeSetAngle,opcodeSetAngleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -752,7 +754,9 @@ namespace MWScript interpreter.installSegment3(opcodeGetPCRankExplicit,new OpGetPCRank); interpreter.installSegment5(opcodeSetScale,new OpSetScale); + interpreter.installSegment5(opcodeSetScaleExplicit,new OpSetScale); interpreter.installSegment5(opcodeSetAngle,new OpSetAngle); + interpreter.installSegment5(opcodeSetAngleExplicit,new OpSetAngle); } } } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index c4e34a540..8ebdf2981 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -290,8 +290,6 @@ namespace MWWorld { if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow - // start positions others than 0, 0, 0 act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); } if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) @@ -304,8 +302,6 @@ namespace MWWorld { if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow - // start positions others than 0, 0, 0 //body->setWorldTransform(btTransform().se } } diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 69da98096..db96b6a19 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -668,6 +668,7 @@ namespace MWWorld Ogre::Quaternion rotz(Ogre::Degree(z),Ogre::Vector3::UNIT_Z); ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); mPhysics->rotateObject(Class::get(ptr).getId(ptr),ptr.getRefData().getBaseNode()->getOrientation()); + std::cout << Class::get(ptr).getId(ptr); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d30d5e9f1..4ead88994 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -392,8 +392,16 @@ namespace Physic RigidBody* PhysicEngine::getRigidBody(std::string name) { - RigidBody* body = RigidBodyMap[name]; - return body; + RigidBodyContainer::iterator it = RigidBodyMap.find(name); + if (it != RigidBodyMap.end() ) + { + RigidBody* body = RigidBodyMap[name]; + return body; + } + else + { + return 0; + } } void PhysicEngine::stepSimulation(double deltaT) @@ -450,8 +458,16 @@ namespace Physic PhysicActor* PhysicEngine::getCharacter(std::string name) { - PhysicActor* act = PhysicActorMap[name]; - return act; + PhysicActorContainer::iterator it = PhysicActorMap.find(name); + if (it != PhysicActorMap.end() ) + { + PhysicActor* act = PhysicActorMap[name]; + return act; + } + else + { + return 0; + } } void PhysicEngine::emptyEventLists(void) From fb4e7f02b9c7305d0d6140b193fd0c21b152ed8b Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Tue, 29 May 2012 22:03:00 -0400 Subject: [PATCH 009/688] code correction --- apps/openmw/mwclass/npc.cpp | 6 +++--- libs/openengine/bullet/pmove.cpp | 8 +++++--- libs/openengine/bullet/pmove.h | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e7f6cc527..b1d61a4ed 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -119,7 +119,7 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - + ESMS::LiveCellRef *ref = ptr.get(); @@ -128,12 +128,12 @@ namespace MWClass std::string headID = ref->base->head; std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; - + std::string smodel = "meshes\\base_anim.nif"; if(beast) smodel = "meshes\\base_animkna.nif"; - physics.insertActorPhysics(ptr, smodel); + physics.insertObjectPhysics(ptr, smodel); } diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 591f1869f..cdeb557ba 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -232,7 +232,7 @@ bool PM_SlideMove( bool gravity ) end = pm->ps.origin + pm->ps.velocity * time_left; // see if we can make it there - //pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); + //pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemaskg); //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&(end), *(const D3DXVECTOR3* const)&(pm->ps.velocity), 0, pml.traceObj); newtrace(&trace, pm->ps.origin, end, halfExtents, Ogre::Math::DegreesToRadians (pm->ps.viewangles.y), pm->isInterior, pm->mEngine); @@ -277,7 +277,7 @@ bool PM_SlideMove( bool gravity ) { // pm->ps->velocity += (trace.plane.normal + pm->ps->velocity) //VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); - pm->ps.velocity = (trace.planenormal + pm->ps.velocity); + pm->ps.velocity = trace.planenormal + pm->ps.velocity; break; } } @@ -330,7 +330,7 @@ bool PM_SlideMove( bool gravity ) if (clipVelocity.dotProduct(planes[i]) >= 0) //if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) continue; - + // slide the original velocity along the crease //dProduct (planes[i], planes[j], dir); @@ -363,6 +363,7 @@ bool PM_SlideMove( bool gravity ) // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { + if ( k == i || k == j ) continue; @@ -1460,6 +1461,7 @@ static void PM_GroundTrace( void ) // slopes that are too steep will not be considered onground //if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) + //std::cout << "MinWalkNormal" << trace.planenormal.z; if (trace.planenormal.z < MIN_WALK_NORMAL) { //if ( pm->debugLevel ) diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index e46eb9d2e..6e67db711 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -41,7 +41,7 @@ static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); #define MAX_GENTITIES (1 << GENTITYNUM_BITS) #define ENTITYNUM_NONE (MAX_GENTITIES - 1) #define ENTITYNUM_WORLD (MAX_GENTITIES - 2) -#define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes +#define MIN_WALK_NORMAL .7f // can't walk on very steep slopes #define JUMP_VELOCITY (270) #define PS_PMOVEFRAMECOUNTBITS 6 #define MINS_Z -24 @@ -90,7 +90,7 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(800.0f), speed(480.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + playerStruct() : gravity(800.0f), speed(500.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) { origin = Ogre::Vector3(733.164f,900.0f, 839.432f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); From d081f7ea83d55f1941f3bc953a0c44457afc5f6f Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Thu, 31 May 2012 20:13:40 -0400 Subject: [PATCH 010/688] Don't run up walls --- libs/openengine/bullet/pmove.cpp | 15 ++++++++++++++- libs/openengine/bullet/pmove.h | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index cdeb557ba..1fc394259 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -301,6 +301,12 @@ bool PM_SlideMove( bool gravity ) if ( into >= 0.1 ) continue; // move doesn't interact with the plane + std::cout << "Second plane" << planes[i] << "\n"; + if(planes[i].x >= .70) + { + pm->ps.velocity = Ogre::Vector3(0,0,0); + return true; + } // see how hard we are hitting things if ( -into > pml.impactSpeed ) pml.impactSpeed = -into; @@ -321,6 +327,13 @@ bool PM_SlideMove( bool gravity ) if (clipVelocity.dotProduct(planes[j]) >= 0.1) //if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) continue; // move doesn't interact with the plane + + + + + //pm->ps.velocity = Ogre::Vector3(0,0,0); + //return true; + // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); @@ -330,8 +343,8 @@ bool PM_SlideMove( bool gravity ) if (clipVelocity.dotProduct(planes[i]) >= 0) //if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) continue; + - // slide the original velocity along the crease //dProduct (planes[i], planes[j], dir); dir = planes[i].crossProduct(planes[j]) ; diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index 6e67db711..d48bd3637 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -90,7 +90,7 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(800.0f), speed(500.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + playerStruct() : gravity(800.0f), speed(2000.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) { origin = Ogre::Vector3(733.164f,900.0f, 839.432f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); From 4ff36a9018834f35fa034594d76d47bfc86d2ea1 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 6 Jun 2012 16:29:44 -0400 Subject: [PATCH 011/688] Bullet loader trafos changed to match NIFLoader --- components/nifbullet/bullet_nif_loader.cpp | 97 +++++++++++++++++----- components/nifbullet/bullet_nif_loader.hpp | 8 +- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 30cb4562d..153c52934 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -1,4 +1,4 @@ -/* + /* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2008-2010 Nicolay Korslund Email: < korslund@gmail.com > @@ -51,19 +51,64 @@ using namespace Mangle::VFS; using namespace NifBullet; +// Helper math functions. Reinventing linear algebra for the win! + +// Computes B = AxB (matrix*matrix) +static void matrixMul(const Matrix &A, Matrix &B) +{ + for (int i=0;i<3;i++) + { + float a = B.v[0].array[i]; + float b = B.v[1].array[i]; + float c = B.v[2].array[i]; + + B.v[0].array[i] = a*A.v[0].array[0] + b*A.v[0].array[1] + c*A.v[0].array[2]; + B.v[1].array[i] = a*A.v[1].array[0] + b*A.v[1].array[1] + c*A.v[1].array[2]; + B.v[2].array[i] = a*A.v[2].array[0] + b*A.v[2].array[1] + c*A.v[2].array[2]; + } +} + +// Computes C = B + AxC*scale +static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale) +{ + // Keep the original values + float a = C[0]; + float b = C[1]; + float c = C[2]; + + // Perform matrix multiplication, scaling and addition + for (int i=0;i<3;i++) + C[i] = B.array[i] + (a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2])*scale; +} + +// Computes B = AxB (matrix*vector) +static void vectorMul(const Matrix &A, float *C) +{ + // Keep the original values + float a = C[0]; + float b = C[1]; + float c = C[2]; + + // Perform matrix multiplication, scaling and addition + for (int i=0;i<3;i++) + C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2]; +} + + ManualBulletShapeLoader::~ManualBulletShapeLoader() { delete vfs; } -Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(Nif::Transformation* tr) + +Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(const Nif::Transformation* tr) { Ogre::Matrix3 rot(tr->rotation.v[0].array[0],tr->rotation.v[0].array[1],tr->rotation.v[0].array[2], tr->rotation.v[1].array[0],tr->rotation.v[1].array[1],tr->rotation.v[1].array[2], tr->rotation.v[2].array[0],tr->rotation.v[2].array[1],tr->rotation.v[2].array[2]); return rot; } -Ogre::Vector3 ManualBulletShapeLoader::getVector(Nif::Transformation* tr) +Ogre::Vector3 ManualBulletShapeLoader::getVector(const Nif::Transformation* tr) { Ogre::Vector3 vect3(tr->pos.array[0],tr->pos.array[1],tr->pos.array[2]); return vect3; @@ -88,6 +133,7 @@ btVector3 ManualBulletShapeLoader::getbtVector(Nif::Vector v) void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) { + std::cout << "Beginning physics\n"; cShape = static_cast(resource); resourceName = cShape->getName(); cShape->collide = false; @@ -131,12 +177,12 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) bool hasCollisionNode = hasRootCollisionNode(node); //do a first pass - handleNode(node,0,Ogre::Matrix3::IDENTITY,Ogre::Vector3::ZERO,1,hasCollisionNode,false,false); + handleNode(node,0,NULL,hasCollisionNode,false,false); //if collide = false, then it does a second pass which create a shape for raycasting. if(cShape->collide == false) { - handleNode(node,0,Ogre::Matrix3::IDENTITY,Ogre::Vector3::ZERO,1,hasCollisionNode,false,true); + handleNode(node,0,NULL,hasCollisionNode,false,true); } cShape->collide = hasCollisionNode&&cShape->collide; @@ -157,6 +203,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) currentShape = new TriangleMeshShape(mTriMesh,true); cShape->Shape = currentShape; + std::cout << "End bullet physics\n"; } bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) @@ -186,8 +233,9 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) } void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, - Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) + const Nif::Transformation *trafo,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) { + std::cout << "Handle Node\n"; // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; @@ -221,19 +269,30 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, } } - //transfo of parents node + curent node - Ogre::Matrix3 finalRot; - Ogre::Vector3 finalPos; - float finalScale; + + + if (trafo) + { + std::cout << "Before trafo\n"; + // Get a non-const reference to the node's data, since we're + // overwriting it. TODO: Is this necessary? + Transformation &final = *((Transformation*)node->trafo); - Nif::Transformation &final = *((Nif::Transformation*)node->trafo); - Ogre::Vector3 nodePos = getVector(&final); - Ogre::Matrix3 nodeRot = getMatrix(&final); + // For both position and rotation we have that: + // final_vector = old_vector + old_rotation*new_vector*old_scale + vectorMulAdd(trafo->rotation, trafo->pos, final.pos.array, trafo->scale); + vectorMulAdd(trafo->rotation, trafo->velocity, final.velocity.array, trafo->scale); - finalPos = nodePos + parentPos; - finalRot = parentRot*nodeRot; - finalScale = final.scale*parentScale; + // Merge the rotations together + matrixMul(trafo->rotation, final.rotation); + // Scalar values are so nice to deal with. Why can't everything + // just be scalar? + final.scale *= trafo->scale; + std::cout << "After Trafo\n"; + } + + std::cout << "After Trafo2\n"; // For NiNodes, loop through children if (node->recType == Nif::RC_NiNode) @@ -244,14 +303,14 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, { if (list.has(i)) { - handleNode(&list[i], flags,finalRot,finalPos,finalScale,hasCollisionNode,isCollisionNode,raycastingOnly); + handleNode(&list[i], flags,node->trafo,hasCollisionNode,isCollisionNode,raycastingOnly); } } } else if (node->recType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) { cShape->collide = true; - handleNiTriShape(dynamic_cast(node), flags,finalRot,finalPos,parentScale,raycastingOnly); + handleNiTriShape(dynamic_cast(node), flags,getMatrix(node->trafo),getVector(node->trafo),node->trafo->scale,raycastingOnly); } else if(node->recType == Nif::RC_RootCollisionNode) { @@ -260,7 +319,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, for (int i=0; itrafo, hasCollisionNode,true,raycastingOnly); } } } diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index ed3aceac4..488a7a42b 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -95,9 +95,9 @@ public: void load(const std::string &name,const std::string &group); private: - Ogre::Matrix3 getMatrix(Nif::Transformation* tr); + Ogre::Matrix3 getMatrix(const Nif::Transformation* tr); - Ogre::Vector3 getVector(Nif::Transformation* tr); + Ogre::Vector3 getVector(const Nif::Transformation* tr); btQuaternion getbtQuat(Ogre::Matrix3 m); @@ -107,10 +107,10 @@ private: *Parse a node. */ void handleNode(Nif::Node *node, int flags, - Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly); + const Nif::Transformation *trafo, bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly); /** - *Helpler function + *Helper function */ bool hasRootCollisionNode(Nif::Node* node); From fb0a52809dd1da3ef953728be2200a633bb383bf Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 9 Jun 2012 15:19:15 -0400 Subject: [PATCH 012/688] Changing transformation processing --- apps/openmw/mwclass/npc.cpp | 2 +- components/nifbullet/bullet_nif_loader.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b1d61a4ed..0a1a750f7 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -133,7 +133,7 @@ namespace MWClass std::string smodel = "meshes\\base_anim.nif"; if(beast) smodel = "meshes\\base_animkna.nif"; - physics.insertObjectPhysics(ptr, smodel); + physics.insertActorPhysics(ptr, smodel); } diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 153c52934..e87d5624e 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -185,7 +185,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) handleNode(node,0,NULL,hasCollisionNode,false,true); } - cShape->collide = hasCollisionNode&&cShape->collide; + //cShape->collide = hasCollisionNode&&cShape->collide; struct TriangleMeshShape : public btBvhTriangleMeshShape { @@ -353,15 +353,17 @@ void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape *shape, int flags float* vertices = (float*)data->vertices.ptr; unsigned short* triangles = (unsigned short*)data->triangles.ptr; - + const Matrix &rot = shape->trafo->rotation; + const Vector &pos = shape->trafo->pos; + float scale = shape->trafo->scale; for(unsigned int i=0; i < data->triangles.length; i = i+3) { Ogre::Vector3 b1(vertices[triangles[i+0]*3]*parentScale,vertices[triangles[i+0]*3+1]*parentScale,vertices[triangles[i+0]*3+2]*parentScale); Ogre::Vector3 b2(vertices[triangles[i+1]*3]*parentScale,vertices[triangles[i+1]*3+1]*parentScale,vertices[triangles[i+1]*3+2]*parentScale); Ogre::Vector3 b3(vertices[triangles[i+2]*3]*parentScale,vertices[triangles[i+2]*3+1]*parentScale,vertices[triangles[i+2]*3+2]*parentScale); - b1 = parentRot * b1 + parentPos; - b2 = parentRot * b2 + parentPos; - b3 = parentRot * b3 + parentPos; + vectorMulAdd(rot, pos, b1.ptr(), scale); + vectorMulAdd(rot, pos, b2.ptr(), scale); + vectorMulAdd(rot, pos, b3.ptr(), scale); mTriMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); } } From bef4bef5d229e7f4f5335f6a4e3a6f83a8fc5d80 Mon Sep 17 00:00:00 2001 From: Mark Siewert Date: Sun, 10 Jun 2012 11:14:46 +0200 Subject: [PATCH 013/688] - Add support for loading multiple esm/esp files. Selection in omwlauncher is recognized and applied. - Quick hack for multiple terrain palettes. Prevents crashes and/or wrong textures in masters/plugins beyond the first. - Support deleting parent entries from the list. --- apps/openmw/engine.cpp | 30 ++++++++++++++----- apps/openmw/engine.hpp | 8 +++-- apps/openmw/main.cpp | 26 +++++++++------- apps/openmw/mwrender/terrain.cpp | 24 +++++++++++---- apps/openmw/mwrender/terrain.hpp | 5 +++- apps/openmw/mwworld/world.cpp | 32 +++++++++++++++----- apps/openmw/mwworld/world.hpp | 3 +- components/esm/esm_reader.hpp | 8 +++++ components/esm/loadland.cpp | 1 + components/esm/loadland.hpp | 1 + components/esm_store/reclists.hpp | 50 ++++++++++++++++++++++++------- components/esm_store/store.cpp | 7 +++++ 12 files changed, 148 insertions(+), 47 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e192b1f88..4fd43fa3f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -228,18 +228,32 @@ void OMW::Engine::setCell (const std::string& cellName) // Set master file (esm) // - If the given name does not have an extension, ".esm" is added automatically -// - Currently OpenMW only supports one master at the same time. void OMW::Engine::addMaster (const std::string& master) { - assert (mMaster.empty()); - mMaster = master; + mMaster.push_back(master); + std::string &str = mMaster.back(); - // Append .esm if not already there - std::string::size_type sep = mMaster.find_last_of ("."); + // Append .esm if not already there + std::string::size_type sep = str.find_last_of ("."); if (sep == std::string::npos) { - mMaster += ".esm"; + str += ".esm"; + } +} + +// Add plugin file (esp) + +void OMW::Engine::addPlugin (const std::string& plugin) +{ + mPlugins.push_back(plugin); + std::string &str = mPlugins.back(); + + // Append .esp if not already there + std::string::size_type sep = str.find_last_of ("."); + if (sep == std::string::npos) + { + str += ".esp"; } } @@ -341,8 +355,8 @@ void OMW::Engine::go() MWGui::CursorReplace replacer; // Create the world - mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mNewGame, mEncoding, mFallbackMap)); + mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, + mResDir, mNewGame, mEncoding, mFallbackMap) ); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index cf1ef3b9c..1cbc63609 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,7 +68,8 @@ namespace OMW boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; - std::string mMaster; + std::vector mMaster; + std::vector mPlugins; int mFpsLevel; bool mDebug; bool mVerboseScripts; @@ -124,9 +125,12 @@ namespace OMW /// Set master file (esm) /// - If the given name does not have an extension, ".esm" is added automatically - /// - Currently OpenMW only supports one master at the same time. void addMaster(const std::string& master); + /// Same as "addMaster", but for plugin files (esp) + /// - If the given name does not have an extension, ".esp" is added automatically + void addPlugin(const std::string& plugin); + /// Enable fps counter void showFPS(int level); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 68aa12fb3..3a63361f9 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -239,19 +239,23 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat master.push_back("Morrowind"); } - if (master.size() > 1) - { - std::cout - << "Ignoring all but the first master file (multiple master files not yet supported)." - << std::endl; - } - engine.addMaster(master[0]); - StringsVector plugin = variables["plugin"].as(); - if (!plugin.empty()) - { - std::cout << "Ignoring plugin files (plugins not yet supported)." << std::endl; + int cnt = master.size() + plugin.size(); + if (cnt > 255) + { + std::cerr + << "Error: Trying to load more than 255 master and plugin files! This will break all combaibility and is not supported!" + << std::endl; + return false; } + for (std::vector::size_type i = 0; i < master.size(); i++) + { + engine.addMaster(master[i]); + } + for (std::vector::size_type i = 0; i < plugin.size(); i++) + { + engine.addPlugin(plugin[i]); + } // startup-settings engine.setCell(variables["start"].as()); diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 4dac750c7..9247d42cf 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -156,7 +156,7 @@ namespace MWRender std::map indexes; initTerrainTextures(&terrainData, cellX, cellY, x * numTextures, y * numTextures, - numTextures, indexes); + numTextures, indexes, land->plugin); if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL) { @@ -218,8 +218,14 @@ namespace MWRender void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData, int cellX, int cellY, int fromX, int fromY, int size, - std::map& indexes) + std::map& indexes, + size_t plugin) { + // FIXME: In a multiple esm configuration, we have multiple palettes. Since this code + // crosses cell boundaries, we no longer have a unique terrain palette. Instead, we need + // to adopt the following code for a dynamic palette. And this is evil - the current design + // does not work well for this task... + assert(terrainData != NULL && "Must have valid terrain data"); assert(fromX >= 0 && fromY >= 0 && "Can't get a terrain texture on terrain outside the current cell"); @@ -232,12 +238,16 @@ namespace MWRender // //If we don't sort the ltex indexes, the splatting order may differ between //cells which may lead to inconsistent results when shading between cells + int num = MWBase::Environment::get().getWorld()->getStore().landTexts.getSizePlugin(plugin); std::set ltexIndexes; for ( int y = fromY - 1; y < fromY + size + 1; y++ ) { for ( int x = fromX - 1; x < fromX + size + 1; x++ ) { - ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y)); + int idx = getLtexIndexAt(cellX, cellY, x, y); + if (idx > num) + idx = 0; + ltexIndexes.insert(idx); } } @@ -249,7 +259,7 @@ namespace MWRender iter != ltexIndexes.end(); ++iter ) { - const uint16_t ltexIndex = *iter; + uint16_t ltexIndex = *iter; //this is the base texture, so we can ignore this at present if ( ltexIndex == baseTexture ) { @@ -262,8 +272,10 @@ namespace MWRender { //NB: All vtex ids are +1 compared to the ltex ids - assert( (int)MWBase::Environment::get().getWorld()->getStore().landTexts.getSize() >= (int)ltexIndex - 1 && + /* + assert( (int)mEnvironment.mWorld->getStore().landTexts.getSizePlugin(plugin) >= (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); + */ std::string texture; if ( ltexIndex == 0 ) @@ -272,7 +284,7 @@ namespace MWRender } else { - texture = MWBase::Environment::get().getWorld()->getStore().landTexts.search(ltexIndex-1)->texture; + texture = MWBase::Environment::get().getWorld()->getStore().landTexts.search(ltexIndex-1, plugin)->texture; //TODO this is needed due to MWs messed up texture handling texture = texture.substr(0, texture.rfind(".")) + ".dds"; } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 273ede084..5bda71eef 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -63,11 +63,14 @@ namespace MWRender{ * @param size the size (number of splats) to get * @param indexes a mapping of ltex index to the terrain texture layer that * can be used by initTerrainBlendMaps + * @param plugin the index of the plugin providing the texture list for this + * cell data; required because MW uses texture data on a per-plugin base */ void initTerrainTextures(Ogre::Terrain::ImportData* terrainData, int cellX, int cellY, int fromX, int fromY, int size, - std::map& indexes); + std::map& indexes, + size_t plugin = 0); /** * Creates the blend (splatting maps) for the given terrain from the ltex data. diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index e8d555689..fe3ddd5a8 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -176,7 +176,8 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, bool newGame, + const std::vector& master, + const std::vector& plugins, const boost::filesystem::path& resDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm, *this), @@ -189,15 +190,32 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering); - boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master)); + int idx = 0; + for (std::vector::size_type i = 0; i < master.size(); i++, idx++) + { + boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i])); + + std::cout << "Loading ESM " << masterPath.string() << "\n"; - std::cout << "Loading ESM " << masterPath.string() << "\n"; + // This parses the ESM file and loads a sample cell + mEsm.setEncoding(encoding); + mEsm.open (masterPath.string()); + mEsm.setIndex(idx); + mStore.load (mEsm); + } - // This parses the ESM file and loads a sample cell - mEsm.setEncoding(encoding); - mEsm.open (masterPath.string()); - mStore.load (mEsm); + for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) + { + boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); + + std::cout << "Loading ESP " << pluginPath.string() << "\n"; + // This parses the ESP file and loads a sample cell + mEsm.setEncoding(encoding); + mEsm.open (pluginPath.string()); + mEsm.setIndex(idx); + mStore.load (mEsm); + } MWRender::Player* play = &(mRendering->getPlayer()); mPlayer = new MWWorld::Player (play, mStore.npcs.find ("player"), *this); mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index e8391773b..9e36765ff 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -117,7 +117,8 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, bool newGame, + const std::vector& master, + const std::vector& plugins, const boost::filesystem::path& resDir, bool newGame, const std::string& encoding, std::map fallbackMap); ~World(); diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 340482891..4d61c3575 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -193,6 +193,14 @@ public: void openRaw(const std::string &file); + // This is a quick hack for multiple esm/esp files. Each plugin introduces its own + // terrain palette, but ESMReader does not pass a reference to the correct plugin + // to the individual load() methods. This hack allows to pass this reference + // indirectly to the load() method. + int idx; + void setIndex(const int index) {idx = index;} + const int getIndex() {return idx;} + /************************************************************************* * * Medium-level reading shortcuts diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 96afdf831..822952c91 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -23,6 +23,7 @@ Land::~Land() void Land::load(ESMReader &esm) { mEsm = &esm; + plugin = mEsm->getIndex(); // Get the grid location esm.getSubNameIs("INTV"); diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index ebc314a28..d03012d38 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -17,6 +17,7 @@ struct Land int flags; // Only first four bits seem to be used, don't know what // they mean. int X, Y; // Map coordinates. + int plugin; // Plugin index, used to reference the correct material palette. // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 48bf050cd..dd66d718a 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -26,6 +26,7 @@ namespace ESMS virtual void load(ESMReader &esm, const std::string &id) = 0; virtual int getSize() = 0; + virtual void remove(const std::string &id) {}; virtual void listIdentifier (std::vector& identifier) const = 0; static std::string toLower (const std::string& name) @@ -57,6 +58,14 @@ namespace ESMS list[id2].load(esm); } + // Delete the given object ID + void remove(const std::string &id) + { + std::string id2 = toLower (id); + + list.erase(id2); + } + // Find the given object ID, or return NULL if not found. const X* search(const std::string &id) const { @@ -268,38 +277,57 @@ namespace ESMS { virtual ~LTexList() {} - // TODO: For multiple ESM/ESP files we need one list per file. - std::vector ltex; + // For multiple ESM/ESP files we need one list per file. + typedef std::vector LandTextureList; + std::vector ltex; LTexList() { + ltex.push_back(LandTextureList()); + LandTextureList <exl = ltex[0]; // More than enough to hold Morrowind.esm. - ltex.reserve(128); + ltexl.reserve(128); } - const LandTexture* search(size_t index) const + const LandTexture* search(size_t index, size_t plugin) const { - assert(index < ltex.size()); - return <ex.at(index); + assert(plugin < ltex.size()); + const LandTextureList <exl = ltex[plugin]; + + assert(index < ltexl.size()); + return <exl.at(index); } int getSize() { return ltex.size(); } int getSize() const { return ltex.size(); } - virtual void listIdentifier (std::vector& identifier) const {} + int getSizePlugin(size_t plugin) { assert(plugin < ltex.size()); return ltex[plugin].size(); } + int getSizePlugin(size_t plugin) const { assert(plugin < ltex.size()); return ltex[plugin].size(); } - void load(ESMReader &esm, const std::string &id) + virtual void listIdentifier (std::vector& identifier) const {} + + void load(ESMReader &esm, const std::string &id, size_t plugin) { LandTexture lt; lt.load(esm); lt.id = id; // Make sure we have room for the structure - if(lt.index + 1 > (int)ltex.size()) - ltex.resize(lt.index+1); + if (plugin >= ltex.size()) { + ltex.resize(plugin+1); + } + LandTextureList <exl = ltex[plugin]; + if(lt.index + 1 > (int)ltexl.size()) + ltexl.resize(lt.index+1); // Store it - ltex[lt.index] = lt; + ltexl[lt.index] = lt; + } + + void load(ESMReader &esm, const std::string &id) + { + size_t plugin = esm.getIndex(); + load(esm, id, plugin); } }; diff --git a/components/esm_store/store.cpp b/components/esm_store/store.cpp index c676601e5..b6972355c 100644 --- a/components/esm_store/store.cpp +++ b/components/esm_store/store.cpp @@ -67,6 +67,13 @@ void ESMStore::load(ESMReader &esm) { // Load it std::string id = esm.getHNOString("NAME"); + // ... unless it got deleted! + if (esm.isNextSub("DELE")) { + esm.skipRecord(); + all.erase(id); + it->second->remove(id); + continue; + } it->second->load(esm, id); if (n.val==ESM::REC_DIAL) From 595b0729da99bf9a9d2d06f2a28eedbcacb9832e Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 10 Jun 2012 21:08:58 -0400 Subject: [PATCH 014/688] Few things changed --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- components/nifbullet/bullet_nif_loader.cpp | 9 +++------ libs/openengine/bullet/pmove.cpp | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 653fabd08..150cb4092 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -85,7 +85,7 @@ namespace MWClass const std::string &model = ref->base->model; assert (ref->base != NULL); if(!model.empty()){ - physics.insertActorPhysics(ptr, "meshes\\" + model); + physics.insertObjectPhysics(ptr, "meshes\\" + model); } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0a1a750f7..656e6d444 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -133,7 +133,7 @@ namespace MWClass std::string smodel = "meshes\\base_anim.nif"; if(beast) smodel = "meshes\\base_animkna.nif"; - physics.insertActorPhysics(ptr, smodel); + //physics.insertObjectPhysics(ptr, smodel); } diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index e87d5624e..17c5f18ac 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -133,7 +133,6 @@ btVector3 ManualBulletShapeLoader::getbtVector(Nif::Vector v) void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) { - std::cout << "Beginning physics\n"; cShape = static_cast(resource); resourceName = cShape->getName(); cShape->collide = false; @@ -203,7 +202,6 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) currentShape = new TriangleMeshShape(mTriMesh,true); cShape->Shape = currentShape; - std::cout << "End bullet physics\n"; } bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) @@ -235,7 +233,7 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, const Nif::Transformation *trafo,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) { - std::cout << "Handle Node\n"; + // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; @@ -273,7 +271,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, if (trafo) { - std::cout << "Before trafo\n"; + // Get a non-const reference to the node's data, since we're // overwriting it. TODO: Is this necessary? Transformation &final = *((Transformation*)node->trafo); @@ -289,10 +287,9 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // Scalar values are so nice to deal with. Why can't everything // just be scalar? final.scale *= trafo->scale; - std::cout << "After Trafo\n"; + } - std::cout << "After Trafo2\n"; // For NiNodes, loop through children if (node->recType == Nif::RC_NiNode) diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 1fc394259..ae160365b 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -301,7 +301,7 @@ bool PM_SlideMove( bool gravity ) if ( into >= 0.1 ) continue; // move doesn't interact with the plane - std::cout << "Second plane" << planes[i] << "\n"; + if(planes[i].x >= .70) { pm->ps.velocity = Ogre::Vector3(0,0,0); From b14c132a31bc9b8f410647b502a570b92b5c6608 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 11 Jun 2012 13:25:06 -0400 Subject: [PATCH 015/688] Merging in the latest master --- apps/openmw/mwgui/settingswindow.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index ce95edbd2..e3827c7b0 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -16,10 +16,10 @@ namespace MWGui SettingsWindow(WindowManager& parWindowManager); private: - static const float sFovMin = 30; - static const float sFovMax = 140; - static const float sViewDistMin = 2000; - static const float sViewDistMax = 5600; + static int const sFovMin = 30; + static int const sFovMax = 140; + static int const sViewDistMin = 2000; + static int const sViewDistMax = 5600; protected: MyGUI::Button* mOkButton; From 10810ee3115c2414500bb914c07f533a57dcd6fb Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 11 Jun 2012 19:55:10 -0400 Subject: [PATCH 016/688] Outputting formatted string with scale --- libs/openengine/bullet/physic.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index a94434e5b..c33d106cd 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -324,11 +324,18 @@ namespace Physic RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { + char uniqueID[4]; + sprintf( uniqueID, "%1.2f", scale ); + std::string sid = uniqueID; + std::string outputstring = mesh + sid + ">|"; + std::cout << outputstring << "\n"; + //get the shape from the .nif mShapeLoader->load(mesh,"General"); BulletShapeManager::getSingletonPtr()->load(mesh,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); shape->Shape->setLocalScaling(btVector3(scale,scale,scale)); + //create the motionState CMotionState* newMotionState = new CMotionState(this,name); From 5028f9926d6724f6b89aee5eb9fd330554f6d2c8 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Tue, 12 Jun 2012 18:09:58 -0400 Subject: [PATCH 017/688] Bullet scale solution --- components/nifogre/ogre_nif_loader.cpp | 2 +- libs/openengine/bullet/physic.cpp | 7 +++---- libs/openengine/bullet/pmove.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 331701c2a..669ef584f 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1294,7 +1294,7 @@ void NIFLoader::loadResource(Resource *resource) // Look it up resourceName = mesh->getName(); - //std::cout << resourceName << "\n"; + if (!vfs->isFile(resourceName)) { diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index c33d106cd..0cacd67a8 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -328,12 +328,11 @@ namespace Physic sprintf( uniqueID, "%1.2f", scale ); std::string sid = uniqueID; std::string outputstring = mesh + sid + ">|"; - std::cout << outputstring << "\n"; //get the shape from the .nif - mShapeLoader->load(mesh,"General"); - BulletShapeManager::getSingletonPtr()->load(mesh,"General"); - BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); + mShapeLoader->load(outputstring,"General"); + BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); + BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); shape->Shape->setLocalScaling(btVector3(scale,scale,scale)); diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index d48bd3637..f368f05f6 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -90,7 +90,7 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(800.0f), speed(2000.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + playerStruct() : gravity(800.0f), speed(480.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) { origin = Ogre::Vector3(733.164f,900.0f, 839.432f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); From 897a331244443f03a21fc0ca45c6788f48cc18c5 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 13 Jun 2012 12:21:51 -0400 Subject: [PATCH 018/688] NPC activation --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 86fd1fe23..e33fd322a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -84,7 +84,7 @@ namespace MWClass const std::string &model = ref->base->model; assert (ref->base != NULL); if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + physics.insertActorPhysics(ptr, "meshes\\" + model); } MWBase::Environment::get().getMechanicsManager()->addActor (ptr); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a3c20840a..8062dc35e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -130,7 +130,7 @@ namespace MWClass std::string smodel = "meshes\\base_anim.nif"; if(beast) smodel = "meshes\\base_animkna.nif"; - + physics.insertActorPhysics(ptr, smodel); MWBase::Environment::get().getMechanicsManager()->addActor (ptr); From c4c8288af8d51f01f22df3813a6b01b8f0e76a14 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 13 Jun 2012 19:34:13 -0400 Subject: [PATCH 019/688] Seven digit scales --- components/bsa/bsa_archive.cpp | 36 ++++++++++++++++++------------- libs/openengine/bullet/physic.cpp | 7 +++--- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index d3b75d4ae..e9ce3f615 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -73,14 +73,16 @@ class DirArchive: public Ogre::FileSystemArchive { { String passed = filename; - if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' + if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') + passed = filename.substr(0, filename.length() - 6); + else if(filename.at(filename.length() - 2) == '"') + passed = filename.substr(0, filename.length() - 9); + else 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) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); + + copy = passed; } @@ -226,14 +228,16 @@ public: BSAFile *narc = (BSAFile*)&arc; String passed = filename; - if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' + if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') + passed = filename.substr(0, filename.length() - 6); + else if(filename.at(filename.length() - 2) == '"') + passed = filename.substr(0, filename.length() - 9); + else 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) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); + + // Open the file StreamPtr strm = narc->getFile(passed.c_str()); @@ -248,14 +252,16 @@ bool exists(const String& filename) { // Check if the file exists. 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) == '<' + if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') + passed = filename.substr(0, filename.length() - 6); + else if(filename.at(filename.length() - 2) == '"') + passed = filename.substr(0, filename.length() - 9); + else 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) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); + + return arc.exists(passed.c_str()); } diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 0cacd67a8..d075536d2 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -324,10 +324,11 @@ namespace Physic RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { - char uniqueID[4]; - sprintf( uniqueID, "%1.2f", scale ); + char uniqueID[8]; + sprintf( uniqueID, "%07.3f", scale ); std::string sid = uniqueID; - std::string outputstring = mesh + sid + ">|"; + std::string outputstring = mesh + uniqueID + "\"|"; + //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif mShapeLoader->load(outputstring,"General"); From a1902b4121ba7da31ea9cbfbd7181315642178ea Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 16 Jun 2012 17:29:03 -0400 Subject: [PATCH 020/688] Tweaks for high speed --- libs/openengine/bullet/pmove.cpp | 10 ++++++++++ libs/openengine/bullet/pmove.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index ae160365b..b6876e43f 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -861,6 +861,8 @@ static void PM_WalkMove( playerMove* const pmove ) float accelerate; float vel; //pm->ps.gravity = 4000; + + //std::cout << "Player is walking\n"; if ( pm->ps.waterlevel > 2 && //DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) pml.forward.dotProduct(pml.groundTrace.planenormal) > 0.0f) @@ -1162,6 +1164,11 @@ void PM_GroundTraceMissed() { traceResults trace; Ogre::Vector3 point; + //We should not have significant upwards velocity when in the air, unless we jumped. + //This code protects against flying into the air when moving at high speeds. + //Z velocity is set to 50, instead of 0, to help move up certain steps. + if(pm->ps.velocity.z > 50.0f) + pm->ps.velocity.z = 50.0f; //std::cout << "Ground trace missed\n"; // we just transitioned into freefall //if ( pm->debugLevel ) @@ -1587,8 +1594,11 @@ void PM_AirMove() else PM_SlideMove ( qtrue ); #endif*/ + //std::cout << "Moving in the air" << pm->ps.velocity << "\n"; /*bprintf("%i ", */PM_StepSlideMove ( true )/* )*/; + + } static void PM_NoclipMove( void ) diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index f368f05f6..d838f17f5 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -28,7 +28,7 @@ static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); #define MAX_CLIP_PLANES 5 #define OVERCLIP 1.001f //#define STEPSIZE 18 // 18 is way too much -#define STEPSIZE (18 / 2) +#define STEPSIZE (13.5) #ifndef M_PI #define M_PI 3.14159265358979323846f #endif From ac6b455592af49c9e6603a80f8fb2991ad61a3f3 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 17 Jun 2012 12:57:59 -0400 Subject: [PATCH 021/688] StepSize moved back to 9 --- libs/openengine/bullet/pmove.cpp | 8 +++++--- libs/openengine/bullet/pmove.h | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index b6876e43f..6173acacf 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -530,7 +530,7 @@ int PM_StepSlideMove( bool gravity ) delta = pm->ps.origin.z - start_o.z; if ( delta > 2 ) { - pm->ps.counter = 10; + pm->ps.counter = 5; /* if (gravity) @@ -1167,8 +1167,7 @@ void PM_GroundTraceMissed() //We should not have significant upwards velocity when in the air, unless we jumped. //This code protects against flying into the air when moving at high speeds. //Z velocity is set to 50, instead of 0, to help move up certain steps. - if(pm->ps.velocity.z > 50.0f) - pm->ps.velocity.z = 50.0f; + //std::cout << "Ground trace missed\n"; // we just transitioned into freefall //if ( pm->debugLevel ) @@ -1431,10 +1430,13 @@ static void PM_GroundTrace( void ) // if the trace didn't hit anything, we are in free fall if ( trace.fraction == 1.0) { + if(pm->ps.velocity.z > 50.0f && pm->ps.bSnap) + pm->ps.velocity.z = 50.0f; if(pm->ps.snappingImplemented){ if(pm->ps.bSnap && pm->ps.counter <= 0) PM_GroundTraceMissed(); } + return; diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index d838f17f5..bc475a934 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -28,7 +28,7 @@ static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); #define MAX_CLIP_PLANES 5 #define OVERCLIP 1.001f //#define STEPSIZE 18 // 18 is way too much -#define STEPSIZE (13.5) +#define STEPSIZE (9) #ifndef M_PI #define M_PI 3.14159265358979323846f #endif @@ -42,7 +42,7 @@ static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); #define ENTITYNUM_NONE (MAX_GENTITIES - 1) #define ENTITYNUM_WORLD (MAX_GENTITIES - 2) #define MIN_WALK_NORMAL .7f // can't walk on very steep slopes -#define JUMP_VELOCITY (270) +#define JUMP_VELOCITY (540) #define PS_PMOVEFRAMECOUNTBITS 6 #define MINS_Z -24 #define DEFAULT_VIEWHEIGHT 26 @@ -90,7 +90,7 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(800.0f), speed(480.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + playerStruct() : gravity(800.0f), speed(2000.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) { origin = Ogre::Vector3(733.164f,900.0f, 839.432f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); From 33fe80723ccda649ed2a1391002cc32489c6d6f2 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 17 Jun 2012 20:56:10 -0400 Subject: [PATCH 022/688] btScaledBvhTriangleMeshShapes --- apps/openmw/mwworld/physicssystem.cpp | 1 + apps/openmw/mwworld/physicssystem.hpp | 1 + libs/openengine/bullet/physic.cpp | 16 ++++++++-------- libs/openengine/bullet/physic.hpp | 1 + libs/openengine/bullet/pmove.cpp | 2 +- libs/openengine/bullet/pmove.h | 4 ++-- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 3757609b2..46b72956d 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -237,6 +237,7 @@ namespace MWWorld void PhysicsSystem::addObject (const std::string& handle, const std::string& mesh, const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position) { + handleToMesh[handle] = mesh; OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle,scale); mEngine->addRigidBody(body); btTransform tr; diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 1a8bd87ae..b46ce117b 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -72,6 +72,7 @@ namespace MWWorld OEngine::Physic::PhysicEngine* mEngine; bool mFreeFly; playerMove* playerphysics; + std::map handleToMesh; PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d075536d2..6c4e42697 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -324,24 +324,24 @@ namespace Physic RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { - char uniqueID[8]; + /*char uniqueID[8]; sprintf( uniqueID, "%07.3f", scale ); std::string sid = uniqueID; - std::string outputstring = mesh + uniqueID + "\"|"; + std::string outputstring = mesh + uniqueID + "\"|";*/ //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif - mShapeLoader->load(outputstring,"General"); - BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); - BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - shape->Shape->setLocalScaling(btVector3(scale,scale,scale)); - + mShapeLoader->load(mesh,"General"); + BulletShapeManager::getSingletonPtr()->load(mesh,"General"); + BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); + //shape->Shape->setLocalScaling(); + btScaledBvhTriangleMeshShape* scaled = new btScaledBvhTriangleMeshShape(dynamic_cast (shape->Shape), btVector3(scale,scale,scale)); //create the motionState CMotionState* newMotionState = new CMotionState(this,name); //create the real body - btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape); + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,scaled); RigidBody* body = new RigidBody(CI,name); body->collide = shape->collide; return body; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 811572320..8a3295ec3 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -7,6 +7,7 @@ #include #include #include "BulletShapeLoader.h" +#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" class btRigidBody; class btBroadphaseInterface; diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 6173acacf..fa87e9040 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -1430,7 +1430,7 @@ static void PM_GroundTrace( void ) // if the trace didn't hit anything, we are in free fall if ( trace.fraction == 1.0) { - if(pm->ps.velocity.z > 50.0f && pm->ps.bSnap) + if(pm->ps.velocity.z > 50.0f && pm->ps.bSnap && pm->ps.speed > 1000.0f) pm->ps.velocity.z = 50.0f; if(pm->ps.snappingImplemented){ if(pm->ps.bSnap && pm->ps.counter <= 0) diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index bc475a934..592ca724d 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -42,7 +42,7 @@ static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); #define ENTITYNUM_NONE (MAX_GENTITIES - 1) #define ENTITYNUM_WORLD (MAX_GENTITIES - 2) #define MIN_WALK_NORMAL .7f // can't walk on very steep slopes -#define JUMP_VELOCITY (540) +#define JUMP_VELOCITY (270) #define PS_PMOVEFRAMECOUNTBITS 6 #define MINS_Z -24 #define DEFAULT_VIEWHEIGHT 26 @@ -90,7 +90,7 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(800.0f), speed(2000.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + playerStruct() : gravity(800.0f), speed(480.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) { origin = Ogre::Vector3(733.164f,900.0f, 839.432f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); From 4d55ecfdbed5c09c21e7ea7bb59c675f3b007203 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 18 Jun 2012 13:03:00 -0400 Subject: [PATCH 023/688] Deleting scaled shapes; scaleObject() --- apps/openmw/mwworld/physicssystem.cpp | 10 +++++++++- libs/openengine/bullet/physic.cpp | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 46b72956d..530881ec9 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -300,7 +300,15 @@ namespace MWWorld void PhysicsSystem::scaleObject (const std::string& handle, float scale) { - + if(handleToMesh.find(handle) != handleToMesh.end()) + { + btTransform transform = mEngine->getRigidBody(handle)->getWorldTransform(); + removeObject(handle); + + Ogre::Quaternion quat = Ogre::Quaternion(transform.getRotation().getW(), transform.getRotation().getX(), transform.getRotation().getY(), transform.getRotation().getZ()); + Ogre::Vector3 vec = Ogre::Vector3(transform.getOrigin().getX(), transform.getOrigin().getY(), transform.getOrigin().getZ()); + addObject(handle, handleToMesh[handle], quat, scale, vec); + } } bool PhysicsSystem::toggleCollisionMode() diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 6c4e42697..1dc38475d 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -398,10 +398,16 @@ namespace Physic if (it != RigidBodyMap.end() ) { RigidBody* body = it->second; + btScaledBvhTriangleMeshShape* scaled = dynamic_cast (body->getCollisionShape()); + if(body != NULL) { delete body; } + if(scaled != NULL) + { + delete scaled; + } RigidBodyMap.erase(it); } } From 86d8a07fc71798927e0abfbd9f1deff1202da6e3 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Tue, 19 Jun 2012 13:28:06 -0400 Subject: [PATCH 024/688] Switching back to old scaling --- apps/openmw/mwworld/physicssystem.cpp | 5 ++++- libs/openengine/bullet/physic.cpp | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 530881ec9..94f39da7d 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -213,6 +213,8 @@ namespace MWWorld if(it->first == "player"){ coord = playerphysics->ps.origin; + //playerphysics->ps.speed = 480.0f; + //std::cout << "Position" << coord << "\n"; } @@ -300,6 +302,7 @@ namespace MWWorld void PhysicsSystem::scaleObject (const std::string& handle, float scale) { + /* if(handleToMesh.find(handle) != handleToMesh.end()) { btTransform transform = mEngine->getRigidBody(handle)->getWorldTransform(); @@ -308,7 +311,7 @@ namespace MWWorld Ogre::Quaternion quat = Ogre::Quaternion(transform.getRotation().getW(), transform.getRotation().getX(), transform.getRotation().getY(), transform.getRotation().getZ()); Ogre::Vector3 vec = Ogre::Vector3(transform.getOrigin().getX(), transform.getOrigin().getY(), transform.getOrigin().getZ()); addObject(handle, handleToMesh[handle], quat, scale, vec); - } + }*/ } bool PhysicsSystem::toggleCollisionMode() diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 1dc38475d..6a3f22565 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -324,24 +324,24 @@ namespace Physic RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { - /*char uniqueID[8]; + char uniqueID[8]; sprintf( uniqueID, "%07.3f", scale ); std::string sid = uniqueID; - std::string outputstring = mesh + uniqueID + "\"|";*/ + std::string outputstring = mesh + uniqueID + "\"|"; //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif - mShapeLoader->load(mesh,"General"); - BulletShapeManager::getSingletonPtr()->load(mesh,"General"); - BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); - //shape->Shape->setLocalScaling(); - btScaledBvhTriangleMeshShape* scaled = new btScaledBvhTriangleMeshShape(dynamic_cast (shape->Shape), btVector3(scale,scale,scale)); + mShapeLoader->load(outputstring,"General"); + BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); + BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); + shape->Shape->setLocalScaling( btVector3(scale,scale,scale)); + //btScaledBvhTriangleMeshShape* scaled = new btScaledBvhTriangleMeshShape(dynamic_cast (shape->Shape), btVector3(scale,scale,scale)); //create the motionState CMotionState* newMotionState = new CMotionState(this,name); //create the real body - btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,scaled); + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape); RigidBody* body = new RigidBody(CI,name); body->collide = shape->collide; return body; @@ -398,16 +398,16 @@ namespace Physic if (it != RigidBodyMap.end() ) { RigidBody* body = it->second; - btScaledBvhTriangleMeshShape* scaled = dynamic_cast (body->getCollisionShape()); + //btScaledBvhTriangleMeshShape* scaled = dynamic_cast (body->getCollisionShape()); if(body != NULL) { delete body; } - if(scaled != NULL) + /*if(scaled != NULL) { delete scaled; - } + }*/ RigidBodyMap.erase(it); } } From 5c51674070a41a261b8d6b4b9b7b944960ce3326 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 20 Jun 2012 13:14:27 -0400 Subject: [PATCH 025/688] update --- apps/openmw/mwworld/physicssystem.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 94f39da7d..530881ec9 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -213,8 +213,6 @@ namespace MWWorld if(it->first == "player"){ coord = playerphysics->ps.origin; - //playerphysics->ps.speed = 480.0f; - //std::cout << "Position" << coord << "\n"; } @@ -302,7 +300,6 @@ namespace MWWorld void PhysicsSystem::scaleObject (const std::string& handle, float scale) { - /* if(handleToMesh.find(handle) != handleToMesh.end()) { btTransform transform = mEngine->getRigidBody(handle)->getWorldTransform(); @@ -311,7 +308,7 @@ namespace MWWorld Ogre::Quaternion quat = Ogre::Quaternion(transform.getRotation().getW(), transform.getRotation().getX(), transform.getRotation().getY(), transform.getRotation().getZ()); Ogre::Vector3 vec = Ogre::Vector3(transform.getOrigin().getX(), transform.getOrigin().getY(), transform.getOrigin().getZ()); addObject(handle, handleToMesh[handle], quat, scale, vec); - }*/ + } } bool PhysicsSystem::toggleCollisionMode() From fbde79896800cd37a94e9deb688a2df3ba75f5e5 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Thu, 21 Jun 2012 19:02:20 -0400 Subject: [PATCH 026/688] merge --- components/nifogre/ogre_nif_loader.cpp | 29 +++++++++++--------------- components/nifogre/ogre_nif_loader.hpp | 2 +- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 331701c2a..8882bebcd 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1067,11 +1067,11 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, if(mOutputAnimFiles){ std::string cut = ""; - for(unsigned int i = 0; i < name.length(); i++) + for(unsigned int i = 0; i < resourcename.length(); i++) { - if(!(name.at(i) == '\\' || name.at(i) == '/' || name.at(i) == '>' || name.at(i) == '<' || name.at(i) == '?' || name.at(i) == '*' || name.at(i) == '|' || name.at(i) == ':' || name.at(i) == '"')) + if(!(resourcename.at(i) == '\\' || resourcename.at(i) == '/' || resourcename.at(i) == '>' || resourcename.at(i) == '<' || resourcename.at(i) == '?' || resourcename.at(i) == '*' || resourcename.at(i) == '|' || resourcename.at(i) == ':' || resourcename.at(i) == '"')) { - cut += name.at(i); + cut += resourcename.at(i); } } @@ -1210,14 +1210,14 @@ void NIFLoader::loadResource(Resource *resource) mesh = 0; mSkel.setNull(); flip = false; - name = resource->getName(); - char suffix = name.at(name.length() - 2); + resourcename = resource->getName(); + char suffix = resourcename.at(resourcename.length() - 2); bool addAnim = true; bool hasAnim = false; bool linkSkeleton = true; //bool baddin = false; bNiTri = true; - if(name == "meshes\\base_anim.nif" || name == "meshes\\base_animkna.nif") + if(resourcename == "meshes\\base_anim.nif" || resourcename == "meshes\\base_animkna.nif") { bNiTri = false; } @@ -1239,7 +1239,7 @@ void NIFLoader::loadResource(Resource *resource) { //baddin = true; bNiTri = true; - std::string sub = name.substr(name.length() - 6, 4); + std::string sub = resourcename.substr(resourcename.length() - 6, 4); if(sub.compare("0000") != 0) addAnim = false; @@ -1250,14 +1250,14 @@ void NIFLoader::loadResource(Resource *resource) //baddin = true; linkSkeleton = false; bNiTri = true; - std::string sub = name.substr(name.length() - 6, 4); + std::string sub = resourcename.substr(resourcename.length() - 6, 4); if(sub.compare("0000") != 0) addAnim = false; } - switch(name.at(name.length() - 1)) + switch(resourcename.at(resourcename.length() - 1)) { case '"': triname = "tri chest"; @@ -1366,12 +1366,12 @@ void NIFLoader::loadResource(Resource *resource) mesh->_setBoundingSphereRadius(bounds.getRadius()); } if(hasAnim && addAnim){ - allanimmap[name] = allanim; - alltextmappings[name] = textmappings; + allanimmap[resourcename] = allanim; + alltextmappings[resourcename] = textmappings; } if(!mSkel.isNull() && shapes.size() > 0 && addAnim) { - allshapesmap[name] = shapes; + allshapesmap[resourcename] = shapes; } @@ -1421,11 +1421,6 @@ MeshPtr NIFLoader::load(const std::string &name, return themesh; } -/* -This function shares much of the same code handleShapes() in MWRender::Animation -This function also creates new position and normal buffers for submeshes. -This function points to existing texture and IndexData buffers -*/ std::vector* NIFLoader::getAnim(std::string lowername){ diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 794459e46..c6d7e7286 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -181,7 +181,7 @@ class NIFLoader : Ogre::ManualResourceLoader Ogre::SkeletonPtr mSkel; Ogre::Vector3 vector; std::vector shapes; - std::string name; + std::string resourcename; std::string triname; std::vector allanim; From aa827442e86e5baa2239b78542adde22a36b0535 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Jun 2012 11:39:21 +0200 Subject: [PATCH 027/688] Issue #314: Generalised ActiveSpells class so that it can handle lasting effects from potions too --- apps/openmw/mwmechanics/activespells.cpp | 36 ++++++++++++++++-------- apps/openmw/mwmechanics/activespells.hpp | 6 ++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index ced2a5c3f..f456a0e51 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -3,6 +3,9 @@ #include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwworld/world.hpp" @@ -42,14 +45,13 @@ namespace MWMechanics for (TIterator iter (begin()); iter!=end(); ++iter) { - const ESM::Spell& spell = - *MWBase::Environment::get().getWorld()->getStore().spells.find (iter->first); + const ESM::EffectList& effects = getEffectList (iter->first); const MWWorld::TimeStamp& start = iter->second.first; float magnitude = iter->second.second; - for (std::vector::const_iterator iter (spell.effects.list.begin()); - iter!=spell.effects.list.end(); ++iter) + for (std::vector::const_iterator iter (effects.list.begin()); + iter!=effects.list.end(); ++iter) { if (iter->duration) { @@ -70,18 +72,31 @@ namespace MWMechanics } } + const ESM::EffectList& ActiveSpells::getEffectList (const std::string& id) const + { + if (const ESM::Spell *spell = + MWBase::Environment::get().getWorld()->getStore().spells.search (id)) + return spell->effects; + + if (const ESM::Potion *potion = + MWBase::Environment::get().getWorld()->getStore().potions.search (id)) + return potion->effects; + + throw std::runtime_error ("ID " + id + " can not produce lasting effects"); + } + ActiveSpells::ActiveSpells() : mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} void ActiveSpells::addSpell (const std::string& id) { - const ESM::Spell& spell = *MWBase::Environment::get().getWorld()->getStore().spells.find (id); + const ESM::EffectList& effects = getEffectList (id); bool found = false; - for (std::vector::const_iterator iter (spell.effects.list.begin()); - iter!=spell.effects.list.end(); ++iter) + for (std::vector::const_iterator iter (effects.list.begin()); + iter!=effects.list.end(); ++iter) { if (iter->duration) { @@ -137,13 +152,12 @@ namespace MWMechanics double ActiveSpells::timeToExpire (const TIterator& iterator) const { - const ESM::Spell& spell = - *MWBase::Environment::get().getWorld()->getStore().spells.find (iterator->first); + const ESM::EffectList& effects = getEffectList (iterator->first); int duration = 0; - for (std::vector::const_iterator iter (spell.effects.list.begin()); - iter!=spell.effects.list.end(); ++iter) + for (std::vector::const_iterator iter (effects.list.begin()); + iter!=effects.list.end(); ++iter) { if (iter->duration>duration) duration = iter->duration; diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 179321c58..91c528587 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -12,11 +12,15 @@ namespace ESM { struct Spell; + struct EffectList; } namespace MWMechanics { /// \brief Lasting spell effects + /// + /// \note The name of this class is slightly misleading, since it also handels lasting potion + /// effects. class ActiveSpells { public: @@ -33,6 +37,8 @@ namespace MWMechanics void update() const; + const ESM::EffectList& getEffectList (const std::string& id) const; + public: ActiveSpells(); From 83c3972a89d78cddee8c12b7616bd862fb6c30fa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 24 Jun 2012 16:23:43 +0200 Subject: [PATCH 028/688] Issue #314: added return value to addSpell function --- apps/openmw/mwmechanics/activespells.cpp | 6 ++++-- apps/openmw/mwmechanics/activespells.hpp | 4 +++- apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 6 ++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index f456a0e51..e9215ec57 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -89,7 +89,7 @@ namespace MWMechanics : mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} - void ActiveSpells::addSpell (const std::string& id) + bool ActiveSpells::addSpell (const std::string& id) { const ESM::EffectList& effects = getEffectList (id); @@ -106,7 +106,7 @@ namespace MWMechanics } if (!found) - return; + return false; TContainer::iterator iter = mSpells.find (id); @@ -119,6 +119,8 @@ namespace MWMechanics iter->second = std::make_pair (MWBase::Environment::get().getWorld()->getTimeStamp(), random); mSpellsChanged = true; + + return true; } void ActiveSpells::removeSpell (const std::string& id) diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 91c528587..2226cea84 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -43,9 +43,11 @@ namespace MWMechanics ActiveSpells(); - void addSpell (const std::string& id); + bool addSpell (const std::string& id); ///< Overwrites an existing spell with the same ID. If the spell does not have any /// non-instant effects, it is ignored. + /// + /// \return Has the spell been added? void removeSpell (const std::string& id); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index fe39406fe..dde3afbbb 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -32,6 +32,11 @@ namespace MWWorld } + bool Class::apply (const MWWorld::Ptr& ptr, const std::string& id) const + { + return false; + } + MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const { throw std::runtime_error ("class does not have creature stats"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 46781d516..8dd427bde 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -164,6 +164,12 @@ namespace MWWorld /// effects). Throws an exception, if the object can't hold other objects. /// (default implementation: throws an exception) + virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id) const; + ///< Apply \a id on \a ptr. + /// \return Any effect? + /// + /// (default implementation: ignore and return false) + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. From fc063472d051d3e909286e3274c3023fc6e5081c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 29 Jun 2012 14:41:19 +0200 Subject: [PATCH 029/688] Revert "Added aspect ratio info to the list of resolutions" This reverts commit be5e0471067549cf48f9a882f4961151bb934fcb. --- apps/launcher/graphicspage.cpp | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 9edbe984c..b202b1ecf 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -1,25 +1,11 @@ #include -#include - #include #include #include "graphicspage.hpp" #include "naturalsort.hpp" -QString getAspect(int x, int y) -{ - int gcd = boost::math::gcd (x, y); - int xaspect = x / gcd; - int yaspect = y / gcd; - // special case: 8 : 5 is usually referred to as 16:10 - if (xaspect == 8 && yaspect == 5) - return QString("16:10"); - - return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); -} - GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent) : QWidget(parent) , mCfgMgr(cfg) @@ -257,21 +243,12 @@ QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer) QStringList tokens = qval.split(" ", QString::SkipEmptyParts); assert (tokens.size() >= 3); QString resolutionStr = tokens.at(0) + QString(" x ") + tokens.at(2); + { - // do not add duplicate resolutions - if (!result.contains(resolutionStr)) { - // Add the aspect ratio - QString aspect = getAspect(tokens.at(0).toInt(),tokens.at(2).toInt()); - - if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { - resolutionStr.append(tr("\t(Widescreen ") + aspect + ")"); - - } else if (aspect == QLatin1String("4:3")) { - resolutionStr.append(tr("\t(Standard 4:3)")); - } - - result << resolutionStr; - } + // do not add duplicate resolutions + if (!result.contains(resolutionStr)) + result << resolutionStr; + } } } From 76174098c723ea52e299a7cf62f062ded3c98823 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 29 Jun 2012 16:48:50 +0200 Subject: [PATCH 030/688] Issue #107: CellStore moved from ESMS to MWWorld --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwclass/activator.cpp | 22 ++-- apps/openmw/mwclass/apparatus.cpp | 19 ++- apps/openmw/mwclass/armor.cpp | 27 ++-- apps/openmw/mwclass/book.cpp | 21 ++-- apps/openmw/mwclass/clothing.cpp | 29 +++-- apps/openmw/mwclass/container.cpp | 17 ++- apps/openmw/mwclass/creature.cpp | 12 +- apps/openmw/mwclass/door.cpp | 17 ++- apps/openmw/mwclass/ingredient.cpp | 19 ++- apps/openmw/mwclass/light.cpp | 23 ++-- apps/openmw/mwclass/lockpick.cpp | 20 +-- apps/openmw/mwclass/misc.cpp | 23 ++-- apps/openmw/mwclass/npc.cpp | 12 +- apps/openmw/mwclass/potion.cpp | 19 ++- apps/openmw/mwclass/probe.cpp | 20 +-- apps/openmw/mwclass/repair.cpp | 20 +-- apps/openmw/mwclass/static.cpp | 4 +- apps/openmw/mwclass/weapon.cpp | 31 +++-- apps/openmw/mwdialogue/dialoguemanager.cpp | 16 +-- apps/openmw/mwgui/alchemywindow.cpp | 4 +- apps/openmw/mwgui/bookwindow.cpp | 2 +- apps/openmw/mwgui/container.cpp | 4 +- apps/openmw/mwgui/scrollwindow.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 14 +-- apps/openmw/mwrender/actors.hpp | 3 - apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwrender/objects.cpp | 6 +- apps/openmw/mwrender/objects.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwrender/renderingmanager.hpp | 4 +- apps/openmw/mwworld/actionread.cpp | 3 +- apps/openmw/mwworld/cells.cpp | 46 +++---- .../openmw/mwworld/cellstore.hpp | 116 ++++++++---------- apps/openmw/mwworld/containerstore.cpp | 32 ++--- apps/openmw/mwworld/containerstore.hpp | 75 ++++++----- apps/openmw/mwworld/localscripts.cpp | 6 +- apps/openmw/mwworld/manualref.hpp | 10 +- apps/openmw/mwworld/player.hpp | 7 +- apps/openmw/mwworld/ptr.hpp | 11 +- apps/openmw/mwworld/refdata.cpp | 3 +- apps/openmw/mwworld/scene.cpp | 30 ++--- apps/openmw/mwworld/scene.hpp | 4 +- apps/openmw/mwworld/world.cpp | 50 ++++---- apps/openmw/mwworld/world.hpp | 1 - components/CMakeLists.txt | 3 +- 48 files changed, 396 insertions(+), 427 deletions(-) rename components/esm_store/cell_store.hpp => apps/openmw/mwworld/cellstore.hpp (65%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 045f504a7..a2ebc1191 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -50,7 +50,7 @@ add_openmw_dir (mwworld refdata world physicssystem scene globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellfunctors cells localscripts customdata weather inventorystore ptr actionopen actionread - actionequip timestamp actionalchemy + actionequip timestamp actionalchemy cellstore ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5c5a586af..70e69b99c 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include #include @@ -42,6 +41,7 @@ #include "mwworld/world.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" +#include "mwworld/cellstore.hpp" #include "mwclass/classes.hpp" diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 505f61f4c..cbc73a68c 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -3,18 +3,20 @@ #include -#include - -#include "../mwworld/ptr.hpp" -#include "../mwrender/objects.hpp" #include "../mwbase/environment.hpp" + +#include "../mwworld//cellstore.hpp" +#include "../mwworld/ptr.hpp" + +#include "../mwrender/objects.hpp" + #include "../mwgui/window_manager.hpp" namespace MWClass { void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -30,7 +32,7 @@ namespace MWClass void Activator::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -44,7 +46,7 @@ namespace MWClass std::string Activator::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -52,7 +54,7 @@ namespace MWClass std::string Activator::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -67,7 +69,7 @@ namespace MWClass bool Activator::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -75,7 +77,7 @@ namespace MWClass MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 6945f3c27..84be29bd9 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -3,14 +3,13 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionalchemy.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwrender/objects.hpp" @@ -23,7 +22,7 @@ namespace MWClass { void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +38,7 @@ namespace MWClass void Apparatus::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -53,7 +52,7 @@ namespace MWClass std::string Apparatus::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -70,7 +69,7 @@ namespace MWClass std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -78,7 +77,7 @@ namespace MWClass int Apparatus::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -103,7 +102,7 @@ namespace MWClass std::string Apparatus::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -111,7 +110,7 @@ namespace MWClass bool Apparatus::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -119,7 +118,7 @@ namespace MWClass MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 83c0120c7..eea780faa 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -5,13 +5,12 @@ #include #include -#include - #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwbase/environment.hpp" @@ -25,7 +24,7 @@ namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -41,7 +40,7 @@ namespace MWClass void Armor::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -54,7 +53,7 @@ namespace MWClass std::string Armor::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -76,7 +75,7 @@ namespace MWClass int Armor::getItemMaxHealth (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.health; @@ -84,7 +83,7 @@ namespace MWClass std::string Armor::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -92,7 +91,7 @@ namespace MWClass std::pair, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -126,7 +125,7 @@ namespace MWClass int Armor::getEquipmentSkill (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::string typeGmst; @@ -164,7 +163,7 @@ namespace MWClass int Armor::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -201,7 +200,7 @@ namespace MWClass std::string Armor::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -209,7 +208,7 @@ namespace MWClass bool Armor::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -217,7 +216,7 @@ namespace MWClass MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -260,7 +259,7 @@ namespace MWClass std::string Armor::getEnchantment (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index a107d9b21..515c76e36 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -3,13 +3,12 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwrender/objects.hpp" @@ -21,7 +20,7 @@ namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -37,7 +36,7 @@ namespace MWClass void Book::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -51,7 +50,7 @@ namespace MWClass std::string Book::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -66,7 +65,7 @@ namespace MWClass std::string Book::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -74,7 +73,7 @@ namespace MWClass int Book::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -99,7 +98,7 @@ namespace MWClass std::string Book::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -107,7 +106,7 @@ namespace MWClass bool Book::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -115,7 +114,7 @@ namespace MWClass MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -143,7 +142,7 @@ namespace MWClass std::string Book::getEnchantment (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 11b515faf..01c17b3f3 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" @@ -12,6 +10,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/tooltips.hpp" #include "../mwgui/window_manager.hpp" @@ -24,7 +23,7 @@ namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -40,7 +39,7 @@ namespace MWClass void Clothing::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,7 +53,7 @@ namespace MWClass std::string Clothing::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -71,7 +70,7 @@ namespace MWClass std::string Clothing::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -79,7 +78,7 @@ namespace MWClass std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -119,7 +118,7 @@ namespace MWClass int Clothing::getEquipmentSkill (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->data.type==ESM::Clothing::Shoes) @@ -130,7 +129,7 @@ namespace MWClass int Clothing::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -145,7 +144,7 @@ namespace MWClass std::string Clothing::getUpSoundId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->data.type == 8) @@ -157,7 +156,7 @@ namespace MWClass std::string Clothing::getDownSoundId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->data.type == 8) @@ -169,7 +168,7 @@ namespace MWClass std::string Clothing::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -177,7 +176,7 @@ namespace MWClass bool Clothing::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -185,7 +184,7 @@ namespace MWClass MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -213,7 +212,7 @@ namespace MWClass std::string Clothing::getEnchantment (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 99a32fc32..3c938f401 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" @@ -12,6 +10,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -53,7 +52,7 @@ namespace MWClass void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -69,7 +68,7 @@ namespace MWClass void Container::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -117,7 +116,7 @@ namespace MWClass std::string Container::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -133,7 +132,7 @@ namespace MWClass std::string Container::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -148,7 +147,7 @@ namespace MWClass bool Container::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -156,7 +155,7 @@ namespace MWClass MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -182,7 +181,7 @@ namespace MWClass float Container::getCapacity (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->weight; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c9b43896b..41abda1b0 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -40,7 +40,7 @@ namespace MWClass { std::auto_ptr data (new CustomData); - ESMS::LiveCellRef *ref = ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); // creature stats data->mCreatureStats.mAttributes[0].set (ref->base->data.strength); @@ -69,7 +69,7 @@ namespace MWClass std::string Creature::getId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->mId; @@ -83,7 +83,7 @@ namespace MWClass void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -97,7 +97,7 @@ namespace MWClass std::string Creature::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -126,7 +126,7 @@ namespace MWClass std::string Creature::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -148,7 +148,7 @@ namespace MWClass MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 7041de1c6..ad245997f 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" @@ -12,6 +10,7 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/actionteleport.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -24,7 +23,7 @@ namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -40,7 +39,7 @@ namespace MWClass void Door::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -52,7 +51,7 @@ namespace MWClass std::string Door::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->ref.teleport && !ref->ref.destCell.empty()) // TODO doors that lead to exteriors @@ -64,7 +63,7 @@ namespace MWClass boost::shared_ptr Door::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &openSound = ref->base->openSound; @@ -134,7 +133,7 @@ namespace MWClass std::string Door::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -149,7 +148,7 @@ namespace MWClass bool Door::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -157,7 +156,7 @@ namespace MWClass MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 0c7fa7e69..c2a44e8bd 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -3,13 +3,12 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -22,7 +21,7 @@ namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -38,7 +37,7 @@ namespace MWClass void Ingredient::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -50,7 +49,7 @@ namespace MWClass std::string Ingredient::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -67,7 +66,7 @@ namespace MWClass std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -75,7 +74,7 @@ namespace MWClass int Ingredient::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -100,7 +99,7 @@ namespace MWClass std::string Ingredient::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -108,7 +107,7 @@ namespace MWClass bool Ingredient::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -116,7 +115,7 @@ namespace MWClass MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 154033433..c38033f70 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" @@ -13,6 +11,7 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -25,7 +24,7 @@ namespace MWClass { void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -47,7 +46,7 @@ namespace MWClass void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -65,7 +64,7 @@ namespace MWClass std::string Light::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->model.empty()) @@ -77,7 +76,7 @@ namespace MWClass boost::shared_ptr Light::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (!(ref->base->data.flags & ESM::Light::Carry)) @@ -91,7 +90,7 @@ namespace MWClass std::string Light::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -99,7 +98,7 @@ namespace MWClass std::pair, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -112,7 +111,7 @@ namespace MWClass int Light::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -138,7 +137,7 @@ namespace MWClass std::string Light::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -146,7 +145,7 @@ namespace MWClass bool Light::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -154,7 +153,7 @@ namespace MWClass MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 27e292bc0..81430fe17 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" @@ -12,6 +10,8 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" + #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -23,7 +23,7 @@ namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +39,7 @@ namespace MWClass void Lockpick::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,7 +54,7 @@ namespace MWClass std::string Lockpick::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -71,7 +71,7 @@ namespace MWClass std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -88,7 +88,7 @@ namespace MWClass int Lockpick::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -113,7 +113,7 @@ namespace MWClass std::string Lockpick::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -121,7 +121,7 @@ namespace MWClass bool Lockpick::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -129,7 +129,7 @@ namespace MWClass MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index d8e0553b9..3a764ebe7 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -5,13 +5,12 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -26,7 +25,7 @@ namespace MWClass { void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -42,7 +41,7 @@ namespace MWClass void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -56,7 +55,7 @@ namespace MWClass std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -73,7 +72,7 @@ namespace MWClass std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -81,7 +80,7 @@ namespace MWClass int Miscellaneous::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -96,7 +95,7 @@ namespace MWClass std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) @@ -108,7 +107,7 @@ namespace MWClass std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) @@ -120,7 +119,7 @@ namespace MWClass std::string Miscellaneous::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -128,7 +127,7 @@ namespace MWClass bool Miscellaneous::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -136,7 +135,7 @@ namespace MWClass MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f30e164f9..9561ba7b6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -53,7 +53,7 @@ namespace MWClass { std::auto_ptr data (new CustomData); - ESMS::LiveCellRef *ref = ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); // NPC stats if (!ref->base->faction.empty()) @@ -107,7 +107,7 @@ namespace MWClass std::string Npc::getId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->mId; @@ -120,7 +120,7 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -138,7 +138,7 @@ namespace MWClass std::string Npc::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -182,7 +182,7 @@ namespace MWClass std::string Npc::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -302,7 +302,7 @@ namespace MWClass MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 157af01f5..105703bd6 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -3,13 +3,12 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -22,7 +21,7 @@ namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -38,7 +37,7 @@ namespace MWClass void Potion::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -52,7 +51,7 @@ namespace MWClass std::string Potion::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -69,7 +68,7 @@ namespace MWClass std::string Potion::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -77,7 +76,7 @@ namespace MWClass int Potion::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -102,7 +101,7 @@ namespace MWClass std::string Potion::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -110,7 +109,7 @@ namespace MWClass bool Potion::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -118,7 +117,7 @@ namespace MWClass MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 0b256d729..cb62b2c4d 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" @@ -12,6 +10,8 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" + #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -23,7 +23,7 @@ namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +39,7 @@ namespace MWClass void Probe::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,7 +54,7 @@ namespace MWClass std::string Probe::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -70,7 +70,7 @@ namespace MWClass std::string Probe::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -87,7 +87,7 @@ namespace MWClass int Probe::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -112,7 +112,7 @@ namespace MWClass std::string Probe::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -120,7 +120,7 @@ namespace MWClass bool Probe::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -128,7 +128,7 @@ namespace MWClass MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 5666a95a5..4a66b177e 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -3,13 +3,13 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" + #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -21,7 +21,7 @@ namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -37,7 +37,7 @@ namespace MWClass void Repair::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -51,7 +51,7 @@ namespace MWClass std::string Repair::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -68,7 +68,7 @@ namespace MWClass std::string Repair::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -76,7 +76,7 @@ namespace MWClass int Repair::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -101,7 +101,7 @@ namespace MWClass std::string Repair::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -109,7 +109,7 @@ namespace MWClass bool Repair::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -117,7 +117,7 @@ namespace MWClass MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 4a4136816..b93d18596 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -11,7 +11,7 @@ namespace MWClass { void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -27,7 +27,7 @@ namespace MWClass void Static::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 482d618b0..79c98dda2 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" @@ -12,6 +10,7 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/world.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -24,7 +23,7 @@ namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -40,7 +39,7 @@ namespace MWClass void Weapon::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,7 +53,7 @@ namespace MWClass std::string Weapon::getName (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -76,7 +75,7 @@ namespace MWClass int Weapon::getItemMaxHealth (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.health; @@ -84,7 +83,7 @@ namespace MWClass std::string Weapon::getScript (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -92,7 +91,7 @@ namespace MWClass std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -116,7 +115,7 @@ namespace MWClass int Weapon::getEquipmentSkill (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const int size = 12; @@ -146,7 +145,7 @@ namespace MWClass int Weapon::getValue (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -161,7 +160,7 @@ namespace MWClass std::string Weapon::getUpSoundId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->base->data.type; @@ -207,7 +206,7 @@ namespace MWClass std::string Weapon::getDownSoundId (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->base->data.type; @@ -253,7 +252,7 @@ namespace MWClass std::string Weapon::getInventoryIcon (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -261,7 +260,7 @@ namespace MWClass bool Weapon::hasToolTip (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -269,7 +268,7 @@ namespace MWClass MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -351,7 +350,7 @@ namespace MWClass std::string Weapon::getEnchantment (const MWWorld::Ptr& ptr) const { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 45e2c8cb3..58670044a 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -392,7 +392,7 @@ namespace MWDialogue if(select.type==ESM::VT_Int) { - ESMS::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); int isFaction = int(toLower(npc->base->faction) == toLower(name)); if(selectCompare(comp,!isFaction,select.i)) return false; @@ -409,7 +409,7 @@ namespace MWDialogue if(select.type==ESM::VT_Int) { - ESMS::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); int isClass = int(toLower(npc->base->cls) == toLower(name)); if(selectCompare(comp,!isClass,select.i)) return false; @@ -426,7 +426,7 @@ namespace MWDialogue if(select.type==ESM::VT_Int) { - ESMS::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); int isRace = int(toLower(npc->base->race) == toLower(name)); if(selectCompare(comp,!isRace,select.i)) return false; @@ -493,7 +493,7 @@ namespace MWDialogue if (isCreature) return false; - ESMS::LiveCellRef *cellRef = actor.get(); + MWWorld::LiveCellRef *cellRef = actor.get(); if (!cellRef) return false; @@ -508,7 +508,7 @@ namespace MWDialogue if (isCreature) return false; - ESMS::LiveCellRef *cellRef = actor.get(); + MWWorld::LiveCellRef *cellRef = actor.get(); if (!cellRef) return false; @@ -558,7 +558,7 @@ namespace MWDialogue //check gender if (!isCreature) { - ESMS::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); if(npc->base->flags&npc->base->Female) { if(static_cast (info.data.gender)==0) return false; @@ -771,13 +771,13 @@ namespace MWDialogue int services = 0; if (mActor.getTypeName() == typeid(ESM::NPC).name()) { - ESMS::LiveCellRef* ref = mActor.get(); + MWWorld::LiveCellRef* ref = mActor.get(); if (ref->base->hasAI) services = ref->base->AI.services; } else if (mActor.getTypeName() == typeid(ESM::Creature).name()) { - ESMS::LiveCellRef* ref = mActor.get(); + MWWorld::LiveCellRef* ref = mActor.get(); if (ref->base->hasAI) services = ref->base->AI.services; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 423678eed..1535f3a26 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -275,7 +275,7 @@ namespace MWGui for (MWWorld::ContainerStoreIterator it(store.begin(MWWorld::ContainerStore::Type_Apparatus)); it != store.end(); ++it) { - ESMS::LiveCellRef* ref = it->get(); + MWWorld::LiveCellRef* ref = it->get(); if (ref->base->data.type == ESM::Apparatus::Albemic && (bestAlbemic.isEmpty() || ref->base->data.quality > bestAlbemic.get()->base->data.quality)) bestAlbemic = *it; @@ -420,7 +420,7 @@ namespace MWGui continue; // add the effects of this ingredient to list of effects - ESMS::LiveCellRef* ref = ingredient->getUserData()->get(); + MWWorld::LiveCellRef* ref = ingredient->getUserData()->get(); for (int i=0; i<4; ++i) { if (ref->base->data.effectID[i] < 0) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index a9dcd4555..74ba7d44e 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -55,7 +55,7 @@ void BookWindow::open (MWWorld::Ptr book) MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = mBook.get(); BookTextParser parser; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 36ed16b0e..9d6fc3c6b 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -273,7 +273,7 @@ void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) // check the container's Organic flag (if this is a container). container with Organic flag doesn't allow putting items inside if (mPtr.getTypeName() == typeid(ESM::Container).name()) { - ESMS::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->flags & ESM::Container::Organic) { // user notification @@ -556,7 +556,7 @@ void ContainerBase::addItem(MWWorld::Ptr item, int count) { MWWorld::ContainerStore& containerStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - int origCount = item.getRefData().getCount(); + int origCount = item.getRefData().getCount(); item.getRefData().setCount(count); MWWorld::ContainerStoreIterator it = containerStore.add(item); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 92b18d953..5b527cf86 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -31,7 +31,7 @@ void ScrollWindow::open (MWWorld::Ptr scroll) mScroll = scroll; - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = mScroll.get(); BookTextParser parser; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index a42da60d1..487c5020a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -86,7 +86,7 @@ namespace MWGui offerButtonWidth, mOfferButton->getHeight()); - setCoord(400, 0, 400, 300); + setCoord(400, 0, 400, 300); static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &TradeWindow::onWindowResize); } @@ -161,7 +161,7 @@ namespace MWGui int merchantgold; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { - ESMS::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->npdt52.gold == -10) merchantgold = ref->base->npdt12.gold; else @@ -169,7 +169,7 @@ namespace MWGui } else // ESM::Creature { - ESMS::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); merchantgold = ref->base->data.gold; } if (mCurrentBalance > 0 && merchantgold < mCurrentBalance) @@ -244,7 +244,7 @@ namespace MWGui int merchantgold; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { - ESMS::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->npdt52.gold == -10) merchantgold = ref->base->npdt12.gold; else @@ -252,7 +252,7 @@ namespace MWGui } else // ESM::Creature { - ESMS::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); merchantgold = ref->base->data.gold; } @@ -289,13 +289,13 @@ namespace MWGui int services = 0; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { - ESMS::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->hasAI) services = ref->base->AI.services; } else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) { - ESMS::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->hasAI) services = ref->base->AI.services; } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 432bcc297..c66492780 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -1,12 +1,9 @@ #ifndef _GAME_RENDER_ACTORS_H #define _GAME_RENDER_ACTORS_H -#include "components/esm_store/cell_store.hpp" #include #include - - #include #include "components/nifogre/ogre_nif_loader.hpp" diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 9ca3ed731..3303f3ddf 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -12,7 +12,7 @@ CreatureAnimation::~CreatureAnimation(){ } CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend){ insert = ptr.getRefData().getBaseNode(); - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 5ceafca36..ed1f591dd 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -38,7 +38,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere lfoot(0), rfoot(0) { - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); Ogre::Entity* blank = 0; std::vector* blankshape = 0; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index b9efcd3f5..985dcde7e 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -56,7 +56,7 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) insert->setPosition(f[0], f[1], f[2]); insert->setScale(ptr.getCellRef().scale, ptr.getCellRef().scale, ptr.getCellRef().scale); - + // Convert MW rotation to a quaternion: f = ptr.getCellRef().pos.rot; @@ -194,7 +194,7 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f Ogre::Light *light = mRenderer.getScene()->createLight(); light->setDiffuseColour (r, g, b); - ESMS::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); LightInfo info; @@ -307,7 +307,7 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store) mBounds.erase(store); } -void Objects::buildStaticGeometry(ESMS::CellStore& cell) +void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell) { if(mStaticGeometry.find(&cell) != mStaticGeometry.end()) { diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 6132879e6..05634e4b0 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -3,8 +3,6 @@ #include -#include - #include "../mwworld/refdata.hpp" #include "../mwworld/ptr.hpp" @@ -88,7 +86,7 @@ public: ///< \return found? void removeCell(MWWorld::Ptr::CellStore* store); - void buildStaticGeometry(ESMS::CellStore &cell); + void buildStaticGeometry(MWWorld::Ptr::CellStore &cell); void setMwRoot(Ogre::SceneNode* root); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 1029b5b60..03bb24635 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -323,7 +323,7 @@ bool RenderingManager::toggleRenderMode(int mode) } } -void RenderingManager::configureFog(ESMS::CellStore &mCell) +void RenderingManager::configureFog(MWWorld::Ptr::CellStore &mCell) { Ogre::ColourValue color; color.setAsABGR (mCell.cell->ambi.fog); @@ -372,7 +372,7 @@ void RenderingManager::setAmbientMode() } } -void RenderingManager::configureAmbient(ESMS::CellStore &mCell) +void RenderingManager::configureAmbient(MWWorld::Ptr::CellStore &mCell) { mAmbientColor.setAsABGR (mCell.cell->ambi.ambient); setAmbientMode(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 20f22fcca..2c1b21dd5 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -136,13 +136,13 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList int skyGetMasserPhase() const; int skyGetSecundaPhase() const; void skySetMoonColour (bool red); - void configureAmbient(ESMS::CellStore &mCell); + void configureAmbient(MWWorld::Ptr::CellStore &mCell); void requestMap (MWWorld::Ptr::CellStore* cell); ///< request the local map for a cell /// configure fog according to cell - void configureFog(ESMS::CellStore &mCell); + void configureFog(MWWorld::Ptr::CellStore &mCell); /// configure fog manually void configureFog(const float density, const Ogre::ColourValue& colour); diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 34e95cbd0..d84c8346f 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -13,7 +13,7 @@ namespace MWWorld void ActionRead::execute () { - ESMS::LiveCellRef *ref = + LiveCellRef *ref = mObject.get(); if (ref->base->data.isScroll) @@ -28,4 +28,3 @@ namespace MWWorld } } } - diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 3d6042e46..8e89d86d7 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -39,7 +39,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) { - for (ESMS::CellRefList::List::iterator iter ( + for (CellRefList::List::iterator iter ( cellStore.containers.list.begin()); iter!=cellStore.containers.list.end(); ++iter) { @@ -49,7 +49,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) iter->base->inventory, mStore); } - for (ESMS::CellRefList::List::iterator iter ( + for (CellRefList::List::iterator iter ( cellStore.creatures.list.begin()); iter!=cellStore.creatures.list.end(); ++iter) { @@ -59,7 +59,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) iter->base->inventory, mStore); } - for (ESMS::CellRefList::List::iterator iter ( + for (CellRefList::List::iterator iter ( cellStore.npcs.list.begin()); iter!=cellStore.npcs.list.end(); ++iter) { @@ -168,64 +168,64 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce return Ptr(); } - if (ESMS::LiveCellRef *ref = cell.activators.find (name)) + if (MWWorld::LiveCellRef *ref = cell.activators.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.potions.find (name)) + if (MWWorld::LiveCellRef *ref = cell.potions.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.appas.find (name)) + if (MWWorld::LiveCellRef *ref = cell.appas.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.armors.find (name)) + if (MWWorld::LiveCellRef *ref = cell.armors.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.books.find (name)) + if (MWWorld::LiveCellRef *ref = cell.books.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.clothes.find (name)) + if (MWWorld::LiveCellRef *ref = cell.clothes.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.containers.find (name)) + if (MWWorld::LiveCellRef *ref = cell.containers.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.creatures.find (name)) + if (MWWorld::LiveCellRef *ref = cell.creatures.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.doors.find (name)) + if (MWWorld::LiveCellRef *ref = cell.doors.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.ingreds.find (name)) + if (MWWorld::LiveCellRef *ref = cell.ingreds.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.creatureLists.find (name)) + if (MWWorld::LiveCellRef *ref = cell.creatureLists.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.itemLists.find (name)) + if (MWWorld::LiveCellRef *ref = cell.itemLists.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.lights.find (name)) + if (MWWorld::LiveCellRef *ref = cell.lights.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.lockpicks.find (name)) + if (MWWorld::LiveCellRef *ref = cell.lockpicks.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.miscItems.find (name)) + if (MWWorld::LiveCellRef *ref = cell.miscItems.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.npcs.find (name)) + if (MWWorld::LiveCellRef *ref = cell.npcs.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.probes.find (name)) + if (MWWorld::LiveCellRef *ref = cell.probes.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.repairs.find (name)) + if (MWWorld::LiveCellRef *ref = cell.repairs.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.statics.find (name)) + if (MWWorld::LiveCellRef *ref = cell.statics.find (name)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = cell.weapons.find (name)) + if (MWWorld::LiveCellRef *ref = cell.weapons.find (name)) return Ptr (ref, &cell); return Ptr(); diff --git a/components/esm_store/cell_store.hpp b/apps/openmw/mwworld/cellstore.hpp similarity index 65% rename from components/esm_store/cell_store.hpp rename to apps/openmw/mwworld/cellstore.hpp index 024412291..cac5eb617 100644 --- a/components/esm_store/cell_store.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -1,17 +1,9 @@ -#ifndef _GAME_CELL_STORE_H -#define _GAME_CELL_STORE_H +#ifndef GAME_MWWORLD_CELLSTORE_H +#define GAME_MWWORLD_CELLSTORE_H -/* - Cell storage. +#include - Used to load, look up and store all references in a single cell. - - Depends on esm/loadcell.hpp (loading from ESM) and esm_store.hpp - (looking up references.) Neither of these modules depend on us. - */ - -#include "store.hpp" -#include "components/esm/records.hpp" +#include #include #include @@ -20,10 +12,8 @@ #include #include -namespace ESMS +namespace MWWorld { - using namespace ESM; - /// A reference to one object (of any type) in a cell. /// /// Constructing this with a CellRef instance in the constructor means that @@ -33,7 +23,7 @@ namespace ESMS template struct LiveCellRef { - LiveCellRef(const CellRef& cref, const X* b = NULL) : base(b), ref(cref), + LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) : base(b), ref(cref), mData(ref) {} @@ -45,7 +35,7 @@ namespace ESMS /* Information about this instance, such as 3D location and rotation and individual type-dependent data. */ - CellRef ref; + ESM::CellRef ref; /// runtime-data D mData; @@ -63,7 +53,7 @@ namespace ESMS // ESMStore. Insert the reference into the list if a match is // found. If not, throw an exception. template - void find(CellRef &ref, const Y& recList) + void find(ESM::CellRef &ref, const Y& recList) { const X* obj = recList.find(ref.refID); if(obj == NULL) @@ -107,28 +97,28 @@ namespace ESMS float mWaterLevel; // Lists for each individual object type - CellRefList activators; - CellRefList potions; - CellRefList appas; - CellRefList armors; - CellRefList books; - CellRefList clothes; - CellRefList containers; - CellRefList creatures; - CellRefList doors; - CellRefList ingreds; - CellRefList creatureLists; - CellRefList itemLists; + CellRefList activators; + CellRefList potions; + CellRefList appas; + CellRefList armors; + CellRefList books; + CellRefList clothes; + CellRefList containers; + CellRefList creatures; + CellRefList doors; + CellRefList ingreds; + CellRefList creatureLists; + CellRefList itemLists; CellRefList lights; - CellRefList lockpicks; - CellRefList miscItems; - CellRefList npcs; - CellRefList probes; - CellRefList repairs; - CellRefList statics; - CellRefList weapons; + CellRefList lockpicks; + CellRefList miscItems; + CellRefList npcs; + CellRefList probes; + CellRefList repairs; + CellRefList statics; + CellRefList weapons; - void load (const ESMStore &store, ESMReader &esm) + void load (const ESMS::ESMStore &store, ESM::ESMReader &esm) { if (mState!=State_Loaded) { @@ -143,7 +133,7 @@ namespace ESMS } } - void preload (const ESMStore &store, ESMReader &esm) + void preload (const ESMS::ESMStore &store, ESM::ESMReader &esm) { if (mState==State_Unloaded) { @@ -196,7 +186,7 @@ namespace ESMS } /// Run through references and store IDs - void listRefs(const ESMStore &store, ESMReader &esm) + void listRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) { assert (cell); @@ -206,7 +196,7 @@ namespace ESMS // Reopen the ESM reader and seek to the right position. cell->restore (esm); - CellRef ref; + ESM::CellRef ref; // Get each reference in turn while (cell->getNextRef (esm, ref)) @@ -222,7 +212,7 @@ namespace ESMS std::sort (mIds.begin(), mIds.end()); } - void loadRefs(const ESMStore &store, ESMReader &esm) + void loadRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) { assert (cell); @@ -232,7 +222,7 @@ namespace ESMS // Reopen the ESM reader and seek to the right position. cell->restore(esm); - CellRef ref; + ESM::CellRef ref; // Get each reference in turn while(cell->getNextRef(esm, ref)) @@ -253,26 +243,26 @@ namespace ESMS */ switch(rec) { - case REC_ACTI: activators.find(ref, store.activators); break; - case REC_ALCH: potions.find(ref, store.potions); break; - case REC_APPA: appas.find(ref, store.appas); break; - case REC_ARMO: armors.find(ref, store.armors); break; - case REC_BOOK: books.find(ref, store.books); break; - case REC_CLOT: clothes.find(ref, store.clothes); break; - case REC_CONT: containers.find(ref, store.containers); break; - case REC_CREA: creatures.find(ref, store.creatures); break; - case REC_DOOR: doors.find(ref, store.doors); break; - case REC_INGR: ingreds.find(ref, store.ingreds); break; - case REC_LEVC: creatureLists.find(ref, store.creatureLists); break; - case REC_LEVI: itemLists.find(ref, store.itemLists); break; - case REC_LIGH: lights.find(ref, store.lights); break; - case REC_LOCK: lockpicks.find(ref, store.lockpicks); break; - case REC_MISC: miscItems.find(ref, store.miscItems); break; - case REC_NPC_: npcs.find(ref, store.npcs); break; - case REC_PROB: probes.find(ref, store.probes); break; - case REC_REPA: repairs.find(ref, store.repairs); break; - case REC_STAT: statics.find(ref, store.statics); break; - case REC_WEAP: weapons.find(ref, store.weapons); break; + case ESM::REC_ACTI: activators.find(ref, store.activators); break; + case ESM::REC_ALCH: potions.find(ref, store.potions); break; + case ESM::REC_APPA: appas.find(ref, store.appas); break; + case ESM::REC_ARMO: armors.find(ref, store.armors); break; + case ESM::REC_BOOK: books.find(ref, store.books); break; + case ESM::REC_CLOT: clothes.find(ref, store.clothes); break; + case ESM::REC_CONT: containers.find(ref, store.containers); break; + case ESM::REC_CREA: creatures.find(ref, store.creatures); break; + case ESM::REC_DOOR: doors.find(ref, store.doors); break; + case ESM::REC_INGR: ingreds.find(ref, store.ingreds); break; + case ESM::REC_LEVC: creatureLists.find(ref, store.creatureLists); break; + case ESM::REC_LEVI: itemLists.find(ref, store.itemLists); break; + case ESM::REC_LIGH: lights.find(ref, store.lights); break; + case ESM::REC_LOCK: lockpicks.find(ref, store.lockpicks); break; + case ESM::REC_MISC: miscItems.find(ref, store.miscItems); break; + case ESM::REC_NPC_: npcs.find(ref, store.npcs); break; + case ESM::REC_PROB: probes.find(ref, store.probes); break; + case ESM::REC_REPA: repairs.find(ref, store.repairs); break; + case ESM::REC_STAT: statics.find(ref, store.statics); break; + case ESM::REC_WEAP: weapons.find(ref, store.weapons); break; case 0: std::cout << "Cell reference " + ref.refID + " not found!\n"; break; default: diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 3304d0e6d..a071710b7 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include @@ -19,11 +19,11 @@ namespace { template - float getTotalWeight (const ESMS::CellRefList& cellRefList) + float getTotalWeight (const MWWorld::CellRefList& cellRefList) { float sum = 0; - for (typename ESMS::CellRefList::List::const_iterator iter ( + for (typename MWWorld::CellRefList::List::const_iterator iter ( cellRefList.list.begin()); iter!=cellRefList.list.end(); ++iter) @@ -78,7 +78,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) // this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold) if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) { - ESMS::LiveCellRef *gold = + MWWorld::LiveCellRef *gold = ptr.get(); if (compare_string_ci(gold->ref.refID, "gold_001") @@ -281,29 +281,29 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (int mask, ContainerStor ++*this; } -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Potion), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mPotion(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Apparatus), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mApparatus(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Armor), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mArmor(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Book), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mBook(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Clothing), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mClothing(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Ingredient), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mIngredient(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Light), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLight(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Lockpick), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLockpick(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Miscellaneous), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mMiscellaneous(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Probe), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mProbe(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Repair), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mRepair(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Weapon), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mWeapon(iterator){} void MWWorld::ContainerStoreIterator::incType() diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 15b553f9d..0a20bcc76 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -3,8 +3,7 @@ #include -#include - +#include "cellstore.hpp" #include "refdata.hpp" #include "ptr.hpp" @@ -40,18 +39,18 @@ namespace MWWorld private: - ESMS::CellRefList potions; - ESMS::CellRefList appas; - ESMS::CellRefList armors; - ESMS::CellRefList books; - ESMS::CellRefList clothes; - ESMS::CellRefList ingreds; - ESMS::CellRefList lights; - ESMS::CellRefList lockpicks; - ESMS::CellRefList miscItems; - ESMS::CellRefList probes; - ESMS::CellRefList repairs; - ESMS::CellRefList weapons; + MWWorld::CellRefList potions; + MWWorld::CellRefList appas; + MWWorld::CellRefList armors; + MWWorld::CellRefList books; + MWWorld::CellRefList clothes; + MWWorld::CellRefList ingreds; + MWWorld::CellRefList lights; + MWWorld::CellRefList lockpicks; + MWWorld::CellRefList miscItems; + MWWorld::CellRefList probes; + MWWorld::CellRefList repairs; + MWWorld::CellRefList weapons; int mStateId; mutable float mCachedWeight; mutable bool mWeightUpToDate; @@ -122,18 +121,18 @@ namespace MWWorld ContainerStore *mContainer; mutable Ptr mPtr; - ESMS::CellRefList::List::iterator mPotion; - ESMS::CellRefList::List::iterator mApparatus; - ESMS::CellRefList::List::iterator mArmor; - ESMS::CellRefList::List::iterator mBook; - ESMS::CellRefList::List::iterator mClothing; - ESMS::CellRefList::List::iterator mIngredient; - ESMS::CellRefList::List::iterator mLight; - ESMS::CellRefList::List::iterator mLockpick; - ESMS::CellRefList::List::iterator mMiscellaneous; - ESMS::CellRefList::List::iterator mProbe; - ESMS::CellRefList::List::iterator mRepair; - ESMS::CellRefList::List::iterator mWeapon; + MWWorld::CellRefList::List::iterator mPotion; + MWWorld::CellRefList::List::iterator mApparatus; + MWWorld::CellRefList::List::iterator mArmor; + MWWorld::CellRefList::List::iterator mBook; + MWWorld::CellRefList::List::iterator mClothing; + MWWorld::CellRefList::List::iterator mIngredient; + MWWorld::CellRefList::List::iterator mLight; + MWWorld::CellRefList::List::iterator mLockpick; + MWWorld::CellRefList::List::iterator mMiscellaneous; + MWWorld::CellRefList::List::iterator mProbe; + MWWorld::CellRefList::List::iterator mRepair; + MWWorld::CellRefList::List::iterator mWeapon; private: @@ -144,18 +143,18 @@ namespace MWWorld ///< Begin-iterator // construct iterator using a CellRefList iterator - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, ESMS::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); void incType(); diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 2d90d90e0..246d95940 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -1,13 +1,15 @@ #include "localscripts.hpp" +#include "cellstore.hpp" + namespace { template void listCellScripts (MWWorld::LocalScripts& localScripts, - ESMS::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) { - for (typename ESMS::CellRefList::List::iterator iter ( + for (typename MWWorld::CellRefList::List::iterator iter ( cellRefList.list.begin()); iter!=cellRefList.list.end(); ++iter) { diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index f8bc7d983..9705fc7af 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -3,10 +3,10 @@ #include -#include #include #include "ptr.hpp" +#include "cellstore.hpp" namespace MWWorld { @@ -24,11 +24,11 @@ namespace MWWorld { if (const T *instance = list.search (name)) { - ESMS::LiveCellRef ref; + LiveCellRef ref; ref.base = instance; mRef = ref; - mPtr = Ptr (&boost::any_cast&> (mRef), 0); + mPtr = Ptr (&boost::any_cast&> (mRef), 0); return true; } @@ -41,11 +41,11 @@ namespace MWWorld { if (const T *instance = list.search (name)) { - ESMS::LiveCellRef ref; + LiveCellRef ref; ref.base = instance; mRef = ref; - mPtr = Ptr (&boost::any_cast&> (mRef), 0); + mPtr = Ptr (&boost::any_cast&> (mRef), 0); return true; } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index a5c5ff308..09c4949d4 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -3,8 +3,7 @@ #include "OgreCamera.h" -#include - +#include "../mwworld/cellstore.hpp" #include "../mwworld/refdata.hpp" #include "../mwworld/ptr.hpp" @@ -20,9 +19,9 @@ namespace MWWorld class World; /// \brief NPC object representing the player and additional player data - class Player + class Player { - ESMS::LiveCellRef mPlayer; + LiveCellRef mPlayer; MWWorld::Ptr::CellStore *mCellStore; MWRender::Player *mRenderer; MWWorld::World& mWorld; diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 4cf3e98da..c5461c469 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -7,8 +7,7 @@ #include -#include - +#include "cellstore.hpp" #include "refdata.hpp" namespace MWWorld @@ -21,7 +20,7 @@ namespace MWWorld { public: - typedef ESMS::CellStore CellStore; + typedef MWWorld::CellStore CellStore; boost::any mPtr; ESM::CellRef *mCellRef; @@ -51,7 +50,7 @@ namespace MWWorld } template - Ptr (ESMS::LiveCellRef *liveCellRef, CellStore *cell) + Ptr (MWWorld::LiveCellRef *liveCellRef, CellStore *cell) : mContainerStore (0) { mPtr = liveCellRef; @@ -62,9 +61,9 @@ namespace MWWorld } template - ESMS::LiveCellRef *get() const + MWWorld::LiveCellRef *get() const { - return boost::any_cast*> (mPtr); + return boost::any_cast*> (mPtr); } ESM::CellRef& getCellRef() const; diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index e1c14b907..a8aa058bc 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -1,9 +1,8 @@ #include "refdata.hpp" -#include - #include "customdata.hpp" +#include "cellstore.hpp" namespace MWWorld { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a47137d25..8a4373b1e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -23,7 +23,7 @@ namespace template void insertCellRefList(MWRender::RenderingManager& rendering, - T& cellRefList, ESMS::CellStore &cell, MWWorld::PhysicsSystem& physics) + T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics) { if (!cellRefList.list.empty()) { @@ -312,7 +312,7 @@ namespace MWWorld mCellChanged = false; } - void Scene::insertCell(ESMS::CellStore &cell) + void Scene::insertCell (Ptr::CellStore &cell) { // Loop through all references in the cell insertCellRefList(mRendering, cell.activators, cell, *mPhysics); @@ -348,67 +348,67 @@ namespace MWWorld // insert into the correct CellRefList if (type == typeid(ESM::Potion).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->potions.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); } else if (type == typeid(ESM::Apparatus).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->appas.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); } else if (type == typeid(ESM::Armor).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->armors.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); } else if (type == typeid(ESM::Book).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->books.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); } else if (type == typeid(ESM::Clothing).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->clothes.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); } else if (type == typeid(ESM::Ingredient).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->ingreds.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); } else if (type == typeid(ESM::Light).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->lights.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); } else if (type == typeid(ESM::Tool).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->lockpicks.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); } else if (type == typeid(ESM::Repair).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->repairs.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); } else if (type == typeid(ESM::Probe).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->probes.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); } else if (type == typeid(ESM::Weapon).name()) { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->weapons.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); } @@ -432,7 +432,7 @@ namespace MWWorld MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); - ESMS::LiveCellRef* ref = newRef.getPtr().get(); + MWWorld::LiveCellRef* ref = newRef.getPtr().get(); cell->miscItems.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); @@ -444,7 +444,7 @@ namespace MWWorld } else { - ESMS::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->miscItems.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 906580ff4..373b20ed5 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -8,8 +8,6 @@ #include -#include - #include "../mwrender/renderingmanager.hpp" #include "../mwrender/renderinginterface.hpp" @@ -98,7 +96,7 @@ namespace MWWorld void markCellAsUnchanged(); - void insertCell(ESMS::CellStore &cell); + void insertCell (Ptr::CellStore &cell); /// this method is only meant for dropping objects into the gameworld from a container /// and thus only handles object types that can be placed in a container diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 79fd03e36..525ae33ce 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -33,10 +33,10 @@ namespace { template void listCellScripts (const ESMS::ESMStore& store, - ESMS::CellRefList& cellRefList, MWWorld::LocalScripts& localScripts, + MWWorld::CellRefList& cellRefList, MWWorld::LocalScripts& localScripts, MWWorld::Ptr::CellStore *cell) { - for (typename ESMS::CellRefList::List::iterator iter ( + for (typename MWWorld::CellRefList::List::iterator iter ( cellRefList.list.begin()); iter!=cellRefList.list.end(); ++iter) { @@ -53,10 +53,10 @@ namespace } template - ESMS::LiveCellRef *searchViaHandle (const std::string& handle, - ESMS::CellRefList& refList) + MWWorld::LiveCellRef *searchViaHandle (const std::string& handle, + MWWorld::CellRefList& refList) { - typedef typename ESMS::CellRefList::List::iterator iterator; + typedef typename MWWorld::CellRefList::List::iterator iterator; for (iterator iter (refList.list.begin()); iter!=refList.list.end(); ++iter) { @@ -75,45 +75,45 @@ namespace MWWorld { Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) { - if (ESMS::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.activators)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.potions)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.potions)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.appas)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.appas)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.armors)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.armors)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.books)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.books)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.clothes)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.clothes)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.containers)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.creatures)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.doors)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.doors)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.ingreds)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.lights)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.lights)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.lockpicks)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.lockpicks)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.miscItems)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.miscItems)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.npcs)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.npcs)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.probes)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.probes)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.repairs)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.repairs)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.statics)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.statics)) return Ptr (ref, &cell); - if (ESMS::LiveCellRef *ref = searchViaHandle (handle, cell.weapons)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.weapons)) return Ptr (ref, &cell); return Ptr(); } @@ -961,8 +961,8 @@ namespace MWWorld Ogre::Vector2 World::getNorthVector(Ptr::CellStore* cell) { - ESMS::CellRefList statics = cell->statics; - ESMS::LiveCellRef* ref = statics.find("northmarker"); + MWWorld::CellRefList statics = cell->statics; + MWWorld::LiveCellRef* ref = statics.find("northmarker"); if (!ref) return Vector2(0, 1); Ogre::SceneNode* node = ref->mData.getBaseNode(); diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 34990574e..f0da56b65 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -6,7 +6,6 @@ #include -#include #include #include "../mwrender/debugging.hpp" diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8a1875d0f..284ca3cce 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -35,7 +35,7 @@ add_component_dir (file_finder ) add_component_dir (esm_store - cell_store reclists store + reclists store ) add_component_dir (esm @@ -74,4 +74,3 @@ target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) - From 7fcd41c69d8c84192e69fb22b848c2e2b9f39698 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 29 Jun 2012 18:54:23 +0200 Subject: [PATCH 031/688] Issue #107: Detemplateised CellStore; some include cleanup --- apps/openmw/mwclass/activator.cpp | 12 +- apps/openmw/mwclass/apparatus.cpp | 16 +- apps/openmw/mwclass/armor.cpp | 24 +-- apps/openmw/mwclass/book.cpp | 18 +-- apps/openmw/mwclass/clothing.cpp | 26 +-- apps/openmw/mwclass/container.cpp | 14 +- apps/openmw/mwclass/creature.cpp | 12 +- apps/openmw/mwclass/door.cpp | 14 +- apps/openmw/mwclass/ingredient.cpp | 16 +- apps/openmw/mwclass/light.cpp | 20 +-- apps/openmw/mwclass/lockpick.cpp | 16 +- apps/openmw/mwclass/misc.cpp | 20 +-- apps/openmw/mwclass/npc.cpp | 12 +- apps/openmw/mwclass/potion.cpp | 16 +- apps/openmw/mwclass/probe.cpp | 16 +- apps/openmw/mwclass/repair.cpp | 16 +- apps/openmw/mwclass/static.cpp | 4 +- apps/openmw/mwclass/weapon.cpp | 28 ++-- apps/openmw/mwdialogue/dialoguemanager.cpp | 16 +- apps/openmw/mwgui/alchemywindow.cpp | 4 +- apps/openmw/mwgui/bookwindow.cpp | 3 +- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 3 +- apps/openmw/mwgui/tradewindow.cpp | 12 +- apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwrender/objects.cpp | 3 +- apps/openmw/mwworld/actionread.cpp | 3 +- apps/openmw/mwworld/cells.cpp | 46 +++--- apps/openmw/mwworld/cellstore.cpp | 124 +++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 175 +++++---------------- apps/openmw/mwworld/containerstore.cpp | 30 ++-- apps/openmw/mwworld/containerstore.hpp | 72 ++++----- apps/openmw/mwworld/localscripts.cpp | 6 +- apps/openmw/mwworld/manualref.hpp | 8 +- apps/openmw/mwworld/player.hpp | 4 +- apps/openmw/mwworld/ptr.hpp | 10 +- apps/openmw/mwworld/scene.cpp | 28 ++-- apps/openmw/mwworld/world.cpp | 50 +++--- 39 files changed, 461 insertions(+), 442 deletions(-) create mode 100644 apps/openmw/mwworld/cellstore.cpp diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index cbc73a68c..d8b8d54e8 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -16,7 +16,7 @@ namespace MWClass { void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -32,7 +32,7 @@ namespace MWClass void Activator::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -46,7 +46,7 @@ namespace MWClass std::string Activator::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -54,7 +54,7 @@ namespace MWClass std::string Activator::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -69,7 +69,7 @@ namespace MWClass bool Activator::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -77,7 +77,7 @@ namespace MWClass MWGui::ToolTipInfo Activator::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 84be29bd9..3321ab154 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -22,7 +22,7 @@ namespace MWClass { void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -38,7 +38,7 @@ namespace MWClass void Apparatus::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -52,7 +52,7 @@ namespace MWClass std::string Apparatus::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -69,7 +69,7 @@ namespace MWClass std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -77,7 +77,7 @@ namespace MWClass int Apparatus::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -102,7 +102,7 @@ namespace MWClass std::string Apparatus::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -110,7 +110,7 @@ namespace MWClass bool Apparatus::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -118,7 +118,7 @@ namespace MWClass MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index eea780faa..c3beed9ea 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -24,7 +24,7 @@ namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -40,7 +40,7 @@ namespace MWClass void Armor::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -53,7 +53,7 @@ namespace MWClass std::string Armor::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -75,7 +75,7 @@ namespace MWClass int Armor::getItemMaxHealth (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.health; @@ -83,7 +83,7 @@ namespace MWClass std::string Armor::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -91,7 +91,7 @@ namespace MWClass std::pair, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -125,7 +125,7 @@ namespace MWClass int Armor::getEquipmentSkill (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::string typeGmst; @@ -163,7 +163,7 @@ namespace MWClass int Armor::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -200,7 +200,7 @@ namespace MWClass std::string Armor::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -208,7 +208,7 @@ namespace MWClass bool Armor::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -216,7 +216,7 @@ namespace MWClass MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -259,7 +259,7 @@ namespace MWClass std::string Armor::getEnchantment (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 515c76e36..6ad680b65 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -20,7 +20,7 @@ namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -36,7 +36,7 @@ namespace MWClass void Book::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -50,7 +50,7 @@ namespace MWClass std::string Book::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -65,7 +65,7 @@ namespace MWClass std::string Book::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -73,7 +73,7 @@ namespace MWClass int Book::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -98,7 +98,7 @@ namespace MWClass std::string Book::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -106,7 +106,7 @@ namespace MWClass bool Book::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -114,7 +114,7 @@ namespace MWClass MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -142,7 +142,7 @@ namespace MWClass std::string Book::getEnchantment (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 01c17b3f3..c063dd1b1 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -23,7 +23,7 @@ namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +39,7 @@ namespace MWClass void Clothing::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -53,7 +53,7 @@ namespace MWClass std::string Clothing::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -70,7 +70,7 @@ namespace MWClass std::string Clothing::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -78,7 +78,7 @@ namespace MWClass std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -118,7 +118,7 @@ namespace MWClass int Clothing::getEquipmentSkill (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->data.type==ESM::Clothing::Shoes) @@ -129,7 +129,7 @@ namespace MWClass int Clothing::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -144,7 +144,7 @@ namespace MWClass std::string Clothing::getUpSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->data.type == 8) @@ -156,7 +156,7 @@ namespace MWClass std::string Clothing::getDownSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->data.type == 8) @@ -168,7 +168,7 @@ namespace MWClass std::string Clothing::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -176,7 +176,7 @@ namespace MWClass bool Clothing::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -184,7 +184,7 @@ namespace MWClass MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -212,7 +212,7 @@ namespace MWClass std::string Clothing::getEnchantment (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 3c938f401..25e00fa3d 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -52,7 +52,7 @@ namespace MWClass void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -68,7 +68,7 @@ namespace MWClass void Container::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -116,7 +116,7 @@ namespace MWClass std::string Container::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -132,7 +132,7 @@ namespace MWClass std::string Container::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -147,7 +147,7 @@ namespace MWClass bool Container::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -155,7 +155,7 @@ namespace MWClass MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -181,7 +181,7 @@ namespace MWClass float Container::getCapacity (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->weight; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 41abda1b0..ff96692d0 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -40,7 +40,7 @@ namespace MWClass { std::auto_ptr data (new CustomData); - MWWorld::LiveCellRef *ref = ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); // creature stats data->mCreatureStats.mAttributes[0].set (ref->base->data.strength); @@ -69,7 +69,7 @@ namespace MWClass std::string Creature::getId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->mId; @@ -83,7 +83,7 @@ namespace MWClass void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -97,7 +97,7 @@ namespace MWClass std::string Creature::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -126,7 +126,7 @@ namespace MWClass std::string Creature::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -148,7 +148,7 @@ namespace MWClass MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index ad245997f..6b302f2b6 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -23,7 +23,7 @@ namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +39,7 @@ namespace MWClass void Door::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -51,7 +51,7 @@ namespace MWClass std::string Door::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->ref.teleport && !ref->ref.destCell.empty()) // TODO doors that lead to exteriors @@ -63,7 +63,7 @@ namespace MWClass boost::shared_ptr Door::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &openSound = ref->base->openSound; @@ -133,7 +133,7 @@ namespace MWClass std::string Door::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -148,7 +148,7 @@ namespace MWClass bool Door::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -156,7 +156,7 @@ namespace MWClass MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index c2a44e8bd..8bf526e77 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -21,7 +21,7 @@ namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -37,7 +37,7 @@ namespace MWClass void Ingredient::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const std::string &model = ref->base->model; @@ -49,7 +49,7 @@ namespace MWClass std::string Ingredient::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -66,7 +66,7 @@ namespace MWClass std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -74,7 +74,7 @@ namespace MWClass int Ingredient::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -99,7 +99,7 @@ namespace MWClass std::string Ingredient::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -107,7 +107,7 @@ namespace MWClass bool Ingredient::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -115,7 +115,7 @@ namespace MWClass MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index c38033f70..61af188c5 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -24,7 +24,7 @@ namespace MWClass { void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -46,7 +46,7 @@ namespace MWClass void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -64,7 +64,7 @@ namespace MWClass std::string Light::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->model.empty()) @@ -76,7 +76,7 @@ namespace MWClass boost::shared_ptr Light::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (!(ref->base->data.flags & ESM::Light::Carry)) @@ -90,7 +90,7 @@ namespace MWClass std::string Light::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -98,7 +98,7 @@ namespace MWClass std::pair, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -111,7 +111,7 @@ namespace MWClass int Light::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -137,7 +137,7 @@ namespace MWClass std::string Light::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -145,7 +145,7 @@ namespace MWClass bool Light::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -153,7 +153,7 @@ namespace MWClass MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 81430fe17..17dfff1e9 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -23,7 +23,7 @@ namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +39,7 @@ namespace MWClass void Lockpick::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,7 +54,7 @@ namespace MWClass std::string Lockpick::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -71,7 +71,7 @@ namespace MWClass std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -88,7 +88,7 @@ namespace MWClass int Lockpick::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -113,7 +113,7 @@ namespace MWClass std::string Lockpick::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -121,7 +121,7 @@ namespace MWClass bool Lockpick::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -129,7 +129,7 @@ namespace MWClass MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 3a764ebe7..ff6acb6b5 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -25,7 +25,7 @@ namespace MWClass { void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -41,7 +41,7 @@ namespace MWClass void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -55,7 +55,7 @@ namespace MWClass std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -72,7 +72,7 @@ namespace MWClass std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -80,7 +80,7 @@ namespace MWClass int Miscellaneous::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -95,7 +95,7 @@ namespace MWClass std::string Miscellaneous::getUpSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) @@ -107,7 +107,7 @@ namespace MWClass std::string Miscellaneous::getDownSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) @@ -119,7 +119,7 @@ namespace MWClass std::string Miscellaneous::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -127,7 +127,7 @@ namespace MWClass bool Miscellaneous::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -135,7 +135,7 @@ namespace MWClass MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9561ba7b6..5d5a59d27 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -53,7 +53,7 @@ namespace MWClass { std::auto_ptr data (new CustomData); - MWWorld::LiveCellRef *ref = ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); // NPC stats if (!ref->base->faction.empty()) @@ -107,7 +107,7 @@ namespace MWClass std::string Npc::getId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->mId; @@ -120,7 +120,7 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -138,7 +138,7 @@ namespace MWClass std::string Npc::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -182,7 +182,7 @@ namespace MWClass std::string Npc::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -302,7 +302,7 @@ namespace MWClass MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 105703bd6..8b0bbcde8 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -21,7 +21,7 @@ namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -37,7 +37,7 @@ namespace MWClass void Potion::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -51,7 +51,7 @@ namespace MWClass std::string Potion::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -68,7 +68,7 @@ namespace MWClass std::string Potion::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -76,7 +76,7 @@ namespace MWClass int Potion::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -101,7 +101,7 @@ namespace MWClass std::string Potion::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -109,7 +109,7 @@ namespace MWClass bool Potion::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -117,7 +117,7 @@ namespace MWClass MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index cb62b2c4d..feac83787 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -23,7 +23,7 @@ namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +39,7 @@ namespace MWClass void Probe::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -54,7 +54,7 @@ namespace MWClass std::string Probe::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -70,7 +70,7 @@ namespace MWClass std::string Probe::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -87,7 +87,7 @@ namespace MWClass int Probe::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -112,7 +112,7 @@ namespace MWClass std::string Probe::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -120,7 +120,7 @@ namespace MWClass bool Probe::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -128,7 +128,7 @@ namespace MWClass MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 4a66b177e..e38dc68a5 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -21,7 +21,7 @@ namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -37,7 +37,7 @@ namespace MWClass void Repair::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -51,7 +51,7 @@ namespace MWClass std::string Repair::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -68,7 +68,7 @@ namespace MWClass std::string Repair::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -76,7 +76,7 @@ namespace MWClass int Repair::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -101,7 +101,7 @@ namespace MWClass std::string Repair::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -109,7 +109,7 @@ namespace MWClass bool Repair::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -117,7 +117,7 @@ namespace MWClass MWGui::ToolTipInfo Repair::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index b93d18596..dbc697f8b 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -11,7 +11,7 @@ namespace MWClass { void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -27,7 +27,7 @@ namespace MWClass void Static::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 79c98dda2..73e8107fc 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -23,7 +23,7 @@ namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); @@ -39,7 +39,7 @@ namespace MWClass void Weapon::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); @@ -53,7 +53,7 @@ namespace MWClass std::string Weapon::getName (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->name; @@ -75,7 +75,7 @@ namespace MWClass int Weapon::getItemMaxHealth (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.health; @@ -83,7 +83,7 @@ namespace MWClass std::string Weapon::getScript (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->script; @@ -91,7 +91,7 @@ namespace MWClass std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); std::vector slots; @@ -115,7 +115,7 @@ namespace MWClass int Weapon::getEquipmentSkill (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); const int size = 12; @@ -145,7 +145,7 @@ namespace MWClass int Weapon::getValue (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->data.value; @@ -160,7 +160,7 @@ namespace MWClass std::string Weapon::getUpSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->base->data.type; @@ -206,7 +206,7 @@ namespace MWClass std::string Weapon::getDownSoundId (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); int type = ref->base->data.type; @@ -252,7 +252,7 @@ namespace MWClass std::string Weapon::getInventoryIcon (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->icon; @@ -260,7 +260,7 @@ namespace MWClass bool Weapon::hasToolTip (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return (ref->base->name != ""); @@ -268,7 +268,7 @@ namespace MWClass MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; @@ -350,7 +350,7 @@ namespace MWClass std::string Weapon::getEnchantment (const MWWorld::Ptr& ptr) const { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); return ref->base->enchant; diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 58670044a..7f0765843 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -392,7 +392,7 @@ namespace MWDialogue if(select.type==ESM::VT_Int) { - MWWorld::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); int isFaction = int(toLower(npc->base->faction) == toLower(name)); if(selectCompare(comp,!isFaction,select.i)) return false; @@ -409,7 +409,7 @@ namespace MWDialogue if(select.type==ESM::VT_Int) { - MWWorld::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); int isClass = int(toLower(npc->base->cls) == toLower(name)); if(selectCompare(comp,!isClass,select.i)) return false; @@ -426,7 +426,7 @@ namespace MWDialogue if(select.type==ESM::VT_Int) { - MWWorld::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); int isRace = int(toLower(npc->base->race) == toLower(name)); if(selectCompare(comp,!isRace,select.i)) return false; @@ -493,7 +493,7 @@ namespace MWDialogue if (isCreature) return false; - MWWorld::LiveCellRef *cellRef = actor.get(); + MWWorld::LiveCellRef *cellRef = actor.get(); if (!cellRef) return false; @@ -508,7 +508,7 @@ namespace MWDialogue if (isCreature) return false; - MWWorld::LiveCellRef *cellRef = actor.get(); + MWWorld::LiveCellRef *cellRef = actor.get(); if (!cellRef) return false; @@ -558,7 +558,7 @@ namespace MWDialogue //check gender if (!isCreature) { - MWWorld::LiveCellRef* npc = actor.get(); + MWWorld::LiveCellRef* npc = actor.get(); if(npc->base->flags&npc->base->Female) { if(static_cast (info.data.gender)==0) return false; @@ -771,13 +771,13 @@ namespace MWDialogue int services = 0; if (mActor.getTypeName() == typeid(ESM::NPC).name()) { - MWWorld::LiveCellRef* ref = mActor.get(); + MWWorld::LiveCellRef* ref = mActor.get(); if (ref->base->hasAI) services = ref->base->AI.services; } else if (mActor.getTypeName() == typeid(ESM::Creature).name()) { - MWWorld::LiveCellRef* ref = mActor.get(); + MWWorld::LiveCellRef* ref = mActor.get(); if (ref->base->hasAI) services = ref->base->AI.services; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 1535f3a26..6fedc8d1d 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -275,7 +275,7 @@ namespace MWGui for (MWWorld::ContainerStoreIterator it(store.begin(MWWorld::ContainerStore::Type_Apparatus)); it != store.end(); ++it) { - MWWorld::LiveCellRef* ref = it->get(); + MWWorld::LiveCellRef* ref = it->get(); if (ref->base->data.type == ESM::Apparatus::Albemic && (bestAlbemic.isEmpty() || ref->base->data.quality > bestAlbemic.get()->base->data.quality)) bestAlbemic = *it; @@ -420,7 +420,7 @@ namespace MWGui continue; // add the effects of this ingredient to list of effects - MWWorld::LiveCellRef* ref = ingredient->getUserData()->get(); + MWWorld::LiveCellRef* ref = ingredient->getUserData()->get(); for (int i=0; i<4; ++i) { if (ref->base->data.effectID[i] < 0) diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 74ba7d44e..bb7d5f0a3 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -55,8 +55,7 @@ void BookWindow::open (MWWorld::Ptr book) MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); - MWWorld::LiveCellRef *ref = - mBook.get(); + MWWorld::LiveCellRef *ref = mBook.get(); BookTextParser parser; std::vector results = parser.split(ref->base->text, mLeftPage->getSize().width, mLeftPage->getSize().height); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 9d6fc3c6b..1d4c70de2 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -273,7 +273,7 @@ void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) // check the container's Organic flag (if this is a container). container with Organic flag doesn't allow putting items inside if (mPtr.getTypeName() == typeid(ESM::Container).name()) { - MWWorld::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->flags & ESM::Container::Organic) { // user notification diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 5b527cf86..67c58cd9c 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -31,8 +31,7 @@ void ScrollWindow::open (MWWorld::Ptr scroll) mScroll = scroll; - MWWorld::LiveCellRef *ref = - mScroll.get(); + MWWorld::LiveCellRef *ref = mScroll.get(); BookTextParser parser; MyGUI::IntSize size = parser.parse(ref->base->text, mTextView, 390); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 487c5020a..847898938 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -161,7 +161,7 @@ namespace MWGui int merchantgold; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { - MWWorld::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->npdt52.gold == -10) merchantgold = ref->base->npdt12.gold; else @@ -169,7 +169,7 @@ namespace MWGui } else // ESM::Creature { - MWWorld::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); merchantgold = ref->base->data.gold; } if (mCurrentBalance > 0 && merchantgold < mCurrentBalance) @@ -244,7 +244,7 @@ namespace MWGui int merchantgold; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { - MWWorld::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->npdt52.gold == -10) merchantgold = ref->base->npdt12.gold; else @@ -252,7 +252,7 @@ namespace MWGui } else // ESM::Creature { - MWWorld::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); merchantgold = ref->base->data.gold; } @@ -289,13 +289,13 @@ namespace MWGui int services = 0; if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { - MWWorld::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->hasAI) services = ref->base->AI.services; } else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) { - MWWorld::LiveCellRef* ref = mPtr.get(); + MWWorld::LiveCellRef* ref = mPtr.get(); if (ref->base->hasAI) services = ref->base->AI.services; } diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 3303f3ddf..206a051f0 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -12,7 +12,7 @@ CreatureAnimation::~CreatureAnimation(){ } CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend){ insert = ptr.getRefData().getBaseNode(); - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ed1f591dd..5681a3ad1 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -38,7 +38,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere lfoot(0), rfoot(0) { - MWWorld::LiveCellRef *ref = + MWWorld::LiveCellRef *ref = ptr.get(); Ogre::Entity* blank = 0; std::vector* blankshape = 0; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 985dcde7e..c78a98ef5 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -194,8 +194,7 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f Ogre::Light *light = mRenderer.getScene()->createLight(); light->setDiffuseColour (r, g, b); - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); LightInfo info; info.name = light->getName(); diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index d84c8346f..1e03230c7 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -13,8 +13,7 @@ namespace MWWorld void ActionRead::execute () { - LiveCellRef *ref = - mObject.get(); + LiveCellRef *ref = mObject.get(); if (ref->base->data.isScroll) { diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 8e89d86d7..64290d9b7 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -39,7 +39,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) { - for (CellRefList::List::iterator iter ( + for (CellRefList::List::iterator iter ( cellStore.containers.list.begin()); iter!=cellStore.containers.list.end(); ++iter) { @@ -49,7 +49,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) iter->base->inventory, mStore); } - for (CellRefList::List::iterator iter ( + for (CellRefList::List::iterator iter ( cellStore.creatures.list.begin()); iter!=cellStore.creatures.list.end(); ++iter) { @@ -59,7 +59,7 @@ void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) iter->base->inventory, mStore); } - for (CellRefList::List::iterator iter ( + for (CellRefList::List::iterator iter ( cellStore.npcs.list.begin()); iter!=cellStore.npcs.list.end(); ++iter) { @@ -168,64 +168,64 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce return Ptr(); } - if (MWWorld::LiveCellRef *ref = cell.activators.find (name)) + if (MWWorld::LiveCellRef *ref = cell.activators.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.potions.find (name)) + if (MWWorld::LiveCellRef *ref = cell.potions.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.appas.find (name)) + if (MWWorld::LiveCellRef *ref = cell.appas.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.armors.find (name)) + if (MWWorld::LiveCellRef *ref = cell.armors.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.books.find (name)) + if (MWWorld::LiveCellRef *ref = cell.books.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.clothes.find (name)) + if (MWWorld::LiveCellRef *ref = cell.clothes.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.containers.find (name)) + if (MWWorld::LiveCellRef *ref = cell.containers.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.creatures.find (name)) + if (MWWorld::LiveCellRef *ref = cell.creatures.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.doors.find (name)) + if (MWWorld::LiveCellRef *ref = cell.doors.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.ingreds.find (name)) + if (MWWorld::LiveCellRef *ref = cell.ingreds.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.creatureLists.find (name)) + if (MWWorld::LiveCellRef *ref = cell.creatureLists.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.itemLists.find (name)) + if (MWWorld::LiveCellRef *ref = cell.itemLists.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.lights.find (name)) + if (MWWorld::LiveCellRef *ref = cell.lights.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.lockpicks.find (name)) + if (MWWorld::LiveCellRef *ref = cell.lockpicks.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.miscItems.find (name)) + if (MWWorld::LiveCellRef *ref = cell.miscItems.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.npcs.find (name)) + if (MWWorld::LiveCellRef *ref = cell.npcs.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.probes.find (name)) + if (MWWorld::LiveCellRef *ref = cell.probes.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.repairs.find (name)) + if (MWWorld::LiveCellRef *ref = cell.repairs.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.statics.find (name)) + if (MWWorld::LiveCellRef *ref = cell.statics.find (name)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = cell.weapons.find (name)) + if (MWWorld::LiveCellRef *ref = cell.weapons.find (name)) return Ptr (ref, &cell); return Ptr(); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp new file mode 100644 index 000000000..60a7eb6e1 --- /dev/null +++ b/apps/openmw/mwworld/cellstore.cpp @@ -0,0 +1,124 @@ + +#include "cellstore.hpp" + +#include + +#include + +namespace MWWorld +{ + CellStore::CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) + { + mWaterLevel = cell->water; + } + + void CellStore::load (const ESMS::ESMStore &store, ESM::ESMReader &esm) + { + if (mState!=State_Loaded) + { + if (mState==State_Preloaded) + mIds.clear(); + + std::cout << "loading cell " << cell->getDescription() << std::endl; + + loadRefs (store, esm); + + mState = State_Loaded; + } + } + + void CellStore::preload (const ESMS::ESMStore &store, ESM::ESMReader &esm) + { + if (mState==State_Unloaded) + { + listRefs (store, esm); + + mState = State_Preloaded; + } + } + + void CellStore::listRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) + { + assert (cell); + + if (cell->context.filename.empty()) + return; // this is a dynamically generated cell -> skipping. + + // Reopen the ESM reader and seek to the right position. + cell->restore (esm); + + ESM::CellRef ref; + + // Get each reference in turn + while (cell->getNextRef (esm, ref)) + { + std::string lowerCase; + + std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + mIds.push_back (lowerCase); + } + + std::sort (mIds.begin(), mIds.end()); + } + + void CellStore::loadRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) + { + assert (cell); + + if (cell->context.filename.empty()) + return; // this is a dynamically generated cell -> skipping. + + // Reopen the ESM reader and seek to the right position. + cell->restore(esm); + + ESM::CellRef ref; + + // Get each reference in turn + while(cell->getNextRef(esm, ref)) + { + std::string lowerCase; + + std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + int rec = store.find(ref.refID); + + ref.refID = lowerCase; + + /* We can optimize this further by storing the pointer to the + record itself in store.all, so that we don't need to look it + up again here. However, never optimize. There are infinite + opportunities to do that later. + */ + switch(rec) + { + case ESM::REC_ACTI: activators.find(ref, store.activators); break; + case ESM::REC_ALCH: potions.find(ref, store.potions); break; + case ESM::REC_APPA: appas.find(ref, store.appas); break; + case ESM::REC_ARMO: armors.find(ref, store.armors); break; + case ESM::REC_BOOK: books.find(ref, store.books); break; + case ESM::REC_CLOT: clothes.find(ref, store.clothes); break; + case ESM::REC_CONT: containers.find(ref, store.containers); break; + case ESM::REC_CREA: creatures.find(ref, store.creatures); break; + case ESM::REC_DOOR: doors.find(ref, store.doors); break; + case ESM::REC_INGR: ingreds.find(ref, store.ingreds); break; + case ESM::REC_LEVC: creatureLists.find(ref, store.creatureLists); break; + case ESM::REC_LEVI: itemLists.find(ref, store.itemLists); break; + case ESM::REC_LIGH: lights.find(ref, store.lights); break; + case ESM::REC_LOCK: lockpicks.find(ref, store.lockpicks); break; + case ESM::REC_MISC: miscItems.find(ref, store.miscItems); break; + case ESM::REC_NPC_: npcs.find(ref, store.npcs); break; + case ESM::REC_PROB: probes.find(ref, store.probes); break; + case ESM::REC_REPA: repairs.find(ref, store.repairs); break; + case ESM::REC_STAT: statics.find(ref, store.statics); break; + case ESM::REC_WEAP: weapons.find(ref, store.weapons); break; + + case 0: std::cout << "Cell reference " + ref.refID + " not found!\n"; break; + default: + std::cout << "WARNING: Ignoring reference '" << ref.refID << "' of unhandled type\n"; + } + } + } +} diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index cac5eb617..8253f3f0b 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -3,15 +3,19 @@ #include -#include - #include #include #include -#include #include #include +#include "refdata.hpp" + +namespace ESMS +{ + struct ESMStore; +} + namespace MWWorld { /// A reference to one object (of any type) in a cell. @@ -20,7 +24,7 @@ namespace MWWorld /// in practice (where D is RefData) the possibly mutable data is copied /// across to mData. If later adding data (such as position) to CellRef /// this would have to be manually copied across. - template + template struct LiveCellRef { LiveCellRef(const ESM::CellRef& cref, const X* b = NULL) : base(b), ref(cref), @@ -38,14 +42,14 @@ namespace MWWorld ESM::CellRef ref; /// runtime-data - D mData; + RefData mData; }; /// A list of cell references - template + template struct CellRefList { - typedef LiveCellRef LiveRef; + typedef LiveCellRef LiveRef; typedef std::list List; List list; @@ -75,7 +79,6 @@ namespace MWWorld }; /// A storage struct for one single cell reference. - template class CellStore { public: @@ -85,10 +88,7 @@ namespace MWWorld State_Unloaded, State_Preloaded, State_Loaded }; - CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) - { - mWaterLevel = cell->water; - } + CellStore (const ESM::Cell *cell_); const ESM::Cell *cell; State mState; @@ -97,51 +97,30 @@ namespace MWWorld float mWaterLevel; // Lists for each individual object type - CellRefList activators; - CellRefList potions; - CellRefList appas; - CellRefList armors; - CellRefList books; - CellRefList clothes; - CellRefList containers; - CellRefList creatures; - CellRefList doors; - CellRefList ingreds; - CellRefList creatureLists; - CellRefList itemLists; - CellRefList lights; - CellRefList lockpicks; - CellRefList miscItems; - CellRefList npcs; - CellRefList probes; - CellRefList repairs; - CellRefList statics; - CellRefList weapons; + CellRefList activators; + CellRefList potions; + CellRefList appas; + CellRefList armors; + CellRefList books; + CellRefList clothes; + CellRefList containers; + CellRefList creatures; + CellRefList doors; + CellRefList ingreds; + CellRefList creatureLists; + CellRefList itemLists; + CellRefList lights; + CellRefList lockpicks; + CellRefList miscItems; + CellRefList npcs; + CellRefList probes; + CellRefList repairs; + CellRefList statics; + CellRefList weapons; - void load (const ESMS::ESMStore &store, ESM::ESMReader &esm) - { - if (mState!=State_Loaded) - { - if (mState==State_Preloaded) - mIds.clear(); + void load (const ESMS::ESMStore &store, ESM::ESMReader &esm); - std::cout << "loading cell " << cell->getDescription() << std::endl; - - loadRefs (store, esm); - - mState = State_Loaded; - } - } - - void preload (const ESMS::ESMStore &store, ESM::ESMReader &esm) - { - if (mState==State_Unloaded) - { - listRefs (store, esm); - - mState = State_Preloaded; - } - } + void preload (const ESMS::ESMStore &store, ESM::ESMReader &esm); /// Call functor (ref) for each reference. functor must return a bool. Returning /// false will abort the iteration. @@ -186,91 +165,9 @@ namespace MWWorld } /// Run through references and store IDs - void listRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) - { - assert (cell); - - if (cell->context.filename.empty()) - return; // this is a dynamically generated cell -> skipping. - - // Reopen the ESM reader and seek to the right position. - cell->restore (esm); - - ESM::CellRef ref; - - // Get each reference in turn - while (cell->getNextRef (esm, ref)) - { - std::string lowerCase; - - std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - mIds.push_back (lowerCase); - } - - std::sort (mIds.begin(), mIds.end()); - } - - void loadRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm) - { - assert (cell); - - if (cell->context.filename.empty()) - return; // this is a dynamically generated cell -> skipping. - - // Reopen the ESM reader and seek to the right position. - cell->restore(esm); - - ESM::CellRef ref; - - // Get each reference in turn - while(cell->getNextRef(esm, ref)) - { - std::string lowerCase; - - std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - int rec = store.find(ref.refID); - - ref.refID = lowerCase; - - /* We can optimize this further by storing the pointer to the - record itself in store.all, so that we don't need to look it - up again here. However, never optimize. There are infinite - opportunities to do that later. - */ - switch(rec) - { - case ESM::REC_ACTI: activators.find(ref, store.activators); break; - case ESM::REC_ALCH: potions.find(ref, store.potions); break; - case ESM::REC_APPA: appas.find(ref, store.appas); break; - case ESM::REC_ARMO: armors.find(ref, store.armors); break; - case ESM::REC_BOOK: books.find(ref, store.books); break; - case ESM::REC_CLOT: clothes.find(ref, store.clothes); break; - case ESM::REC_CONT: containers.find(ref, store.containers); break; - case ESM::REC_CREA: creatures.find(ref, store.creatures); break; - case ESM::REC_DOOR: doors.find(ref, store.doors); break; - case ESM::REC_INGR: ingreds.find(ref, store.ingreds); break; - case ESM::REC_LEVC: creatureLists.find(ref, store.creatureLists); break; - case ESM::REC_LEVI: itemLists.find(ref, store.itemLists); break; - case ESM::REC_LIGH: lights.find(ref, store.lights); break; - case ESM::REC_LOCK: lockpicks.find(ref, store.lockpicks); break; - case ESM::REC_MISC: miscItems.find(ref, store.miscItems); break; - case ESM::REC_NPC_: npcs.find(ref, store.npcs); break; - case ESM::REC_PROB: probes.find(ref, store.probes); break; - case ESM::REC_REPA: repairs.find(ref, store.repairs); break; - case ESM::REC_STAT: statics.find(ref, store.statics); break; - case ESM::REC_WEAP: weapons.find(ref, store.weapons); break; - - case 0: std::cout << "Cell reference " + ref.refID + " not found!\n"; break; - default: - std::cout << "WARNING: Ignoring reference '" << ref.refID << "' of unhandled type\n"; - } - } - } + void listRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm); + void loadRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm); }; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index a071710b7..8ce1b5c8f 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -19,11 +19,11 @@ namespace { template - float getTotalWeight (const MWWorld::CellRefList& cellRefList) + float getTotalWeight (const MWWorld::CellRefList& cellRefList) { float sum = 0; - for (typename MWWorld::CellRefList::List::const_iterator iter ( + for (typename MWWorld::CellRefList::List::const_iterator iter ( cellRefList.list.begin()); iter!=cellRefList.list.end(); ++iter) @@ -78,7 +78,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) // this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold) if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) { - MWWorld::LiveCellRef *gold = + MWWorld::LiveCellRef *gold = ptr.get(); if (compare_string_ci(gold->ref.refID, "gold_001") @@ -281,29 +281,29 @@ MWWorld::ContainerStoreIterator::ContainerStoreIterator (int mask, ContainerStor ++*this; } -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Potion), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mPotion(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Apparatus), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mApparatus(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Armor), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mArmor(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Book), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mBook(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Clothing), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mClothing(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Ingredient), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mIngredient(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Light), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLight(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Lockpick), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mLockpick(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Miscellaneous), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mMiscellaneous(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Probe), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mProbe(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Repair), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mRepair(iterator){} -MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) +MWWorld::ContainerStoreIterator::ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator iterator) : mType(MWWorld::ContainerStore::Type_Weapon), mMask(MWWorld::ContainerStore::Type_All), mContainer(container), mWeapon(iterator){} void MWWorld::ContainerStoreIterator::incType() diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 0a20bcc76..f71b493bd 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -39,18 +39,18 @@ namespace MWWorld private: - MWWorld::CellRefList potions; - MWWorld::CellRefList appas; - MWWorld::CellRefList armors; - MWWorld::CellRefList books; - MWWorld::CellRefList clothes; - MWWorld::CellRefList ingreds; - MWWorld::CellRefList lights; - MWWorld::CellRefList lockpicks; - MWWorld::CellRefList miscItems; - MWWorld::CellRefList probes; - MWWorld::CellRefList repairs; - MWWorld::CellRefList weapons; + MWWorld::CellRefList potions; + MWWorld::CellRefList appas; + MWWorld::CellRefList armors; + MWWorld::CellRefList books; + MWWorld::CellRefList clothes; + MWWorld::CellRefList ingreds; + MWWorld::CellRefList lights; + MWWorld::CellRefList lockpicks; + MWWorld::CellRefList miscItems; + MWWorld::CellRefList probes; + MWWorld::CellRefList repairs; + MWWorld::CellRefList weapons; int mStateId; mutable float mCachedWeight; mutable bool mWeightUpToDate; @@ -121,18 +121,18 @@ namespace MWWorld ContainerStore *mContainer; mutable Ptr mPtr; - MWWorld::CellRefList::List::iterator mPotion; - MWWorld::CellRefList::List::iterator mApparatus; - MWWorld::CellRefList::List::iterator mArmor; - MWWorld::CellRefList::List::iterator mBook; - MWWorld::CellRefList::List::iterator mClothing; - MWWorld::CellRefList::List::iterator mIngredient; - MWWorld::CellRefList::List::iterator mLight; - MWWorld::CellRefList::List::iterator mLockpick; - MWWorld::CellRefList::List::iterator mMiscellaneous; - MWWorld::CellRefList::List::iterator mProbe; - MWWorld::CellRefList::List::iterator mRepair; - MWWorld::CellRefList::List::iterator mWeapon; + MWWorld::CellRefList::List::iterator mPotion; + MWWorld::CellRefList::List::iterator mApparatus; + MWWorld::CellRefList::List::iterator mArmor; + MWWorld::CellRefList::List::iterator mBook; + MWWorld::CellRefList::List::iterator mClothing; + MWWorld::CellRefList::List::iterator mIngredient; + MWWorld::CellRefList::List::iterator mLight; + MWWorld::CellRefList::List::iterator mLockpick; + MWWorld::CellRefList::List::iterator mMiscellaneous; + MWWorld::CellRefList::List::iterator mProbe; + MWWorld::CellRefList::List::iterator mRepair; + MWWorld::CellRefList::List::iterator mWeapon; private: @@ -143,18 +143,18 @@ namespace MWWorld ///< Begin-iterator // construct iterator using a CellRefList iterator - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); - ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); + ContainerStoreIterator (ContainerStore *container, MWWorld::CellRefList::List::iterator); void incType(); diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 246d95940..d15f00c92 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -1,15 +1,17 @@ #include "localscripts.hpp" +#include + #include "cellstore.hpp" namespace { template void listCellScripts (MWWorld::LocalScripts& localScripts, - MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) { - for (typename MWWorld::CellRefList::List::iterator iter ( + for (typename MWWorld::CellRefList::List::iterator iter ( cellRefList.list.begin()); iter!=cellRefList.list.end(); ++iter) { diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 9705fc7af..6044ac8d5 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -24,11 +24,11 @@ namespace MWWorld { if (const T *instance = list.search (name)) { - LiveCellRef ref; + LiveCellRef ref; ref.base = instance; mRef = ref; - mPtr = Ptr (&boost::any_cast&> (mRef), 0); + mPtr = Ptr (&boost::any_cast&> (mRef), 0); return true; } @@ -41,11 +41,11 @@ namespace MWWorld { if (const T *instance = list.search (name)) { - LiveCellRef ref; + LiveCellRef ref; ref.base = instance; mRef = ref; - mPtr = Ptr (&boost::any_cast&> (mRef), 0); + mPtr = Ptr (&boost::any_cast&> (mRef), 0); return true; } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 09c4949d4..c8f60a2ee 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -21,7 +21,7 @@ namespace MWWorld /// \brief NPC object representing the player and additional player data class Player { - LiveCellRef mPlayer; + LiveCellRef mPlayer; MWWorld::Ptr::CellStore *mCellStore; MWRender::Player *mRenderer; MWWorld::World& mWorld; @@ -118,7 +118,7 @@ namespace MWWorld void setLeftRight (int value); void setForwardBackward (int value); - void setUpDown(int value); + void setUpDown(int value); void toggleRunning(); }; diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index c5461c469..b7469b8f5 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -8,7 +8,6 @@ #include #include "cellstore.hpp" -#include "refdata.hpp" namespace MWWorld { @@ -20,7 +19,8 @@ namespace MWWorld { public: - typedef MWWorld::CellStore CellStore; + typedef MWWorld::CellStore CellStore; + ///< \deprecated boost::any mPtr; ESM::CellRef *mCellRef; @@ -50,7 +50,7 @@ namespace MWWorld } template - Ptr (MWWorld::LiveCellRef *liveCellRef, CellStore *cell) + Ptr (MWWorld::LiveCellRef *liveCellRef, CellStore *cell) : mContainerStore (0) { mPtr = liveCellRef; @@ -61,9 +61,9 @@ namespace MWWorld } template - MWWorld::LiveCellRef *get() const + MWWorld::LiveCellRef *get() const { - return boost::any_cast*> (mPtr); + return boost::any_cast*> (mPtr); } ESM::CellRef& getCellRef() const; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 8a4373b1e..bbcbbf2af 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -23,7 +23,7 @@ namespace template void insertCellRefList(MWRender::RenderingManager& rendering, - T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics) + T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics) { if (!cellRefList.list.empty()) { @@ -348,67 +348,67 @@ namespace MWWorld // insert into the correct CellRefList if (type == typeid(ESM::Potion).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->potions.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); } else if (type == typeid(ESM::Apparatus).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->appas.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); } else if (type == typeid(ESM::Armor).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->armors.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); } else if (type == typeid(ESM::Book).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->books.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); } else if (type == typeid(ESM::Clothing).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->clothes.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); } else if (type == typeid(ESM::Ingredient).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->ingreds.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); } else if (type == typeid(ESM::Light).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->lights.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); } else if (type == typeid(ESM::Tool).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->lockpicks.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); } else if (type == typeid(ESM::Repair).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->repairs.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); } else if (type == typeid(ESM::Probe).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->probes.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); } else if (type == typeid(ESM::Weapon).name()) { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->weapons.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); } @@ -432,7 +432,7 @@ namespace MWWorld MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); - MWWorld::LiveCellRef* ref = newRef.getPtr().get(); + MWWorld::LiveCellRef* ref = newRef.getPtr().get(); cell->miscItems.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); @@ -444,7 +444,7 @@ namespace MWWorld } else { - MWWorld::LiveCellRef* ref = ptr.get(); + MWWorld::LiveCellRef* ref = ptr.get(); cell->miscItems.list.push_back( *ref ); newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 525ae33ce..e5633814f 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -33,10 +33,10 @@ namespace { template void listCellScripts (const ESMS::ESMStore& store, - MWWorld::CellRefList& cellRefList, MWWorld::LocalScripts& localScripts, + MWWorld::CellRefList& cellRefList, MWWorld::LocalScripts& localScripts, MWWorld::Ptr::CellStore *cell) { - for (typename MWWorld::CellRefList::List::iterator iter ( + for (typename MWWorld::CellRefList::List::iterator iter ( cellRefList.list.begin()); iter!=cellRefList.list.end(); ++iter) { @@ -53,10 +53,10 @@ namespace } template - MWWorld::LiveCellRef *searchViaHandle (const std::string& handle, - MWWorld::CellRefList& refList) + MWWorld::LiveCellRef *searchViaHandle (const std::string& handle, + MWWorld::CellRefList& refList) { - typedef typename MWWorld::CellRefList::List::iterator iterator; + typedef typename MWWorld::CellRefList::List::iterator iterator; for (iterator iter (refList.list.begin()); iter!=refList.list.end(); ++iter) { @@ -75,45 +75,45 @@ namespace MWWorld { Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) { - if (MWWorld::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.activators)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.potions)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.potions)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.appas)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.appas)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.armors)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.armors)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.books)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.books)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.clothes)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.clothes)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.containers)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.creatures)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.doors)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.doors)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.ingreds)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.lights)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.lights)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.lockpicks)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.lockpicks)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.miscItems)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.miscItems)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.npcs)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.npcs)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.probes)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.probes)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.repairs)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.repairs)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.statics)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.statics)) return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.weapons)) + if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.weapons)) return Ptr (ref, &cell); return Ptr(); } @@ -961,8 +961,8 @@ namespace MWWorld Ogre::Vector2 World::getNorthVector(Ptr::CellStore* cell) { - MWWorld::CellRefList statics = cell->statics; - MWWorld::LiveCellRef* ref = statics.find("northmarker"); + MWWorld::CellRefList statics = cell->statics; + MWWorld::LiveCellRef* ref = statics.find("northmarker"); if (!ref) return Vector2(0, 1); Ogre::SceneNode* node = ref->mData.getBaseNode(); From 2db6e4f4cf3bb271faa1cf43d85580cf6591a2fd Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Fri, 29 Jun 2012 19:13:12 +0200 Subject: [PATCH 032/688] Second try: added aspect ratios to the resolution combobox --- apps/launcher/graphicspage.cpp | 41 ++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index b202b1ecf..fb798fee8 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -1,11 +1,25 @@ #include +#include + #include #include #include "graphicspage.hpp" #include "naturalsort.hpp" +QString getAspect(int x, int y) +{ + int gcd = boost::math::gcd (x, y); + int xaspect = x / gcd; + int yaspect = y / gcd; + // special case: 8 : 5 is usually referred to as 16:10 + if (xaspect == 8 && yaspect == 5) + return QString("16:10"); + + return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); +} + GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent) : QWidget(parent) , mCfgMgr(cfg) @@ -163,7 +177,7 @@ void GraphicsPage::readConfig() QString resolution = QString::number(Settings::Manager::getInt("resolution x", "Video")); resolution.append(" x " + QString::number(Settings::Manager::getInt("resolution y", "Video"))); - int resIndex = mResolutionComboBox->findText(resolution); + int resIndex = mResolutionComboBox->findText(resolution, Qt::MatchStartsWith); if (resIndex != -1) mResolutionComboBox->setCurrentIndex(resIndex); } @@ -175,8 +189,8 @@ void GraphicsPage::writeConfig() Settings::Manager::setString("antialiasing", "Video", mAntiAliasingComboBox->currentText().toStdString()); Settings::Manager::setString("render system", "Video", mRendererComboBox->currentText().toStdString()); - // parse resolution x and y from a string like "800 x 600" - QString resolution = mResolutionComboBox->currentText(); + // Get the current resolution, but with the tabs replaced with a single space + QString resolution = mResolutionComboBox->currentText().simplified(); QStringList tokens = resolution.split(" ", QString::SkipEmptyParts); int resX = tokens.at(0).toInt(); @@ -235,22 +249,31 @@ QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer) Ogre::StringVector::iterator opt_it; uint idx = 0; + for (opt_it = i->second.possibleValues.begin (); - opt_it != i->second.possibleValues.end (); opt_it++, idx++) + opt_it != i->second.possibleValues.end (); opt_it++, idx++) { QString qval = QString::fromStdString(*opt_it).simplified(); // remove extra tokens after the resolution (for example bpp, can be there or not depending on rendersystem) QStringList tokens = qval.split(" ", QString::SkipEmptyParts); assert (tokens.size() >= 3); QString resolutionStr = tokens.at(0) + QString(" x ") + tokens.at(2); - { - // do not add duplicate resolutions - if (!result.contains(resolutionStr)) - result << resolutionStr; + // do not add duplicate resolutions + if (!result.contains(resolutionStr)) { + + QString aspect = getAspect(tokens.at(0).toInt(),tokens.at(2).toInt()); + + if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) { + resolutionStr.append(tr("\t(Widescreen ") + aspect + ")"); + + } else if (aspect == QLatin1String("4:3")) { + resolutionStr.append(tr("\t(Standard 4:3)")); + } + + result << resolutionStr; } } - } // Sort the resolutions in descending order From efb95e2f83b1d78d4d9c37e66011c4157266ac7b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 2 Jul 2012 14:25:46 +0200 Subject: [PATCH 033/688] Issue #317: check if spell actually exist in AddSpell --- apps/openmw/mwscript/statsextensions.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index d23519281..7f749185c 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -299,6 +299,9 @@ namespace MWScript std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + // make sure a spell with this ID actually exists. + MWBase::Environment::get().getWorld()->getStore().spells.find (id); + MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.add (id); } }; From 035c1c4b6e0f08c9fd4d6cbbfa72aafb68df6c45 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Jul 2012 01:06:38 +0200 Subject: [PATCH 034/688] added World interface class; cleanup of World interface --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 245 ++++++++++++++++++++++++++++++++++ apps/openmw/mwworld/world.cpp | 28 ++-- apps/openmw/mwworld/world.hpp | 149 ++++++++++----------- 4 files changed, 332 insertions(+), 92 deletions(-) create mode 100644 apps/openmw/mwbase/world.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a2ebc1191..770596c69 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment + environment world ) # Main executable diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp new file mode 100644 index 000000000..b2c19ab35 --- /dev/null +++ b/apps/openmw/mwbase/world.hpp @@ -0,0 +1,245 @@ +#ifndef GAME_MWBASE_WORLD_H +#define GAME_MWBASE_WORLD_H + +#include +#include +#include + +#include + +#include "../mwworld/globals.hpp" + +namespace Ogre +{ + class Vector2; + class Vector3; +} + +namespace OEngine +{ + namespace Render + { + class Fader; + } +} + +namespace ESM +{ + class ESMReader; + struct Position; + struct Cell; + struct Class; + struct Potion; +} + +namespace ESMS +{ + struct ESMStore; +} + +namespace MWWorld +{ + class CellStore; + class Player; + class LocalScripts; + class Ptr; + class TimeStamp; +} + +namespace MWBase +{ + class World + { + World (const World&); + ///< not implemented + + World& operator= (const World&); + ///< not implemented + + public: + + enum RenderMode + { + Render_CollisionDebug, + Render_Wireframe, + Render_Pathgrid, + Render_Compositors + }; + + World() {} + + virtual ~World() {} + + virtual OEngine::Render::Fader* getFader() = 0; + ///< \ŧodo remove this function. Rendering details should not be exposed. + + virtual MWWorld::CellStore *getExterior (int x, int y) = 0; + + virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; + + virtual void setWaterHeight(const float height) = 0; + + virtual void toggleWater() = 0; + + virtual void adjustSky() = 0; + + virtual void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches) = 0; + + virtual void setFallbackValues (const std::map& fallbackMap) = 0; + + virtual std::string getFallback (const std::string& key) const = 0; + + virtual std::string getFallback (const std::string& key, const std::string& def) const = 0; + + virtual MWWorld::Player& getPlayer() = 0; + + virtual const ESMS::ESMStore& getStore() const = 0; + + virtual ESM::ESMReader& getEsmReader() = 0; + + virtual MWWorld::LocalScripts& getLocalScripts() = 0; + + virtual bool hasCellChanged() const = 0; + ///< Has the player moved to a different cell, since the last frame? + + virtual bool isCellExterior() const = 0; + + virtual bool isCellQuasiExterior() const = 0; + + virtual Ogre::Vector2 getNorthVector (MWWorld::CellStore* cell) = 0; + ///< get north vector (OGRE coordinates) for given interior cell + + virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0; + + virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0; + + virtual char getGlobalVariableType (const std::string& name) const = 0; + ///< Return ' ', if there is no global variable with this name. + + virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0; + ///< Return a pointer to a liveCellRef with the given name. + /// \param activeOnly do non search inactive cells. + + virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0; + ///< Return a pointer to a liveCellRef with the given Ogre handle. + + /// \todo enable reference in the OGRE scene + virtual void enable (const MWWorld::Ptr& ptr) = 0; + + /// \todo disable reference in the OGRE scene + virtual void disable (const MWWorld::Ptr& ptr) = 0; + + virtual void advanceTime (double hours) = 0; + ///< Advance in-game time. + + virtual void setHour (double hour) = 0; + ///< Set in-game time hour. + + virtual void setMonth (int month) = 0; + ///< Set in-game time month. + + virtual void setDay (int day) = 0; + ///< Set in-game time day. + + virtual MWWorld::TimeStamp getTimeStamp() const = 0; + ///< Return current in-game time stamp. + + virtual bool toggleSky() = 0; + ///< \return Resulting mode + + virtual void changeWeather(const std::string& region, unsigned int id) = 0; + + virtual int getCurrentWeather() const = 0; + + virtual int getMasserPhase() const = 0; + + virtual int getSecundaPhase() const = 0; + + virtual void setMoonColour (bool red) = 0; + + virtual float getTimeScaleFactor() const = 0; + + virtual void changeToInteriorCell (const std::string& cellName, + const ESM::Position& position) = 0; + ///< Move to interior cell. + + virtual void changeToExteriorCell (const ESM::Position& position) = 0; + ///< Move to exterior cell. + + virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; + ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. + + virtual void markCellAsUnchanged() = 0; + + virtual std::string getFacedHandle() = 0; + ///< Return handle of the object the player is looking at + + virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; + + virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + + virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) + const = 0; + ///< Convert cell numbers to position. + + virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0; + ///< Convert position to cell numbers + + virtual void doPhysics (const std::vector >& actors, + float duration) = 0; + ///< Run physics simulation and modify \a world accordingly. + + virtual bool toggleCollisionMode() = 0; + ///< Toggle collision mode for player. If disabled player object should ignore + /// collisions and gravity. + ///< \return Resulting mode + + virtual bool toggleRenderMode (RenderMode mode) = 0; + ///< Toggle a render mode. + ///< \return Resulting mode + + virtual std::pair createRecord (const ESM::Potion& record) + = 0; + ///< Create a new recrod (of type potion) in the ESM store. + /// \return ID, pointer to created record + + virtual std::pair createRecord (const ESM::Class& record) + = 0; + ///< Create a new recrod (of type class) in the ESM store. + /// \return ID, pointer to created record + + virtual const ESM::Cell *createRecord (const ESM::Cell& record) = 0; + ///< Create a new recrod (of type cell) in the ESM store. + /// \return ID, pointer to created record + + virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, + int mode, int number = 1) = 0; + ///< Run animation for a MW-reference. Calls to this function for references that are + /// currently not in the rendered scene should be ignored. + /// + /// \param mode: 0 normal, 1 immediate start, 2 immediate loop + /// \param number How offen the animation should be run + + virtual void skipAnimation (const MWWorld::Ptr& ptr) = 0; + ///< Skip the animation for the given MW-reference for one frame. Calls to this function for + /// references that are currently not in the rendered scene should be ignored. + + virtual void update (float duration) = 0; + + virtual bool placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY) = 0; + ///< place an object into the gameworld at the specified cursor position + /// @param object + /// @param cursor X (relative 0-1) + /// @param cursor Y (relative 0-1) + /// @return true if the object was placed, or false if it was rejected because the position is too far away + + virtual void dropObjectOnGround (const MWWorld::Ptr& object) = 0; + + virtual bool canPlaceObject (float cursorX, float cursorY) = 0; + ///< @return true if it is possible to place on object at specified cursor location + + virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index e5633814f..b0067eabf 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -154,19 +154,19 @@ namespace MWWorld mRendering->skyDisable(); } - void World::setFallbackValues(std::map fallbackMap) + void World::setFallbackValues (const std::map& fallbackMap) { mFallback = fallbackMap; } - std::string World::getFallback(std::string key) + std::string World::getFallback (const std::string& key) const { return getFallback(key, ""); } - std::string World::getFallback(std::string key, std::string def) + std::string World::getFallback (const std::string& key, const std::string& def) const { - std::map::iterator it; + std::map::const_iterator it; if((it = mFallback.find(key)) == mFallback.end()) { return def; @@ -351,7 +351,7 @@ namespace MWWorld throw std::runtime_error ("unknown Ogre handle: " + handle); } - void World::enable (Ptr reference) + void World::enable (const Ptr& reference) { if (!reference.getRefData().isEnabled()) { @@ -362,7 +362,7 @@ namespace MWWorld } } - void World::disable (Ptr reference) + void World::disable (const Ptr& reference) { if (reference.getRefData().isEnabled()) { @@ -537,7 +537,7 @@ namespace MWWorld } } - void World::deleteObject (Ptr ptr) + void World::deleteObject (const Ptr& ptr) { if (ptr.getRefData().getCount()>0) { @@ -552,7 +552,7 @@ namespace MWWorld } } - bool World::moveObjectImp (Ptr ptr, float x, float y, float z) + bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) { bool ret = false; ptr.getRefData().getPosition().pos[0] = x; @@ -590,7 +590,7 @@ namespace MWWorld return ret; } - void World::moveObject (Ptr ptr, float x, float y, float z) + void World::moveObject (const Ptr& ptr, float x, float y, float z) { moveObjectImp(ptr, x, y, z); @@ -753,6 +753,8 @@ namespace MWWorld void World::update (float duration) { + /// \todo split this function up into subfunctions + mWorldScene->update (duration); mWeatherManager->update (duration); @@ -959,7 +961,7 @@ namespace MWWorld return mRendering->getFader(); } - Ogre::Vector2 World::getNorthVector(Ptr::CellStore* cell) + Ogre::Vector2 World::getNorthVector (CellStore* cell) { MWWorld::CellRefList statics = cell->statics; MWWorld::LiveCellRef* ref = statics.find("northmarker"); @@ -981,14 +983,14 @@ namespace MWWorld mRendering->toggleWater(); } - bool World::placeObject(MWWorld::Ptr object, float cursorX, float cursorY) + bool World::placeObject (const Ptr& object, float cursorX, float cursorY) { std::pair result = mPhysics->castRay(cursorX, cursorY); if (!result.first) return false; - MWWorld::Ptr::CellStore* cell; + CellStore* cell; if (isCellExterior()) { int cellX, cellY; @@ -1021,7 +1023,7 @@ namespace MWWorld return true; } - void World::dropObjectOnGround(MWWorld::Ptr object) + void World::dropObjectOnGround (const Ptr& object) { MWWorld::Ptr::CellStore* cell = getPlayer().getPlayer().getCell(); diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index f0da56b65..526a2d7a6 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -25,6 +25,8 @@ #include +#include "../mwbase/world.hpp" + namespace Ogre { class Vector3; @@ -58,20 +60,8 @@ namespace MWWorld /// \brief The game world and its visual representation - class World + class World : public MWBase::World { - public: - - enum RenderMode - { - Render_CollisionDebug, - Render_Wireframe, - Render_Pathgrid, - Render_Compositors - }; - - private: - MWRender::RenderingManager* mRendering; MWWorld::WeatherManager* mWeatherManager; @@ -109,180 +99,183 @@ namespace MWWorld int getDaysPerMonth (int month) const; - bool moveObjectImp (Ptr ptr, float x, float y, float z); + bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed public: - World (OEngine::Render::OgreRenderer& renderer, + World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, bool newGame, const std::string& encoding, std::map fallbackMap); - ~World(); + virtual ~World(); - OEngine::Render::Fader* getFader(); + virtual OEngine::Render::Fader* getFader(); + ///< \ŧodo remove this function. Rendering details should not be exposed. - Ptr::CellStore *getExterior (int x, int y); + virtual CellStore *getExterior (int x, int y); - Ptr::CellStore *getInterior (const std::string& name); + virtual CellStore *getInterior (const std::string& name); - void setWaterHeight(const float height); - void toggleWater(); + virtual void setWaterHeight(const float height); - void adjustSky(); + virtual void toggleWater(); - void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); + virtual void adjustSky(); - void setFallbackValues(std::map fallbackMap); + virtual void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); - std::string getFallback(std::string key); + virtual void setFallbackValues (const std::map& fallbackMap); - std::string getFallback(std::string key, std::string def); + virtual std::string getFallback (const std::string& key) const; - MWWorld::Player& getPlayer(); + virtual std::string getFallback (const std::string& key, const std::string& def) const; - const ESMS::ESMStore& getStore() const; + virtual Player& getPlayer(); - ESM::ESMReader& getEsmReader(); + virtual const ESMS::ESMStore& getStore() const; - LocalScripts& getLocalScripts(); + virtual ESM::ESMReader& getEsmReader(); - bool hasCellChanged() const; + virtual LocalScripts& getLocalScripts(); + + virtual bool hasCellChanged() const; ///< Has the player moved to a different cell, since the last frame? - bool isCellExterior() const; - bool isCellQuasiExterior() const; + virtual bool isCellExterior() const; - Ogre::Vector2 getNorthVector(Ptr::CellStore* cell); + virtual bool isCellQuasiExterior() const; + + virtual Ogre::Vector2 getNorthVector (CellStore* cell); ///< get north vector (OGRE coordinates) for given interior cell - Globals::Data& getGlobalVariable (const std::string& name); + virtual Globals::Data& getGlobalVariable (const std::string& name); - Globals::Data getGlobalVariable (const std::string& name) const; + virtual Globals::Data getGlobalVariable (const std::string& name) const; - char getGlobalVariableType (const std::string& name) const; + virtual char getGlobalVariableType (const std::string& name) const; ///< Return ' ', if there is no global variable with this name. - Ptr getPtr (const std::string& name, bool activeOnly); + virtual Ptr getPtr (const std::string& name, bool activeOnly); ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. - Ptr getPtrViaHandle (const std::string& handle); + virtual Ptr getPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle. - /// \todo enable reference in the OGRE scene - void enable (Ptr reference); + virtual void enable (const Ptr& ptr); - /// \todo 5disable reference in the OGRE scene - void disable (Ptr reference); + virtual void disable (const Ptr& ptr); - void advanceTime (double hours); + virtual void advanceTime (double hours); ///< Advance in-game time. - void setHour (double hour); + virtual void setHour (double hour); ///< Set in-game time hour. - void setMonth (int month); + virtual void setMonth (int month); ///< Set in-game time month. - void setDay (int day); + virtual void setDay (int day); ///< Set in-game time day. - TimeStamp getTimeStamp() const; + virtual TimeStamp getTimeStamp() const; ///< Return current in-game time stamp. - bool toggleSky(); + virtual bool toggleSky(); ///< \return Resulting mode - void changeWeather(const std::string& region, const unsigned int id); + virtual void changeWeather (const std::string& region, const unsigned int id); - int getCurrentWeather() const; + virtual int getCurrentWeather() const; - int getMasserPhase() const; + virtual int getMasserPhase() const; - int getSecundaPhase() const; + virtual int getSecundaPhase() const; - void setMoonColour (bool red); + virtual void setMoonColour (bool red); - float getTimeScaleFactor() const; + virtual float getTimeScaleFactor() const; - void changeToInteriorCell (const std::string& cellName, const ESM::Position& position); + virtual void changeToInteriorCell (const std::string& cellName, + const ESM::Position& position); ///< Move to interior cell. - void changeToExteriorCell (const ESM::Position& position); + virtual void changeToExteriorCell (const ESM::Position& position); ///< Move to exterior cell. - const ESM::Cell *getExterior (const std::string& cellName) const; + virtual const ESM::Cell *getExterior (const std::string& cellName) const; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. - void markCellAsUnchanged(); + virtual void markCellAsUnchanged(); - std::string getFacedHandle(); + virtual std::string getFacedHandle(); ///< Return handle of the object the player is looking at - void deleteObject (Ptr ptr); + virtual void deleteObject (const Ptr& ptr); - void moveObject (Ptr ptr, float x, float y, float z); + virtual void moveObject (const Ptr& ptr, float x, float y, float z); - void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; + virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) + const; ///< Convert cell numbers to position. - void positionToIndex (float x, float y, int &cellX, int &cellY) const; + virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const; ///< Convert position to cell numbers - void doPhysics (const std::vector >& actors, + virtual void doPhysics (const std::vector >& actors, float duration); ///< Run physics simulation and modify \a world accordingly. - bool toggleCollisionMode(); + virtual bool toggleCollisionMode(); ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. ///< \return Resulting mode - bool toggleRenderMode (RenderMode mode); + virtual bool toggleRenderMode (RenderMode mode); ///< Toggle a render mode. ///< \return Resulting mode - std::pair createRecord (const ESM::Potion& record); + virtual std::pair createRecord (const ESM::Potion& record); ///< Create a new recrod (of type potion) in the ESM store. /// \return ID, pointer to created record - std::pair createRecord (const ESM::Class& record); + virtual std::pair createRecord (const ESM::Class& record); ///< Create a new recrod (of type class) in the ESM store. /// \return ID, pointer to created record - const ESM::Cell *createRecord (const ESM::Cell& record); + virtual const ESM::Cell *createRecord (const ESM::Cell& record); ///< Create a new recrod (of type cell) in the ESM store. /// \return ID, pointer to created record - void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, - int number = 1); + virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, + int mode, int number = 1); ///< Run animation for a MW-reference. Calls to this function for references that are /// currently not in the rendered scene should be ignored. /// /// \param mode: 0 normal, 1 immediate start, 2 immediate loop /// \param number How offen the animation should be run - void skipAnimation (const MWWorld::Ptr& ptr); + virtual void skipAnimation (const MWWorld::Ptr& ptr); ///< Skip the animation for the given MW-reference for one frame. Calls to this function for /// references that are currently not in the rendered scene should be ignored. - void update (float duration); + virtual void update (float duration); - bool placeObject(MWWorld::Ptr object, float cursorX, float cursorY); + virtual bool placeObject (const Ptr& object, float cursorX, float cursorY); ///< place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @return true if the object was placed, or false if it was rejected because the position is too far away - void dropObjectOnGround(MWWorld::Ptr object); + virtual void dropObjectOnGround (const Ptr& object); - bool canPlaceObject(float cursorX, float cursorY); + virtual bool canPlaceObject(float cursorX, float cursorY); ///< @return true if it is possible to place on object at specified cursor location - void processChangedSettings(const Settings::CategorySettingVector& settings); + virtual void processChangedSettings(const Settings::CategorySettingVector& settings); }; } From 046e9686f9f845616b01f1cec5b5a8429651af71 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 2 Jul 2012 21:41:21 -0700 Subject: [PATCH 035/688] Cleanup RecordPtrT This moves the index resolution into a separate post method instead of always checking when access. As a result, it reduces the size of it down to the size of a pointer, as opposed to 2 pointers + 1 int. The appropriate methods are added to the various node types to make sure they're resolved. --- components/nif/controlled.hpp | 105 +-- components/nif/controller.hpp | 201 ++++-- components/nif/data.hpp | 1251 ++++++++++++++++----------------- components/nif/effect.hpp | 85 +-- components/nif/extra.hpp | 82 +-- components/nif/nif_file.cpp | 24 +- components/nif/nif_file.hpp | 142 ++-- components/nif/node.hpp | 296 ++++---- components/nif/property.hpp | 298 ++++---- components/nif/record.hpp | 27 +- components/nif/record_ptr.hpp | 136 ++-- 11 files changed, 1364 insertions(+), 1283 deletions(-) diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index d59950132..24281146f 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -25,6 +25,7 @@ #define _NIF_CONTROLLED_H_ #include "extra.hpp" +#include "controller.hpp" namespace Nif { @@ -33,92 +34,104 @@ namespace Nif class Controlled : public Extra { public: - ControllerPtr controller; + ControllerPtr controller; - void read(NIFFile *nif) - { - Extra::read(nif); - controller.read(nif); - } + void read(NIFFile *nif) + { + Extra::read(nif); + controller.read(nif); + } + + void post(NIFFile *nif) + { + Extra::post(nif); + controller.post(nif); + } }; /// Has name, extra-data and controller class Named : public Controlled { public: - Misc::SString name; + Misc::SString name; - void read(NIFFile *nif) - { - name = nif->getString(); - Controlled::read(nif); - } + void read(NIFFile *nif) + { + name = nif->getString(); + Controlled::read(nif); + } }; typedef Named NiSequenceStreamHelper; class NiParticleGrowFade : public Controlled { public: - void read(NIFFile *nif) - { - Controlled::read(nif); + void read(NIFFile *nif) + { + Controlled::read(nif); - // Two floats. - nif->skip(8); - } + // Two floats. + nif->skip(8); + } }; class NiParticleColorModifier : public Controlled { public: - NiColorDataPtr data; + NiColorDataPtr data; - void read(NIFFile *nif) - { - Controlled::read(nif); - data.read(nif); - } + void read(NIFFile *nif) + { + Controlled::read(nif); + data.read(nif); + } + + void post(NIFFile *nif) + { + Controlled::post(nif); + data.post(nif); + } }; class NiGravity : public Controlled { public: - void read(NIFFile *nif) - { - Controlled::read(nif); + void read(NIFFile *nif) + { + Controlled::read(nif); - // two floats, one int, six floats - nif->skip(9*4); - } + // two floats, one int, six floats + nif->skip(9*4); + } }; // NiPinaColada class NiPlanarCollider : public Controlled { public: - void read(NIFFile *nif) - { - Controlled::read(nif); + void read(NIFFile *nif) + { + Controlled::read(nif); - // (I think) 4 floats + 4 vectors - nif->skip(4*16); - } + // (I think) 4 floats + 4 vectors + nif->skip(4*16); + } }; class NiParticleRotation : public Controlled { public: - void read(NIFFile *nif) - { - Controlled::read(nif); + void read(NIFFile *nif) + { + Controlled::read(nif); - /* - byte (0 or 1) - float (1) - float*3 - */ - nif->skip(17); - } + /* + byte (0 or 1) + float (1) + float*3 + */ + nif->skip(17); + } }; } // Namespace diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index d6fb22255..d00c1bc0e 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -34,136 +34,187 @@ namespace Nif class Controller : public Record { public: - ControllerPtr next; - int flags; - float frequency, phase; - float timeStart, timeStop; - ControlledPtr target; + ControllerPtr next; + int flags; + float frequency, phase; + float timeStart, timeStop; + ControlledPtr target; - void read(NIFFile *nif) - { - next.read(nif); + void read(NIFFile *nif) + { + next.read(nif); - flags = nif->getShort(); + flags = nif->getShort(); - frequency = nif->getFloat(); - phase = nif->getFloat(); - timeStart = nif->getFloat(); - timeStop = nif->getFloat(); + frequency = nif->getFloat(); + phase = nif->getFloat(); + timeStart = nif->getFloat(); + timeStop = nif->getFloat(); - target.read(nif); - } + target.read(nif); + } + + void post(NIFFile *nif) + { + Record::post(nif); + next.post(nif); + target.post(nif); + } }; class NiBSPArrayController : public Controller { public: - void read(NIFFile *nif) - { - Controller::read(nif); + void read(NIFFile *nif) + { + Controller::read(nif); - // At the moment, just skip it all - nif->skip(111); - int s = nif->getShort(); - nif->skip(15 + s*40); - } + // At the moment, just skip it all + nif->skip(111); + int s = nif->getShort(); + nif->skip(15 + s*40); + } }; typedef NiBSPArrayController NiParticleSystemController; class NiMaterialColorController : public Controller { public: - NiPosDataPtr data; + NiPosDataPtr data; - void read(NIFFile *nif) - { - Controller::read(nif); - data.read(nif); - } + void read(NIFFile *nif) + { + Controller::read(nif); + data.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + data.post(nif); + } }; class NiPathController : public Controller { public: - NiPosDataPtr posData; - NiFloatDataPtr floatData; + NiPosDataPtr posData; + NiFloatDataPtr floatData; - void read(NIFFile *nif) - { - Controller::read(nif); + void read(NIFFile *nif) + { + Controller::read(nif); - /* - int = 1 - 2xfloat - short = 0 or 1 - */ - nif->skip(14); - posData.read(nif); - floatData.read(nif); - } + /* + int = 1 + 2xfloat + short = 0 or 1 + */ + nif->skip(14); + posData.read(nif); + floatData.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + + posData.post(nif); + floatData.post(nif); + } }; class NiUVController : public Controller { public: - NiUVDataPtr data; + NiUVDataPtr data; - void read(NIFFile *nif) - { - Controller::read(nif); + void read(NIFFile *nif) + { + Controller::read(nif); - nif->getShort(); // always 0 - data.read(nif); - } + nif->getShort(); // always 0 + data.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + data.post(nif); + } }; class NiKeyframeController : public Controller { public: - NiKeyframeDataPtr data; + NiKeyframeDataPtr data; - void read(NIFFile *nif) - { - Controller::read(nif); - data.read(nif); - } + void read(NIFFile *nif) + { + Controller::read(nif); + data.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + data.post(nif); + } }; class NiAlphaController : public Controller { public: - NiFloatDataPtr data; + NiFloatDataPtr data; - void read(NIFFile *nif) - { - Controller::read(nif); - data.read(nif); - } + void read(NIFFile *nif) + { + Controller::read(nif); + data.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + data.post(nif); + } }; class NiGeomMorpherController : public Controller { public: - NiMorphDataPtr data; + NiMorphDataPtr data; - void read(NIFFile *nif) - { - Controller::read(nif); - data.read(nif); - nif->getByte(); // always 0 - } + void read(NIFFile *nif) + { + Controller::read(nif); + data.read(nif); + nif->getByte(); // always 0 + } + + void post(NIFFile *nif) + { + Controller::post(nif); + data.post(nif); + } }; class NiVisController : public Controller { public: - NiVisDataPtr data; + NiVisDataPtr data; - void read(NIFFile *nif) - { - Controller::read(nif); - data.read(nif); - } + void read(NIFFile *nif) + { + Controller::read(nif); + data.read(nif); + } + + void post(NIFFile *nif) + { + Controller::post(nif); + data.post(nif); + } }; } // Namespace diff --git a/components/nif/data.hpp b/components/nif/data.hpp index df9079758..eaa11b5ee 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -34,794 +34,777 @@ namespace Nif class NiSourceTexture : public Named { public: + // Is this an external (references a separate texture file) or + // internal (data is inside the nif itself) texture? + bool external; - // Is this an external (references a separate texture file) or - // internal (data is inside the nif itself) texture? - bool external; + Misc::SString filename; // In case of external textures + NiPixelDataPtr data; // In case of internal textures - Misc::SString filename; // In case of external textures - NiPixelDataPtr data; // In case of internal textures + /* Pixel layout + 0 - Palettised + 1 - High color 16 + 2 - True color 32 + 3 - Compressed + 4 - Bumpmap + 5 - Default */ + int pixel; - /* Pixel layout - 0 - Palettised - 1 - High color 16 - 2 - True color 32 - 3 - Compressed - 4 - Bumpmap - 5 - Default */ - int pixel; + /* Mipmap format + 0 - no + 1 - yes + 2 - default */ + int mipmap; - /* Mipmap format - 0 - no - 1 - yes - 2 - default */ - int mipmap; + /* Alpha + 0 - none + 1 - binary + 2 - smooth + 3 - default (use material alpha, or multiply material with texture if present) + */ + int alpha; - /* Alpha - 0 - none - 1 - binary - 2 - smooth - 3 - default (use material alpha, or multiply material with texture if present) - */ - int alpha; + void read(NIFFile *nif) + { + Named::read(nif); - void read(NIFFile *nif) - { - Named::read(nif); + external = !!nif->getByte(); + if(external) + filename = nif->getString(); + else + { + nif->getByte(); // always 1 + data.read(nif); + } - external = !!nif->getByte(); + pixel = nif->getInt(); + mipmap = nif->getInt(); + alpha = nif->getInt(); - if(external) filename = nif->getString(); - else - { nif->getByte(); // always 1 - data.read(nif); - } + } - pixel = nif->getInt(); - mipmap = nif->getInt(); - alpha = nif->getInt(); - - nif->getByte(); // always 1 - } + void post(NIFFile *nif) + { + Named::post(nif); + data.post(nif); + } }; // Common ancestor for several data classes class ShapeData : public Record { public: - Misc::FloatArray vertices, normals, colors, uvlist; - const Vector *center; - float radius; + Misc::FloatArray vertices, normals, colors, uvlist; + const Vector *center; + float radius; - void read(NIFFile *nif) - { - int verts = nif->getShort(); + void read(NIFFile *nif) + { + int verts = nif->getShort(); - if(nif->getInt()) - vertices = nif->getFloatLen(verts*3); + if(nif->getInt()) + vertices = nif->getFloatLen(verts*3); - if(nif->getInt()) - normals = nif->getFloatLen(verts*3); + if(nif->getInt()) + normals = nif->getFloatLen(verts*3); - center = nif->getVector(); - radius = nif->getFloat(); + center = nif->getVector(); + radius = nif->getFloat(); - if(nif->getInt()) - colors = nif->getFloatLen(verts*4); + if(nif->getInt()) + colors = nif->getFloatLen(verts*4); - int uvs = nif->getShort(); + int uvs = nif->getShort(); - // Only the first 6 bits are used as a count. I think the rest are - // flags of some sort. - uvs &= 0x3f; + // Only the first 6 bits are used as a count. I think the rest are + // flags of some sort. + uvs &= 0x3f; - if(nif->getInt()) - uvlist = nif->getFloatLen(uvs*verts*2); - } + if(nif->getInt()) + uvlist = nif->getFloatLen(uvs*verts*2); + } }; class NiTriShapeData : public ShapeData { public: - // Triangles, three vertex indices per triangle - Misc::SliceArray triangles; + // Triangles, three vertex indices per triangle + Misc::SliceArray triangles; - void read(NIFFile *nif) - { - ShapeData::read(nif); + void read(NIFFile *nif) + { + ShapeData::read(nif); - int tris = nif->getShort(); - if(tris) - { - // We have three times as many vertices as triangles, so this - // is always equal to tris*3. - int cnt = nif->getInt(); - triangles = nif->getArrayLen(cnt); - } + int tris = nif->getShort(); + if(tris) + { + // We have three times as many vertices as triangles, so this + // is always equal to tris*3. + int cnt = nif->getInt(); + triangles = nif->getArrayLen(cnt); + } - // Read the match list, which lists the vertices that are equal to - // vertices. We don't actually need need this for anything, so - // just skip it. - int verts = nif->getShort(); - if(verts) - { - for(int i=0;igetShort(); - nif->skip(num*sizeof(short)); - } - } - } + // Read the match list, which lists the vertices that are equal to + // vertices. We don't actually need need this for anything, so + // just skip it. + int verts = nif->getShort(); + if(verts) + { + for(int i=0;igetShort(); + nif->skip(num*sizeof(short)); + } + } + } }; class NiAutoNormalParticlesData : public ShapeData { public: - int activeCount; + int activeCount; - void read(NIFFile *nif) - { - ShapeData::read(nif); + void read(NIFFile *nif) + { + ShapeData::read(nif); - // Should always match the number of vertices - activeCount = nif->getShort(); + // Should always match the number of vertices + activeCount = nif->getShort(); - // Skip all the info, we don't support particles yet - nif->getFloat(); // Active radius ? - nif->getShort(); // Number of valid entries in the following arrays ? + // Skip all the info, we don't support particles yet + nif->getFloat(); // Active radius ? + nif->getShort(); // Number of valid entries in the following arrays ? - if(nif->getInt()) - // Particle sizes - nif->getFloatLen(activeCount); - } + if(nif->getInt()) + { + // Particle sizes + nif->getFloatLen(activeCount); + } + } }; class NiRotatingParticlesData : public NiAutoNormalParticlesData { public: - void read(NIFFile *nif) - { - NiAutoNormalParticlesData::read(nif); + void read(NIFFile *nif) + { + NiAutoNormalParticlesData::read(nif); - if(nif->getInt()) - // Rotation quaternions. I THINK activeCount is correct here, - // but verts (vertex number) might also be correct, if there is - // any case where the two don't match. - nif->getArrayLen(activeCount); - } + if(nif->getInt()) + { + // Rotation quaternions. I THINK activeCount is correct here, + // but verts (vertex number) might also be correct, if there is + // any case where the two don't match. + nif->getArrayLen(activeCount); + } + } }; class NiPosData : public Record { public: - void read(NIFFile *nif) - { - int count = nif->getInt(); - int type = nif->getInt(); - if(type != 1 && type != 2) - nif->fail("Cannot handle NiPosData type"); + void read(NIFFile *nif) + { + int count = nif->getInt(); + int type = nif->getInt(); + if(type != 1 && type != 2) + nif->fail("Cannot handle NiPosData type"); - // TODO: Could make structs of these. Seems to be identical to - // translation in NiKeyframeData. - for(int i=0; igetFloat(); - nif->getVector(); // This isn't really shared between type 1 - // and type 2, most likely - if(type == 2) - { - nif->getVector(); - nif->getVector(); - } - } - } + // TODO: Could make structs of these. Seems to be identical to + // translation in NiKeyframeData. + for(int i=0; igetFloat(); + nif->getVector(); // This isn't really shared between type 1 + // and type 2, most likely + if(type == 2) + { + nif->getVector(); + nif->getVector(); + } + } + } }; class NiUVData : public Record { public: - void read(NIFFile *nif) - { - // TODO: This is claimed to be a "float animation key", which is - // also used in FloatData and KeyframeData. We could probably - // reuse and refactor a lot of this if we actually use it at some - // point. - - for(int i=0; i<2; i++) - { - int count = nif->getInt(); - - if(count) - { - nif->getInt(); // always 2 - nif->getArrayLen(count); // Really one time float + one vector - } - } - // Always 0 - nif->getInt(); - nif->getInt(); - } + void read(NIFFile *nif) + { + // TODO: This is claimed to be a "float animation key", which is + // also used in FloatData and KeyframeData. We could probably + // reuse and refactor a lot of this if we actually use it at some + // point. + for(int i=0; i<2; i++) + { + int count = nif->getInt(); + if(count) + { + nif->getInt(); // always 2 + nif->getArrayLen(count); // Really one time float + one vector + } + } + // Always 0 + nif->getInt(); + nif->getInt(); + } }; class NiFloatData : public Record { public: - void read(NIFFile *nif) - { - int count = nif->getInt(); - nif->getInt(); // always 2 - nif->getArrayLen(count); // Really one time float + one vector - } + void read(NIFFile *nif) + { + int count = nif->getInt(); + nif->getInt(); // always 2 + nif->getArrayLen(count); // Really one time float + one vector + } }; class NiPixelData : public Record { public: - unsigned int rmask, gmask, bmask, amask; - int bpp, mips; + unsigned int rmask, gmask, bmask, amask; + int bpp, mips; - void read(NIFFile *nif) - { - nif->getInt(); // always 0 or 1 + void read(NIFFile *nif) + { + nif->getInt(); // always 0 or 1 - rmask = nif->getInt(); // usually 0xff - gmask = nif->getInt(); // usually 0xff00 - bmask = nif->getInt(); // usually 0xff0000 - amask = nif->getInt(); // usually 0xff000000 or zero + rmask = nif->getInt(); // usually 0xff + gmask = nif->getInt(); // usually 0xff00 + bmask = nif->getInt(); // usually 0xff0000 + amask = nif->getInt(); // usually 0xff000000 or zero - bpp = nif->getInt(); + bpp = nif->getInt(); - // Unknown - nif->skip(12); + // Unknown + nif->skip(12); - mips = nif->getInt(); + mips = nif->getInt(); - // Bytes per pixel, should be bpp * 8 - /*int bytes =*/ nif->getInt(); + // Bytes per pixel, should be bpp * 8 + /*int bytes =*/ nif->getInt(); - for(int i=0; igetInt(); - /*int y =*/ nif->getInt(); - /*int offset =*/ nif->getInt(); - } + for(int i=0; igetInt(); + /*int y =*/ nif->getInt(); + /*int offset =*/ nif->getInt(); + } - // Skip the data - unsigned int dataSize = nif->getInt(); - nif->skip(dataSize); - } + // Skip the data + unsigned int dataSize = nif->getInt(); + nif->skip(dataSize); + } }; class NiColorData : public Record { public: - struct ColorData - { - float time; - Vector4 rgba; - }; + struct ColorData + { + float time; + Vector4 rgba; + }; - void read(NIFFile *nif) - { - int count = nif->getInt(); - nif->getInt(); // always 1 + void read(NIFFile *nif) + { + int count = nif->getInt(); + nif->getInt(); // always 1 - // Skip the data - assert(sizeof(ColorData) == 4*5); - nif->skip(sizeof(ColorData) * count); - } + // Skip the data + assert(sizeof(ColorData) == 4*5); + nif->skip(sizeof(ColorData) * count); + } }; class NiVisData : public Record { public: - void read(NIFFile *nif) - { - int count = nif->getInt(); - /* - Each VisData consists of: - float time; - byte isSet; + void read(NIFFile *nif) + { + int count = nif->getInt(); + /* + Each VisData consists of: + float time; + byte isSet; - If you implement this, make sure you use a packed struct - (sizeof==5), or read each element individually. - */ - nif->skip(count*5); - } + If you implement this, make sure you use a packed struct + (sizeof==5), or read each element individually. + */ + nif->skip(count*5); + } }; class NiSkinInstance : public Record { public: - NiSkinDataPtr data; - NodePtr root; - NodeList bones; + NiSkinDataPtr data; + NodePtr root; + NodeList bones; - void read(NIFFile *nif) - { - data.read(nif); - root.read(nif); - bones.read(nif); + void read(NIFFile *nif) + { + data.read(nif); + root.read(nif); + bones.read(nif); + } - if(data.empty() || root.empty()) - nif->fail("NiSkinInstance missing root or data"); - } - - void post(NIFFile *nif); + void post(NIFFile *nif); }; class NiSkinData : public Record { public: - // This is to make sure the structs are packed, ie. that the - // compiler doesn't mess them up with extra alignment bytes. + // This is to make sure the structs are packed, ie. that the + // compiler doesn't mess them up with extra alignment bytes. #pragma pack(push) #pragma pack(1) - struct BoneTrafo - { - Matrix rotation; // Rotation offset from bone? - Vector trans; // Translation - float scale; // Probably scale (always 1) - }; - struct BoneTrafoCopy - { - Ogre::Quaternion rotation; - Ogre::Vector3 trans; - float scale; - }; + struct BoneTrafo + { + Matrix rotation; // Rotation offset from bone? + Vector trans; // Translation + float scale; // Probably scale (always 1) + }; + struct BoneTrafoCopy + { + Ogre::Quaternion rotation; + Ogre::Vector3 trans; + float scale; + }; - struct VertWeight - { - short vertex; - float weight; - }; + struct VertWeight + { + short vertex; + float weight; + }; #pragma pack(pop) - struct BoneInfo - { - const BoneTrafo *trafo; - const Vector4 *unknown; - Misc::SliceArray weights; - }; - struct BoneInfoCopy - { - std::string bonename; - unsigned short bonehandle; - BoneTrafoCopy trafo; - Vector4 unknown; - //std::vector weights; - }; - struct IndividualWeight - { - float weight; + struct BoneInfo + { + const BoneTrafo *trafo; + const Vector4 *unknown; + Misc::SliceArray weights; + }; + struct BoneInfoCopy + { + std::string bonename; + unsigned short bonehandle; + BoneTrafoCopy trafo; + Vector4 unknown; + //std::vector weights; + }; + struct IndividualWeight + { + float weight; unsigned int boneinfocopyindex; - }; + }; - const BoneTrafo *trafo; - std::vector bones; + const BoneTrafo *trafo; + std::vector bones; - void read(NIFFile *nif) - { - assert(sizeof(BoneTrafo) == 4*(9+3+1)); - assert(sizeof(VertWeight) == 6); + void read(NIFFile *nif) + { + assert(sizeof(BoneTrafo) == 4*(9+3+1)); + assert(sizeof(VertWeight) == 6); - trafo = nif->getPtr(); + trafo = nif->getPtr(); - int boneNum = nif->getInt(); - nif->getInt(); // -1 + int boneNum = nif->getInt(); + nif->getInt(); // -1 - bones.resize(boneNum); + bones.resize(boneNum); + for(int i=0;igetPtr(); + bi.unknown = nif->getVector4(); - bi.trafo = nif->getPtr(); - bi.unknown = nif->getVector4(); - - // Number of vertex weights - int count = nif->getShort(); - bi.weights = nif->getArrayLen(count); - } - } + // Number of vertex weights + int count = nif->getShort(); + bi.weights = nif->getArrayLen(count); + } + } }; class NiMorphData : public Record { - float startTime; - float stopTime; - std::vector initialVertices; - std::vector > relevantTimes; - std::vector > relevantData; - std::vector > additionalVertices; - + float startTime; + float stopTime; + std::vector initialVertices; + std::vector > relevantTimes; + std::vector > relevantData; + std::vector > additionalVertices; public: - float getStartTime(){ - return startTime; - } - float getStopTime(){ - return stopTime; - } - void setStartTime(float time){ - startTime = time; - } + float getStartTime() const + { return startTime; } + float getStopTime() const + { return stopTime; } - void setStopTime(float time){ - stopTime = time; - } - std::vector getInitialVertices(){ - return initialVertices; - } - std::vector > getRelevantData(){ - return relevantData; - } - std::vector > getRelevantTimes(){ - return relevantTimes; - } - std::vector > getAdditionalVertices(){ - return additionalVertices; - } - -void read(NIFFile *nif) - { - int morphCount = nif->getInt(); - int vertCount = nif->getInt(); - nif->getByte(); - int magic = nif->getInt(); - /*int type =*/ nif->getInt(); - for(int i = 0; i < vertCount; i++){ + void setStartTime(float time) + { startTime = time; } + void setStopTime(float time) + { stopTime = time; } - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - initialVertices.push_back(Ogre::Vector3(x, y, z)); - } - - for(int i=1; i& getInitialVertices() const + { return initialVertices; } + const std::vector >& getRelevantData() const + { return relevantData; } + const std::vector >& getRelevantTimes() const + { return relevantTimes; } + const std::vector >& getAdditionalVertices() const + { return additionalVertices; } + + void read(NIFFile *nif) { - magic = nif->getInt(); - /*type =*/ nif->getInt(); - std::vector current; - std::vector currentTime; - for(int i = 0; i < magic; i++){ - // Time, data, forward, backward tangents - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - current.push_back(Ogre::Vector3(x,y,z)); - currentTime.push_back(time); - //nif->getFloatLen(4*magic); - } - if(magic){ - relevantData.push_back(current); - relevantTimes.push_back(currentTime); - } - std::vector verts; - for(int i = 0; i < vertCount; i++){ - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - verts.push_back(Ogre::Vector3(x, y, z)); - } - additionalVertices.push_back(verts); - } - } + int morphCount = nif->getInt(); + int vertCount = nif->getInt(); + nif->getByte(); + int magic = nif->getInt(); + /*int type =*/ nif->getInt(); + + for(int i = 0; i < vertCount; i++) + { + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); + initialVertices.push_back(Ogre::Vector3(x, y, z)); + } + + for(int i=1; igetInt(); + /*type =*/ nif->getInt(); + std::vector current; + std::vector currentTime; + for(int i = 0; i < magic; i++) + { + // Time, data, forward, backward tangents + float time = nif->getFloat(); + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); + current.push_back(Ogre::Vector3(x,y,z)); + currentTime.push_back(time); + //nif->getFloatLen(4*magic); + } + + if(magic) + { + relevantData.push_back(current); + relevantTimes.push_back(currentTime); + } + + std::vector verts; + for(int i = 0; i < vertCount; i++) + { + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); + verts.push_back(Ogre::Vector3(x, y, z)); + } + additionalVertices.push_back(verts); + } + } }; class NiKeyframeData : public Record { - std::string bonename; - //Rotations - std::vector quats; - std::vector tbc; - std::vector rottime; - float startTime; - float stopTime; - int rtype; + std::string bonename; + //Rotations + std::vector quats; + std::vector tbc; + std::vector rottime; + float startTime; + float stopTime; + int rtype; - //Translations - std::vector translist1; - std::vector translist2; - std::vector translist3; - std::vector transtbc; - std::vector transtime; - int ttype; + //Translations + std::vector translist1; + std::vector translist2; + std::vector translist3; + std::vector transtbc; + std::vector transtime; + int ttype; - //Scalings + //Scalings + std::vector scalefactor; + std::vector scaletime; + std::vector forwards; + std::vector backwards; + std::vector tbcscale; + int stype; - std::vector scalefactor; - std::vector scaletime; - std::vector forwards; - std::vector backwards; - std::vector tbcscale; - int stype; - - - public: - void clone(NiKeyframeData c) - { - quats = c.getQuat(); - tbc = c.getrTbc(); - rottime = c.getrTime(); + void clone(const NiKeyframeData &c) + { + quats = c.getQuat(); + tbc = c.getrTbc(); + rottime = c.getrTime(); - //types - ttype = c.getTtype(); - rtype = c.getRtype(); - stype = c.getStype(); + //types + ttype = c.getTtype(); + rtype = c.getRtype(); + stype = c.getStype(); - translist1 = c.getTranslist1(); - translist2 = c.getTranslist2(); + translist1 = c.getTranslist1(); + translist2 = c.getTranslist2(); translist3 = c.getTranslist3(); - transtime = c.gettTime(); - - bonename = c.getBonename(); + transtime = c.gettTime(); + bonename = c.getBonename(); + } - } + void setBonename(std::string bone) + { bonename = bone; } + void setStartTime(float start) + { startTime = start; } + void setStopTime(float end) + { stopTime = end; } - void setBonename(std::string bone) - { - bonename = bone; - } - void setStartTime(float start) - { - startTime = start; - } - void setStopTime(float end) - { - stopTime = end; - } - void read(NIFFile *nif) - { - // Rotations first - int count = nif->getInt(); - //std::vector quat(count); - //std::vector rottime(count); - if(count) - { + void read(NIFFile *nif) + { + // Rotations first + int count = nif->getInt(); + //std::vector quat(count); + //std::vector rottime(count); + if(count) + { + //TYPE1 LINEAR_KEY + //TYPE2 QUADRATIC_KEY + //TYPE3 TBC_KEY + //TYPE4 XYZ_ROTATION_KEY + //TYPE5 UNKNOWN_KEY + rtype = nif->getInt(); + //std::cout << "Count: " << count << "Type: " << type << "\n"; - //TYPE1 LINEAR_KEY - //TYPE2 QUADRATIC_KEY - //TYPE3 TBC_KEY - //TYPE4 XYZ_ROTATION_KEY - //TYPE5 UNKNOWN_KEY - rtype = nif->getInt(); - //std::cout << "Count: " << count << "Type: " << type << "\n"; + if(rtype == 1) + { + //We need to actually read in these values instead of skipping them + //nif->skip(count*4*5); // time + quaternion + for (int i = 0; i < count; i++) + { + float time = nif->getFloat(); + float w = nif->getFloat(); + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); + Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); + quats.push_back(quat); + rottime.push_back(time); + //if(time == 0.0 || time > 355.5) + // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; + } + } + else if(rtype == 3) + { + //Example - node 116 in base_anim.nif + for (int i = 0; i < count; i++) + { + float time = nif->getFloat(); + float w = nif->getFloat(); + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); - if(rtype == 1) - { - //We need to actually read in these values instead of skipping them - //nif->skip(count*4*5); // time + quaternion - for (int i = 0; i < count; i++) { - float time = nif->getFloat(); - float w = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); - quats.push_back(quat); - rottime.push_back(time); - //if(time == 0.0 || time > 355.5) - // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; - } - } - else if(rtype == 3) - { //Example - node 116 in base_anim.nif - for (int i = 0; i < count; i++) { - float time = nif->getFloat(); - float w = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); + float tbcx = nif->getFloat(); + float tbcy = nif->getFloat(); + float tbcz = nif->getFloat(); - float tbcx = nif->getFloat(); - float tbcy = nif->getFloat(); - float tbcz = nif->getFloat(); - Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); - Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); - quats.push_back(quat); - rottime.push_back(time); - tbc.push_back(vec); - //if(time == 0.0 || time > 355.5) - // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; - } + Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); + Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); + quats.push_back(quat); + rottime.push_back(time); + tbc.push_back(vec); + //if(time == 0.0 || time > 355.5) + // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; + } + } + else if(rtype == 4) + { + for(int j=0;jgetFloat(); // time + for(int i=0; i<3; i++) + { + int cnt = nif->getInt(); + int type = nif->getInt(); + if(type == 1) + nif->skip(cnt*4*2); // time + unknown + else if(type == 2) + nif->skip(cnt*4*4); // time + unknown vector + else + nif->fail("Unknown sub-rotation type"); + } + } + } + else + nif->fail("Unknown rotation type in NiKeyframeData"); + } + //first = false; - //nif->skip(count*4*8); // rot1 + tension+bias+continuity - } - else if(rtype == 4) - { - for(int j=0;jgetFloat(); // time - for(int i=0; i<3; i++) - { - int cnt = nif->getInt(); - int type = nif->getInt(); - if(type == 1) - nif->skip(cnt*4*2); // time + unknown - else if(type == 2) - nif->skip(cnt*4*4); // time + unknown vector - else nif->fail("Unknown sub-rotation type"); - } - } - } - else nif->fail("Unknown rotation type in NiKeyframeData"); - } - //first = false; + // Then translation + count = nif->getInt(); + if(count) + { + ttype = nif->getInt(); - // Then translation - count = nif->getInt(); - - if(count) - { - ttype = nif->getInt(); + //std::cout << "TransCount:" << count << " Type: " << type << "\n"; + if(ttype == 1) + { + for(int i = 0; i < count; i++) + { + float time = nif->getFloat(); + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); - //std::cout << "TransCount:" << count << " Type: " << type << "\n"; - if(ttype == 1) { - for (int i = 0; i < count; i++) { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - translist1.push_back(trans); - transtime.push_back(time); - } - //nif->getFloatLen(count*4); // time + translation - } - else if(ttype == 2) - { //Example - node 116 in base_anim.nif - for (int i = 0; i < count; i++) { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - float x2 = nif->getFloat(); - float y2 = nif->getFloat(); - float z2 = nif->getFloat(); - float x3 = nif->getFloat(); - float y3 = nif->getFloat(); - float z3 = nif->getFloat(); - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - Ogre::Vector3 trans2 = Ogre::Vector3(x2, y2, z2); - Ogre::Vector3 trans3 = Ogre::Vector3(x3, y3, z3); - transtime.push_back(time); - translist1.push_back(trans); - translist2.push_back(trans2); - translist3.push_back(trans3); - } - - //nif->getFloatLen(count*10); // trans1 + forward + backward - } - else if(ttype == 3){ - for (int i = 0; i < count; i++) { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - float t = nif->getFloat(); - float b = nif->getFloat(); - float c = nif->getFloat(); - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - Ogre::Vector3 tbc = Ogre::Vector3(t, b, c); - translist1.push_back(trans); - transtbc.push_back(tbc); - transtime.push_back(time); - } - //nif->getFloatLen(count*7); // trans1 + tension,bias,continuity - } - else nif->fail("Unknown translation type"); - } + Ogre::Vector3 trans = Ogre::Vector3(x, y, z); + translist1.push_back(trans); + transtime.push_back(time); + } + //nif->getFloatLen(count*4); // time + translation + } + else if(ttype == 2) + { + //Example - node 116 in base_anim.nif + for(int i = 0; i < count; i++) + { + float time = nif->getFloat(); + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); + float x2 = nif->getFloat(); + float y2 = nif->getFloat(); + float z2 = nif->getFloat(); + float x3 = nif->getFloat(); + float y3 = nif->getFloat(); + float z3 = nif->getFloat(); - // Finally, scalings - count = nif->getInt(); - if(count) - { - stype = nif->getInt(); + Ogre::Vector3 trans = Ogre::Vector3(x, y, z); + Ogre::Vector3 trans2 = Ogre::Vector3(x2, y2, z2); + Ogre::Vector3 trans3 = Ogre::Vector3(x3, y3, z3); + transtime.push_back(time); + translist1.push_back(trans); + translist2.push_back(trans2); + translist3.push_back(trans3); + } - - for(int i = 0; i < count; i++){ - - - //int size = 0; - if(stype >= 1 && stype < 4) - { - float time = nif->getFloat(); - float scale = nif->getFloat(); - scaletime.push_back(time); - scalefactor.push_back(scale); - //size = 2; // time+scale - } - else nif->fail("Unknown scaling type"); - if(stype == 2){ - //size = 4; // 1 + forward + backward (floats) - float forward = nif->getFloat(); - float backward = nif->getFloat(); - forwards.push_back(forward); - backwards.push_back(backward); - } - else if(stype == 3){ - float tbcx = nif->getFloat(); - float tbcy = nif->getFloat(); - float tbcz = nif->getFloat(); - Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); - tbcscale.push_back(vec); + //nif->getFloatLen(count*10); // trans1 + forward + backward + } + else if(ttype == 3) + { + for(int i = 0; i < count; i++) + { + float time = nif->getFloat(); + float x = nif->getFloat(); + float y = nif->getFloat(); + float z = nif->getFloat(); + float t = nif->getFloat(); + float b = nif->getFloat(); + float c = nif->getFloat(); + Ogre::Vector3 trans = Ogre::Vector3(x, y, z); + Ogre::Vector3 tbc = Ogre::Vector3(t, b, c); + translist1.push_back(trans); + transtbc.push_back(tbc); + transtime.push_back(time); + } + //nif->getFloatLen(count*7); // trans1 + tension,bias,continuity + } + else nif->fail("Unknown translation type"); + } - //size = 5; // 1 + tbc - } - - } - } - else - stype = 0; - } - int getRtype(){ - return rtype; - } - int getStype(){ - return stype; - } - int getTtype(){ - return ttype; - } - float getStartTime(){ - return startTime; - } - float getStopTime(){ - return stopTime; - } - std::vector getQuat(){ - return quats; - } - std::vector getrTbc(){ - return tbc; - } - std::vector getrTime(){ - return rottime; - } + // Finally, scalings + count = nif->getInt(); + if(count) + { + stype = nif->getInt(); - std::vector getTranslist1(){ - return translist1; - } - std::vector getTranslist2(){ - return translist2; - } - std::vector getTranslist3(){ - return translist3; - } - std::vector gettTime(){ - return transtime; - } - std::vector getScalefactor(){ - return scalefactor; - } - std::vector getForwards(){ - return forwards; - } - std::vector getBackwards(){ - return backwards; - } - std::vector getScaleTbc(){ - return tbcscale; - } + for(int i = 0; i < count; i++) + { + //int size = 0; + if(stype >= 1 && stype < 4) + { + float time = nif->getFloat(); + float scale = nif->getFloat(); + scaletime.push_back(time); + scalefactor.push_back(scale); + //size = 2; // time+scale + } + else + nif->fail("Unknown scaling type"); - std::vector getsTime(){ - return scaletime; - } - std::string getBonename(){ return bonename; - } + if(stype == 2) + { + //size = 4; // 1 + forward + backward (floats) + float forward = nif->getFloat(); + float backward = nif->getFloat(); + forwards.push_back(forward); + backwards.push_back(backward); + } + else if(stype == 3) + { + //size = 5; // 1 + tbc + float tbcx = nif->getFloat(); + float tbcy = nif->getFloat(); + float tbcz = nif->getFloat(); + Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); + tbcscale.push_back(vec); + } + } + } + else + stype = 0; + } + int getRtype() const + { return rtype; } + int getStype() const + { return stype; } + int getTtype() const + { return ttype; } + float getStartTime() const + { return startTime; } + float getStopTime() const + { return stopTime; } + const std::vector& getQuat() const + { return quats; } + const std::vector& getrTbc() const + { return tbc; } + const std::vector& getrTime() const + { return rottime; } + const std::vector& getTranslist1() const + { return translist1; } + const std::vector& getTranslist2() const + { return translist2; } + const std::vector& getTranslist3() const + { return translist3; } + const std::vector& gettTime() const + { return transtime; } + const std::vector& getScalefactor() const + { return scalefactor; } + const std::vector& getForwards() const + { return forwards; } + const std::vector& getBackwards() const + { return backwards; } + const std::vector& getScaleTbc() const + { return tbcscale; } + + const std::vector& getsTime() const + { return scaletime; } + const std::string& getBonename() const + { return bonename; } }; } // Namespace diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index b0cc64228..bac412c76 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -35,57 +35,62 @@ typedef Node Effect; // NiPointLight and NiSpotLight? struct NiLight : Effect { - struct SLight - { - float dimmer; - Vector ambient; - Vector diffuse; - Vector specular; - }; + struct SLight + { + float dimmer; + Vector ambient; + Vector diffuse; + Vector specular; + }; + const SLight *light; - const SLight *light; + void read(NIFFile *nif) + { + Effect::read(nif); - void read(NIFFile *nif) - { - Effect::read(nif); - - nif->getInt(); // 1 - nif->getInt(); // 1? - light = nif->getPtr(); - } + nif->getInt(); // 1 + nif->getInt(); // 1? + light = nif->getPtr(); + } }; struct NiTextureEffect : Effect { - NiSourceTexturePtr texture; + NiSourceTexturePtr texture; - void read(NIFFile *nif) - { - Effect::read(nif); + void read(NIFFile *nif) + { + Effect::read(nif); - int tmp = nif->getInt(); - if(tmp) nif->getInt(); // always 1? + int tmp = nif->getInt(); + if(tmp) nif->getInt(); // always 1? - /* - 3 x Vector4 = [1,0,0,0] - int = 2 - int = 0 or 3 - int = 2 - int = 2 - */ - nif->skip(16*4); + /* + 3 x Vector4 = [1,0,0,0] + int = 2 + int = 0 or 3 + int = 2 + int = 2 + */ + nif->skip(16*4); - texture.read(nif); + texture.read(nif); - /* - byte = 0 - vector4 = [1,0,0,0] - short = 0 - short = -75 - short = 0 - */ - nif->skip(23); - } + /* + byte = 0 + vector4 = [1,0,0,0] + short = 0 + short = -75 + short = 0 + */ + nif->skip(23); + } + + void post(NIFFile *nif) + { + Effect::post(nif); + texture.post(nif); + } }; } // Namespace diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index eec1aa7b4..ad788661a 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -38,70 +38,70 @@ namespace Nif class Extra : public Record { public: - ExtraPtr extra; + ExtraPtr extra; - void read(NIFFile *nif) { extra.read(nif); } + void read(NIFFile *nif) { extra.read(nif); } + void post(NIFFile *nif) { extra.post(nif); } }; class NiVertWeightsExtraData : public Extra { public: - void read(NIFFile *nif) - { - Extra::read(nif); + void read(NIFFile *nif) + { + Extra::read(nif); - // We should have s*4+2 == i, for some reason. Might simply be the - // size of the rest of the record, unhelpful as that may be. - /*int i =*/ nif->getInt(); - int s = nif->getShort(); // number of vertices + // We should have s*4+2 == i, for some reason. Might simply be the + // size of the rest of the record, unhelpful as that may be. + /*int i =*/ nif->getInt(); + int s = nif->getShort(); // number of vertices - nif->getFloatLen(s); // vertex weights I guess - } + nif->getFloatLen(s); // vertex weights I guess + } }; class NiTextKeyExtraData : public Extra { public: - struct TextKey - { - float time; - Misc::SString text; - }; + struct TextKey + { + float time; + Misc::SString text; + }; + std::vector list; - std::vector list; + void read(NIFFile *nif) + { + Extra::read(nif); - void read(NIFFile *nif) - { - Extra::read(nif); + nif->getInt(); // 0 - nif->getInt(); // 0 - - int keynum = nif->getInt(); - list.resize(keynum); - for(int i=0; igetFloat(); - list[i].text = nif->getString(); - } - } + int keynum = nif->getInt(); + list.resize(keynum); + for(int i=0; igetFloat(); + list[i].text = nif->getString(); + } + } }; class NiStringExtraData : public Extra { public: - /* Two known meanings: - "MRK" - marker, only visible in the editor, not rendered in-game - "NCO" - no collision - */ - Misc::SString string; + /* Two known meanings: + "MRK" - marker, only visible in the editor, not rendered in-game + "NCO" - no collision + */ + Misc::SString string; - void read(NIFFile *nif) - { - Extra::read(nif); + void read(NIFFile *nif) + { + Extra::read(nif); - nif->getInt(); // size of string + 4. Really useful... - string = nif->getString(); - } + nif->getInt(); // size of string + 4. Really useful... + string = nif->getString(); + } }; } // Namespace diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 80ea7a0b7..48dd76510 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -190,17 +190,23 @@ void NIFFile::parse() void NiSkinInstance::post(NIFFile *nif) { - int bnum = bones.length(); - if(bnum != static_cast (data->bones.size())) - nif->fail("Mismatch in NiSkinData bone count"); + data.post(nif); + root.post(nif); + bones.post(nif); - root->makeRootBone(data->trafo); + if(data.empty() || root.empty()) + nif->fail("NiSkinInstance missing root or data"); - for(int i=0; ibones.size()) + nif->fail("Mismatch in NiSkinData bone count"); + + root->makeRootBone(data->trafo); + + for(int i=0; ifail("Oops: Missing bone! Don't know how to handle this."); - - bones[i].makeBone(i, data->bones[i]); + if(!bones.has(i)) + nif->fail("Oops: Missing bone! Don't know how to handle this."); + bones[i].makeBone(i, data->bones[i]); } } diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 951ae1f75..bb6f73259 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -43,113 +43,107 @@ namespace Nif class NIFFile { - enum NIFVersion - { - VER_MW = 0x04000002 // Morrowind NIFs + enum NIFVersion { + VER_MW = 0x04000002 // Morrowind NIFs }; - /// Nif file version - int ver; + /// Nif file version + int ver; - /// Input stream - StreamPtr inp; + /// Input stream + StreamPtr inp; - /// File name, used for error messages - std::string filename; + /// File name, used for error messages + std::string filename; - /// Record list - std::vector records; + /// Record list + std::vector records; - /// Parse the file - void parse(); + /// Parse the file + void parse(); - public: - /// Used for error handling - void fail(const std::string &msg) +public: + /// Used for error handling + void fail(const std::string &msg) { - std::string err = "NIFFile Error: " + msg; - err += "\nFile: " + filename; - throw std::runtime_error(err); + std::string err = "NIFFile Error: " + msg; + err += "\nFile: " + filename; + throw std::runtime_error(err); } - /// Open a NIF stream. The name is used for error messages. - NIFFile(StreamPtr nif, const std::string &name) - : filename(name) + /// Open a NIF stream. The name is used for error messages. + NIFFile(StreamPtr nif, const std::string &name) + : filename(name) { - /* Load the entire file into memory. This allows us to use - direct pointers to the data arrays in the NIF, instead of - individually allocating and copying each one. + /* Load the entire file into memory. This allows us to use + direct pointers to the data arrays in the NIF, instead of + individually allocating and copying each one. - The NIF data is only stored temporarily in memory, since once - the mesh data is loaded it is siphoned into OGRE and - deleted. For that reason, we might improve this further and - use a shared region/pool based allocation scheme in the - future, especially since only one NIFFile will ever be loaded - at any given time. - */ - inp = StreamPtr(new BufferStream(nif)); + The NIF data is only stored temporarily in memory, since once + the mesh data is loaded it is siphoned into OGRE and + deleted. For that reason, we might improve this further and + use a shared region/pool based allocation scheme in the + future, especially since only one NIFFile will ever be loaded + at any given time. + */ + inp = StreamPtr(new BufferStream(nif)); - parse(); + parse(); } - ~NIFFile() + ~NIFFile() { - for(std::size_t i=0; i= 0 && index < static_cast (records.size())); - Record *res = records[index]; - assert(res != NULL); - return res; - } + /// Get a given record + Record *getRecord(size_t index) + { + Record *res = records.at(index); + assert(res != NULL); + return res; + } - /// Number of records - int numRecords() { return records.size(); } - - /* ************************************************ + /// Number of records + int numRecords() { return records.size(); } + /************************************************* Parser functions + ****************************************************/ - ****************************************************/ + void skip(size_t size) { inp->getPtr(size); } - void skip(size_t size) { inp->getPtr(size); } + template const X* getPtr() { return (const X*)inp->getPtr(sizeof(X)); } + template X getType() { return *getPtr(); } + unsigned short getShort() { return getType(); } + int getInt() { return getType(); } + float getFloat() { return getType(); } + char getByte() { return getType(); } - template const X* getPtr() { return (const X*)inp->getPtr(sizeof(X)); } - template X getType() { return *getPtr(); } - unsigned short getShort() { return getType(); } - int getInt() { return getType(); } - float getFloat() { return getType(); } - char getByte() { return getType(); } - - template - Misc::SliceArray getArrayLen(int num) + template + Misc::SliceArray getArrayLen(int num) { return Misc::SliceArray((const X*)inp->getPtr(num*sizeof(X)),num); } - template - Misc::SliceArray getArray() + template + Misc::SliceArray getArray() { - int len = getInt(); - return getArrayLen(len); + int len = getInt(); + return getArrayLen(len); } - Misc::SString getString() { return getArray(); } + Misc::SString getString() { return getArray(); } - const Vector *getVector() { return getPtr(); } - const Matrix *getMatrix() { return getPtr(); } - const Transformation *getTrafo() { return getPtr(); } - const Vector4 *getVector4() { return getPtr(); } + const Vector *getVector() { return getPtr(); } + const Matrix *getMatrix() { return getPtr(); } + const Transformation *getTrafo() { return getPtr(); } + const Vector4 *getVector4() { return getPtr(); } - Misc::FloatArray getFloatLen(int num) + Misc::FloatArray getFloatLen(int num) { return getArrayLen(num); } - // For fixed-size strings where you already know the size - const char *getString(int size) + // For fixed-size strings where you already know the size + const char *getString(int size) { return (const char*)inp->getPtr(size); } }; diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 080042746..fe9d10c7a 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -26,6 +26,7 @@ #include "controlled.hpp" #include "data.hpp" +#include "property.hpp" namespace Nif { @@ -37,191 +38,220 @@ namespace Nif class Node : public Named { public: - // Node flags. Interpretation depends somewhat on the type of node. - int flags; - const Transformation *trafo; - PropertyList props; + // Node flags. Interpretation depends somewhat on the type of node. + int flags; + const Transformation *trafo; + PropertyList props; - // Bounding box info - bool hasBounds; - const Vector *boundPos; - const Matrix *boundRot; - const Vector *boundXYZ; // Box size + // Bounding box info + bool hasBounds; + const Vector *boundPos; + const Matrix *boundRot; + const Vector *boundXYZ; // Box size - void read(NIFFile *nif) - { - Named::read(nif); + void read(NIFFile *nif) + { + Named::read(nif); - flags = nif->getShort(); - trafo = nif->getTrafo(); - props.read(nif); + flags = nif->getShort(); + trafo = nif->getTrafo(); + props.read(nif); - hasBounds = !!nif->getInt(); - if(hasBounds) - { - nif->getInt(); // always 1 - boundPos = nif->getVector(); - boundRot = nif->getMatrix(); - boundXYZ = nif->getVector(); - } + hasBounds = !!nif->getInt(); + if(hasBounds) + { + nif->getInt(); // always 1 + boundPos = nif->getVector(); + boundRot = nif->getMatrix(); + boundXYZ = nif->getVector(); + } - boneTrafo = NULL; - boneIndex = -1; - } + boneTrafo = NULL; + boneIndex = -1; + } - // Bone transformation. If set, node is a part of a skeleton. - const NiSkinData::BoneTrafo *boneTrafo; + void post(NIFFile *nif) + { + Named::post(nif); + props.post(nif); + } - // Bone weight info, from NiSkinData - const NiSkinData::BoneInfo *boneInfo; + // Bone transformation. If set, node is a part of a skeleton. + const NiSkinData::BoneTrafo *boneTrafo; - // Bone index. If -1, this node is either not a bone, or if - // boneTrafo is set it is the root bone in the skeleton. - short boneIndex; + // Bone weight info, from NiSkinData + const NiSkinData::BoneInfo *boneInfo; - void makeRootBone(const NiSkinData::BoneTrafo *tr) - { - boneTrafo = tr; - boneIndex = -1; - } + // Bone index. If -1, this node is either not a bone, or if + // boneTrafo is set it is the root bone in the skeleton. + short boneIndex; - void makeBone(short ind, const NiSkinData::BoneInfo &bi) - { - boneInfo = &bi; - boneTrafo = bi.trafo; - boneIndex = ind; - } + void makeRootBone(const NiSkinData::BoneTrafo *tr) + { + boneTrafo = tr; + boneIndex = -1; + } + + void makeBone(short ind, const NiSkinData::BoneInfo &bi) + { + boneInfo = &bi; + boneTrafo = bi.trafo; + boneIndex = ind; + } }; struct NiTriShapeCopy { - std::string sname; - std::vector boneSequence; - Nif::NiSkinData::BoneTrafoCopy trafo; - //Ogre::Quaternion initialBoneRotation; - //Ogre::Vector3 initialBoneTranslation; - std::vector vertices; - std::vector normals; - std::vector boneinfo; - std::map > vertsToWeights; - Nif::NiMorphData morph; + std::string sname; + std::vector boneSequence; + Nif::NiSkinData::BoneTrafoCopy trafo; + //Ogre::Quaternion initialBoneRotation; + //Ogre::Vector3 initialBoneTranslation; + std::vector vertices; + std::vector normals; + std::vector boneinfo; + std::map > vertsToWeights; + Nif::NiMorphData morph; }; struct NiNode : Node { - NodeList children; - NodeList effects; + NodeList children; + NodeList effects; - /* Known NiNode flags: + /* Known NiNode flags: + 0x01 hidden + 0x02 use mesh for collision + 0x04 use bounding box for collision (?) + 0x08 unknown, but common + 0x20, 0x40, 0x80 unknown + */ - 0x01 hidden - 0x02 use mesh for collision - 0x04 use bounding box for collision (?) - 0x08 unknown, but common - 0x20, 0x40, 0x80 unknown - */ + void read(NIFFile *nif) + { + Node::read(nif); + children.read(nif); + effects.read(nif); + } - void read(NIFFile *nif) - { - Node::read(nif); - children.read(nif); - effects.read(nif); - } + void post(NIFFile *nif) + { + Node::post(nif); + children.post(nif); + effects.post(nif); + } }; struct NiTriShape : Node { - /* Possible flags: - 0x40 - mesh has no vertex normals ? + /* Possible flags: + 0x40 - mesh has no vertex normals ? - Only flags included in 0x47 (ie. 0x01, 0x02, 0x04 and 0x40) have - been observed so far. - */ + Only flags included in 0x47 (ie. 0x01, 0x02, 0x04 and 0x40) have + been observed so far. + */ - NiTriShapeDataPtr data; - NiSkinInstancePtr skin; + NiTriShapeDataPtr data; + NiSkinInstancePtr skin; - void read(NIFFile *nif) - { - Node::read(nif); - data.read(nif); - skin.read(nif); - } + void read(NIFFile *nif) + { + Node::read(nif); + data.read(nif); + skin.read(nif); + } - NiTriShapeCopy clone(){ - NiTriShapeCopy copy; - copy.sname = name.toString(); - float *ptr = (float*)data->vertices.ptr; - float *ptrNormals = (float*)data->normals.ptr; - int numVerts = data->vertices.length / 3; - for(int i = 0; i < numVerts; i++) - { - float *current = (float*) (ptr + i * 3); - copy.vertices.push_back(Ogre::Vector3(*current, *(current + 1), *(current + 2))); + void post(NIFFile *nif) + { + Node::post(nif); + data.post(nif); + skin.post(nif); + } - if(ptrNormals){ - float *currentNormals = (float*) (ptrNormals + i * 3); - copy.normals.push_back(Ogre::Vector3(*currentNormals, *(currentNormals + 1), *(currentNormals + 2))); - } - } + NiTriShapeCopy clone() + { + NiTriShapeCopy copy; + copy.sname = name.toString(); + float *ptr = (float*)data->vertices.ptr; + float *ptrNormals = (float*)data->normals.ptr; + int numVerts = data->vertices.length / 3; + for(int i = 0; i < numVerts; i++) + { + float *current = (float*) (ptr + i * 3); + copy.vertices.push_back(Ogre::Vector3(*current, *(current + 1), *(current + 2))); + if(ptrNormals) + { + float *currentNormals = (float*) (ptrNormals + i * 3); + copy.normals.push_back(Ogre::Vector3(*currentNormals, *(currentNormals + 1), *(currentNormals + 2))); + } + } - return copy; - } + return copy; + } }; struct NiCamera : Node { - struct Camera - { - // Camera frustrum - float left, right, top, bottom, nearDist, farDist; + struct Camera + { + // Camera frustrum + float left, right, top, bottom, nearDist, farDist; - // Viewport - float vleft, vright, vtop, vbottom; + // Viewport + float vleft, vright, vtop, vbottom; - // Level of detail modifier - float LOD; - }; + // Level of detail modifier + float LOD; + }; + const Camera *cam; - const Camera *cam; + void read(NIFFile *nif) + { + Node::read(nif); - void read(NIFFile *nif) - { - Node::read(nif); + nif->getPtr(); - nif->getPtr(); - - nif->getInt(); // -1 - nif->getInt(); // 0 - } + nif->getInt(); // -1 + nif->getInt(); // 0 + } }; struct NiAutoNormalParticles : Node { - NiAutoNormalParticlesDataPtr data; + NiAutoNormalParticlesDataPtr data; - void read(NIFFile *nif) - { - Node::read(nif); - data.read(nif); - nif->getInt(); // -1 - } + void read(NIFFile *nif) + { + Node::read(nif); + data.read(nif); + nif->getInt(); // -1 + } + + void post(NIFFile *nif) + { + Node::post(nif); + data.post(nif); + } }; struct NiRotatingParticles : Node { - NiRotatingParticlesDataPtr data; + NiRotatingParticlesDataPtr data; - void read(NIFFile *nif) - { - Node::read(nif); - data.read(nif); - nif->getInt(); // -1 - } + void read(NIFFile *nif) + { + Node::read(nif); + data.read(nif); + nif->getInt(); // -1 + } + + void post(NIFFile *nif) + { + Node::post(nif); + data.post(nif); + } }; - - } // Namespace #endif diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 1a16854af..619e3db0e 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -32,104 +32,116 @@ namespace Nif class Property : public Named { public: - // The meaning of these depends on the actual property type. - int flags; + // The meaning of these depends on the actual property type. + int flags; - void read(NIFFile *nif) - { - Named::read(nif); - flags = nif->getShort(); - } + void read(NIFFile *nif) + { + Named::read(nif); + flags = nif->getShort(); + } }; class NiTexturingProperty : public Property { public: - // A sub-texture - struct Texture - { - /* Clamp mode - 0 - clampS clampT - 1 - clampS wrapT - 2 - wrapS clampT - 3 - wrapS wrapT - */ + // A sub-texture + struct Texture + { + /* Clamp mode + 0 - clampS clampT + 1 - clampS wrapT + 2 - wrapS clampT + 3 - wrapS wrapT + */ - /* Filter: - 0 - nearest - 1 - bilinear - 2 - trilinear - 3, 4, 5 - who knows - */ - bool inUse; - NiSourceTexturePtr texture; + /* Filter: + 0 - nearest + 1 - bilinear + 2 - trilinear + 3, 4, 5 - who knows + */ + bool inUse; + NiSourceTexturePtr texture; - int clamp, set, filter; - short unknown2; + int clamp, set, filter; + short unknown2; + + void read(NIFFile *nif) + { + inUse = !!nif->getInt(); + if(!inUse) return; + + texture.read(nif); + clamp = nif->getInt(); + filter = nif->getInt(); + set = nif->getInt(); + + // I have no idea, but I think these are actually two + // PS2-specific shorts (ps2L and ps2K), followed by an unknown + // short. + nif->skip(6); + } + + void post(NIFFile *nif) + { + texture.post(nif); + } + }; + + /* Apply mode: + 0 - replace + 1 - decal + 2 - modulate + 3 - hilight // These two are for PS2 only? + 4 - hilight2 + */ + int apply; + + /* + * The textures in this list are as follows: + * + * 0 - Base texture + * 1 - Dark texture + * 2 - Detail texture + * 3 - Gloss texture (never used?) + * 4 - Glow texture + * 5 - Bump map texture + * 6 - Decal texture + */ + Texture textures[7]; void read(NIFFile *nif) { - inUse = !!nif->getInt(); - if(!inUse) return; + Property::read(nif); + apply = nif->getInt(); - texture.read(nif); - clamp = nif->getInt(); - filter = nif->getInt(); - set = nif->getInt(); + // Unknown, always 7. Probably the number of textures to read + // below + nif->getInt(); - // I have no idea, but I think these are actually two - // PS2-specific shorts (ps2L and ps2K), followed by an unknown - // short. - nif->skip(6); + textures[0].read(nif); // Base + textures[1].read(nif); // Dark + textures[2].read(nif); // Detail + textures[3].read(nif); // Gloss (never present) + textures[4].read(nif); // Glow + textures[5].read(nif); // Bump map + if(textures[5].inUse) + { + // Ignore these at the moment + /*float lumaScale =*/ nif->getFloat(); + /*float lumaOffset =*/ nif->getFloat(); + /*const Vector4 *lumaMatrix =*/ nif->getVector4(); + } + textures[6].read(nif); // Decal } - }; - /* Apply mode: - 0 - replace - 1 - decal - 2 - modulate - 3 - hilight // These two are for PS2 only? - 4 - hilight2 - */ - int apply; - - /* - * The textures in this list are as follows: - * - * 0 - Base texture - * 1 - Dark texture - * 2 - Detail texture - * 3 - Gloss texture (never used?) - * 4 - Glow texture - * 5 - Bump map texture - * 6 - Decal texture - */ - Texture textures[7]; - - void read(NIFFile *nif) - { - Property::read(nif); - apply = nif->getInt(); - - // Unknown, always 7. Probably the number of textures to read - // below - nif->getInt(); - - textures[0].read(nif); // Base - textures[1].read(nif); // Dark - textures[2].read(nif); // Detail - textures[3].read(nif); // Gloss (never present) - textures[4].read(nif); // Glow - textures[5].read(nif); // Bump map - if(textures[5].inUse) - { - // Ignore these at the moment - /*float lumaScale =*/ nif->getFloat(); - /*float lumaOffset =*/ nif->getFloat(); - /*const Vector4 *lumaMatrix =*/ nif->getVector4(); - } - textures[6].read(nif); // Decal - } + void post(NIFFile *nif) + { + Property::post(nif); + for(int i = 0;i < 7;i++) + textures[i].post(nif); + } }; // These contain no other data than the 'flags' field in Property @@ -140,88 +152,88 @@ typedef Property NiSpecularProperty; typedef Property NiWireframeProperty; // The rest are all struct-based -template +template struct StructPropT : Property { - const Struct* data; + const T* data; - void read(NIFFile *nif) - { - Property::read(nif); - data = nif->getPtr(); - } + void read(NIFFile *nif) + { + Property::read(nif); + data = nif->getPtr(); + } }; struct S_MaterialProperty { - // The vector components are R,G,B - Vector ambient, diffuse, specular, emissive; - float glossiness, alpha; + // The vector components are R,G,B + Vector ambient, diffuse, specular, emissive; + float glossiness, alpha; }; struct S_VertexColorProperty { - /* Vertex mode: - 0 - source ignore - 1 - source emmisive - 2 - source amb diff + /* Vertex mode: + 0 - source ignore + 1 - source emmisive + 2 - source amb diff - Lighting mode - 0 - lighting emmisive - 1 - lighting emmisive ambient/diffuse - */ - int vertmode, lightmode; + Lighting mode + 0 - lighting emmisive + 1 - lighting emmisive ambient/diffuse + */ + int vertmode, lightmode; }; struct S_AlphaProperty { - /* - In NiAlphaProperty, the flags have the following meaning: + /* + In NiAlphaProperty, the flags have the following meaning: - Bit 0 : alpha blending enable - Bits 1-4 : source blend mode - Bits 5-8 : destination blend mode - Bit 9 : alpha test enable - Bit 10-12 : alpha test mode - Bit 13 : no sorter flag ( disables triangle sorting ) + Bit 0 : alpha blending enable + Bits 1-4 : source blend mode + Bits 5-8 : destination blend mode + Bit 9 : alpha test enable + Bit 10-12 : alpha test mode + Bit 13 : no sorter flag ( disables triangle sorting ) - blend modes (glBlendFunc): - 0000 GL_ONE - 0001 GL_ZERO - 0010 GL_SRC_COLOR - 0011 GL_ONE_MINUS_SRC_COLOR - 0100 GL_DST_COLOR - 0101 GL_ONE_MINUS_DST_COLOR - 0110 GL_SRC_ALPHA - 0111 GL_ONE_MINUS_SRC_ALPHA - 1000 GL_DST_ALPHA - 1001 GL_ONE_MINUS_DST_ALPHA - 1010 GL_SRC_ALPHA_SATURATE + blend modes (glBlendFunc): + 0000 GL_ONE + 0001 GL_ZERO + 0010 GL_SRC_COLOR + 0011 GL_ONE_MINUS_SRC_COLOR + 0100 GL_DST_COLOR + 0101 GL_ONE_MINUS_DST_COLOR + 0110 GL_SRC_ALPHA + 0111 GL_ONE_MINUS_SRC_ALPHA + 1000 GL_DST_ALPHA + 1001 GL_ONE_MINUS_DST_ALPHA + 1010 GL_SRC_ALPHA_SATURATE - test modes (glAlphaFunc): - 000 GL_ALWAYS - 001 GL_LESS - 010 GL_EQUAL - 011 GL_LEQUAL - 100 GL_GREATER - 101 GL_NOTEQUAL - 110 GL_GEQUAL - 111 GL_NEVER + test modes (glAlphaFunc): + 000 GL_ALWAYS + 001 GL_LESS + 010 GL_EQUAL + 011 GL_LEQUAL + 100 GL_GREATER + 101 GL_NOTEQUAL + 110 GL_GEQUAL + 111 GL_NEVER - Taken from: - http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html + Taken from: + http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html - Right now we only use standard alpha blending (see the Ogre code - that sets it up) and it appears that this is the only blending - used in the original game. Bloodmoon (along with several mods) do - however use other settings, such as discarding pixel values with - alpha < 1.0. This is faster because we don't have to mess with the - depth stuff like we did for blending. And OGRE has settings for - this too. - */ + Right now we only use standard alpha blending (see the Ogre code + that sets it up) and it appears that this is the only blending + used in the original game. Bloodmoon (along with several mods) do + however use other settings, such as discarding pixel values with + alpha < 1.0. This is faster because we don't have to mess with the + depth stuff like we did for blending. And OGRE has settings for + this too. + */ - // Tested against when certain flags are set (see above.) - unsigned char threshold; + // Tested against when certain flags are set (see above.) + unsigned char threshold; }; typedef StructPropT NiAlphaProperty; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 40f91f84f..06fdce55e 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -88,26 +88,25 @@ enum RecordType /// Base class for all records struct Record { - // Record type and type name - int recType; - Misc::SString recName; + // Record type and type name + int recType; + Misc::SString recName; - Record() : recType(RC_MISSING) {} + Record() : recType(RC_MISSING) {} - /// Parses the record from file - virtual void read(NIFFile *nif) = 0; + /// Parses the record from file + virtual void read(NIFFile *nif) = 0; - /// Does post-processing, after the entire tree is loaded - virtual void post(NIFFile *nif) {} + /// Does post-processing, after the entire tree is loaded + virtual void post(NIFFile *nif) {} virtual ~Record() {} - /* - Use these later if you want custom allocation of all NIF objects - - static void* operator new(size_t size); - static void operator delete(void *p); - */ + /* + Use these later if you want custom allocation of all NIF objects + static void* operator new(size_t size); + static void operator delete(void *p); + */ }; } // Namespace diff --git a/components/nif/record_ptr.hpp b/components/nif/record_ptr.hpp index c5618941e..ac91e25e3 100644 --- a/components/nif/record_ptr.hpp +++ b/components/nif/record_ptr.hpp @@ -37,61 +37,51 @@ namespace Nif template class RecordPtrT { - int index; - X* ptr; - NIFFile *nif; + union { + intptr_t index; + X* ptr; + }; - public: +public: + RecordPtrT() : index(-2) {} - RecordPtrT() : index(-2), ptr(NULL) {} + /// Read the index from the nif + void read(NIFFile *nif) + { + // Can only read the index once + assert(index == -2); - /// Read the index from the nif - void read(NIFFile *_nif) - { - // Can only read the index once - assert(index == -2); + // Store the index for later + index = nif->getInt(); + } - // Store the NIFFile pointer for later - nif = _nif; + /// Resolve index to pointer + void post(NIFFile *nif) + { + if(index < 0) + ptr = NULL; + else + { + Record *r = nif->getRecord(index); + // And cast it + ptr = dynamic_cast(r); + assert(ptr != NULL); + } + } - // And the index, of course - index = nif->getInt(); - } - - /** Set the pointer explicitly. May be used when you are pointing to - records in another file, eg. when you have a .nif / .kf pair. - */ - void set(X *p) - { - ptr = p; - index = -1; - } - - /// Look up the actual object from the index - X* getPtr() - { - // Have we found the pointer already? - if(ptr == NULL) - { - // Get the record - assert(index >= 0); - Record *r = nif->getRecord(index); - - // And cast it - ptr = dynamic_cast(r); + /// Look up the actual object from the index + X* getPtr() + { assert(ptr != NULL); - } - return ptr; - } + return ptr; + } + X& get() { return *getPtr(); } - /// Syntactic sugar - X* operator->() { return getPtr(); } - X& get() { return *getPtr(); } + /// Syntactic sugar + X* operator->() { return getPtr(); } - /// Pointers are allowed to be empty - bool empty() { return index == -1 && ptr == NULL; } - - int getIndex() { return index; } + /// Pointers are allowed to be empty + bool empty() { return ptr == NULL; } }; /** A list of references to other records. These are read as a list, @@ -101,40 +91,38 @@ class RecordPtrT template class RecordListT { - typedef RecordPtrT Ptr; - std::vector list; + typedef RecordPtrT Ptr; + std::vector list; - public: - - void read(NIFFile *nif) - { - int len = nif->getInt(); - list.resize(len); - - assert(len >= 0 && len < 1000); - for(int i=0;i= 0 && index < static_cast (list.size())); - return list[index].get(); + int len = nif->getInt(); + list.resize(len); + + for(size_t i=0;i < list.size();i++) + list[i].read(nif); } - bool has(int index) - { - assert(index >= 0 && index < static_cast (list.size())); - return !list[index].empty(); - } - - int getIndex(int index) + void post(NIFFile *nif) { - if(has(index)) return list[index].getIndex(); - else return -1; + for(size_t i=0;i < list.size();i++) + list[i].post(nif); } - int length() { return list.size(); } + X& operator[](size_t index) + { + return list.at(index).get(); + } + + bool has(size_t index) + { + assert(index >= 0 && index < static_cast (list.size())); + return !list.at(index).empty(); + } + + int length() + { return list.size(); } }; From 291599c6098c446d4cf00b9f18342d040f5b59ab Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 2 Jul 2012 22:49:44 -0700 Subject: [PATCH 036/688] Store the parents of NIF's nodes --- components/nif/node.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/components/nif/node.hpp b/components/nif/node.hpp index fe9d10c7a..e4cc9291e 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -31,6 +31,8 @@ namespace Nif { +class NiNode; + /** A Node is an object that's part of the main NIF tree. It has parent node (unless it's the root), and transformation (location and rotation) relative to it's parent. @@ -66,6 +68,8 @@ public: boundXYZ = nif->getVector(); } + parent = NULL; + boneTrafo = NULL; boneIndex = -1; } @@ -76,6 +80,10 @@ public: props.post(nif); } + // Parent node, or NULL for the root node. As far as I'm aware, only + // NiNodes (or types derived from NiNodes) can be parents. + NiNode *parent; + // Bone transformation. If set, node is a part of a skeleton. const NiSkinData::BoneTrafo *boneTrafo; @@ -139,6 +147,9 @@ struct NiNode : Node Node::post(nif); children.post(nif); effects.post(nif); + + for(size_t i = 0;i < children.length();i++) + children[i].parent = this; } }; From d27863bf8eb6b46b130157ffd801e551a7348c67 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Jul 2012 11:34:20 +0200 Subject: [PATCH 037/688] rename layout files to .layout to get them recognized by layout editor --- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/birth.cpp | 2 +- apps/openmw/mwgui/birth.hpp | 2 +- apps/openmw/mwgui/bookwindow.cpp | 2 +- apps/openmw/mwgui/class.cpp | 16 ++--- apps/openmw/mwgui/class.hpp | 2 +- apps/openmw/mwgui/confirmationdialog.cpp | 2 +- apps/openmw/mwgui/console.cpp | 2 +- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/countdialog.cpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 2 +- apps/openmw/mwgui/dialogue.hpp | 2 +- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 2 +- apps/openmw/mwgui/mainmenu.hpp | 2 +- apps/openmw/mwgui/map_window.cpp | 2 +- apps/openmw/mwgui/messagebox.cpp | 6 +- apps/openmw/mwgui/race.cpp | 2 +- apps/openmw/mwgui/race.hpp | 2 +- apps/openmw/mwgui/review.cpp | 2 +- apps/openmw/mwgui/review.hpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 2 +- apps/openmw/mwgui/spellwindow.cpp | 2 +- apps/openmw/mwgui/stats_window.cpp | 2 +- apps/openmw/mwgui/text_input.cpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 4 +- apps/openmw/mwgui/tradewindow.cpp | 2 +- files/mygui/CMakeLists.txt | 64 +++++++++---------- ...ayout.xml => openmw_alchemy_window.layout} | 0 ...nmw_book_layout.xml => openmw_book.layout} | 0 ...layout.xml => openmw_chargen_birth.layout} | 0 ...layout.xml => openmw_chargen_class.layout} | 0 ...> openmw_chargen_class_description.layout} | 0 ...xml => openmw_chargen_create_class.layout} | 0 ...enmw_chargen_generate_class_result.layout} | 0 ..._layout.xml => openmw_chargen_race.layout} | 0 ...ayout.xml => openmw_chargen_review.layout} | 0 ...=> openmw_chargen_select_attribute.layout} | 0 ...xml => openmw_chargen_select_skill.layout} | 0 ...enmw_chargen_select_specialization.layout} | 0 ....xml => openmw_confirmation_dialog.layout} | 0 ...nsole_layout.xml => openmw_console.layout} | 0 ...out.xml => openmw_container_window.layout} | 0 ..._layout.xml => openmw_count_window.layout} | 0 ...yout.xml => openmw_dialogue_window.layout} | 0 ...penmw_hud_layout.xml => openmw_hud.layout} | 0 ...fobox_layout.xml => openmw_infobox.layout} | 0 ...l => openmw_interactive_messagebox.layout} | 0 ...out.xml => openmw_inventory_window.layout} | 0 ...urnal_layout.xml => openmw_journal.layout} | 0 ...menu_layout.xml => openmw_mainmenu.layout} | 0 ...ow_layout.xml => openmw_map_window.layout} | 0 ...ox_layout.xml => openmw_messagebox.layout} | 0 ...scroll_layout.xml => openmw_scroll.layout} | 0 ...yout.xml => openmw_settings_window.layout} | 0 ..._layout.xml => openmw_spell_window.layout} | 0 ..._layout.xml => openmw_stats_window.layout} | 0 ...ut_layout.xml => openmw_text_input.layout} | 0 ...mw_tooltips.xml => openmw_tooltips.layout} | 0 ..._layout.xml => openmw_trade_window.layout} | 0 62 files changed, 70 insertions(+), 72 deletions(-) rename files/mygui/{openmw_alchemy_window_layout.xml => openmw_alchemy_window.layout} (100%) rename files/mygui/{openmw_book_layout.xml => openmw_book.layout} (100%) rename files/mygui/{openmw_chargen_birth_layout.xml => openmw_chargen_birth.layout} (100%) rename files/mygui/{openmw_chargen_class_layout.xml => openmw_chargen_class.layout} (100%) rename files/mygui/{openmw_chargen_class_description_layout.xml => openmw_chargen_class_description.layout} (100%) rename files/mygui/{openmw_chargen_create_class_layout.xml => openmw_chargen_create_class.layout} (100%) rename files/mygui/{openmw_chargen_generate_class_result_layout.xml => openmw_chargen_generate_class_result.layout} (100%) rename files/mygui/{openmw_chargen_race_layout.xml => openmw_chargen_race.layout} (100%) rename files/mygui/{openmw_chargen_review_layout.xml => openmw_chargen_review.layout} (100%) rename files/mygui/{openmw_chargen_select_attribute_layout.xml => openmw_chargen_select_attribute.layout} (100%) rename files/mygui/{openmw_chargen_select_skill_layout.xml => openmw_chargen_select_skill.layout} (100%) rename files/mygui/{openmw_chargen_select_specialization_layout.xml => openmw_chargen_select_specialization.layout} (100%) rename files/mygui/{openmw_confirmation_dialog_layout.xml => openmw_confirmation_dialog.layout} (100%) rename files/mygui/{openmw_console_layout.xml => openmw_console.layout} (100%) rename files/mygui/{openmw_container_window_layout.xml => openmw_container_window.layout} (100%) rename files/mygui/{openmw_count_window_layout.xml => openmw_count_window.layout} (100%) rename files/mygui/{openmw_dialogue_window_layout.xml => openmw_dialogue_window.layout} (100%) rename files/mygui/{openmw_hud_layout.xml => openmw_hud.layout} (100%) rename files/mygui/{openmw_infobox_layout.xml => openmw_infobox.layout} (100%) rename files/mygui/{openmw_interactive_messagebox_layout.xml => openmw_interactive_messagebox.layout} (100%) rename files/mygui/{openmw_inventory_window_layout.xml => openmw_inventory_window.layout} (100%) rename files/mygui/{openmw_journal_layout.xml => openmw_journal.layout} (100%) rename files/mygui/{openmw_mainmenu_layout.xml => openmw_mainmenu.layout} (100%) rename files/mygui/{openmw_map_window_layout.xml => openmw_map_window.layout} (100%) rename files/mygui/{openmw_messagebox_layout.xml => openmw_messagebox.layout} (100%) rename files/mygui/{openmw_scroll_layout.xml => openmw_scroll.layout} (100%) rename files/mygui/{openmw_settings_window_layout.xml => openmw_settings_window.layout} (100%) rename files/mygui/{openmw_spell_window_layout.xml => openmw_spell_window.layout} (100%) rename files/mygui/{openmw_stats_window_layout.xml => openmw_stats_window.layout} (100%) rename files/mygui/{openmw_text_input_layout.xml => openmw_text_input.layout} (100%) rename files/mygui/{openmw_tooltips.xml => openmw_tooltips.layout} (100%) rename files/mygui/{openmw_trade_window_layout.xml => openmw_trade_window.layout} (100%) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 423678eed..7b24b07de 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -27,7 +27,7 @@ namespace namespace MWGui { AlchemyWindow::AlchemyWindow(WindowManager& parWindowManager) - : WindowBase("openmw_alchemy_window_layout.xml", parWindowManager) + : WindowBase("openmw_alchemy_window.layout", parWindowManager) , ContainerBase(0) { getWidget(mCreateButton, "CreateButton"); diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 49f90d1a0..69056759d 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -10,7 +10,7 @@ using namespace MWGui; using namespace Widgets; BirthDialog::BirthDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_birth_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_birth.layout", parWindowManager) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index b5f7db774..e61be736a 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -5,7 +5,7 @@ /* This file contains the dialog for choosing a birth sign. - Layout is defined by resources/mygui/openmw_chargen_race_layout.xml. + Layout is defined by resources/mygui/openmw_chargen_race.layout. */ namespace MWGui diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index a9dcd4555..81f5cf9bd 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -13,7 +13,7 @@ using namespace MWGui; BookWindow::BookWindow (WindowManager& parWindowManager) : - WindowBase("openmw_book_layout.xml", parWindowManager) + WindowBase("openmw_book.layout", parWindowManager) { getWidget(mCloseButton, "CloseButton"); mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 0961f6327..d0f21f945 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -19,7 +19,7 @@ using namespace MWGui; /* GenerateClassResultDialog */ GenerateClassResultDialog::GenerateClassResultDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_generate_class_result_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager) { // Centre dialog center(); @@ -77,7 +77,7 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) /* PickClassDialog */ PickClassDialog::PickClassDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_class_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_class.layout", parWindowManager) { // Centre dialog center(); @@ -283,7 +283,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin) } InfoBoxDialog::InfoBoxDialog(WindowManager& parWindowManager) - : WindowBase("openmw_infobox_layout.xml", parWindowManager) + : WindowBase("openmw_infobox.layout", parWindowManager) , currentButton(-1) { getWidget(textBox, "TextBox"); @@ -381,7 +381,7 @@ ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) /* CreateClassDialog */ CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_create_class_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_create_class.layout", parWindowManager) , specDialog(nullptr) , attribDialog(nullptr) , skillDialog(nullptr) @@ -703,7 +703,7 @@ void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) /* SelectSpecializationDialog */ SelectSpecializationDialog::SelectSpecializationDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_specialization_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_select_specialization.layout", parWindowManager) { // Centre dialog center(); @@ -768,7 +768,7 @@ void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectAttributeDialog */ SelectAttributeDialog::SelectAttributeDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_attribute_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_select_attribute.layout", parWindowManager) { // Centre dialog center(); @@ -820,7 +820,7 @@ void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectSkillDialog */ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_skill_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_select_skill.layout", parWindowManager) { // Centre dialog center(); @@ -916,7 +916,7 @@ void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) /* DescriptionDialog */ DescriptionDialog::DescriptionDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_class_description_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_class_description.layout", parWindowManager) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 868052800..c9e7aef7b 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -7,7 +7,7 @@ /* This file contains the dialogs for choosing a class. - Layout is defined by resources/mygui/openmw_chargen_class_layout.xml. + Layout is defined by resources/mygui/openmw_chargen_class.layout. */ namespace MWGui diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 5e12c3296..4b41514a0 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -8,7 +8,7 @@ namespace MWGui { ConfirmationDialog::ConfirmationDialog(WindowManager& parWindowManager) : - WindowBase("openmw_confirmation_dialog_layout.xml", parWindowManager) + WindowBase("openmw_confirmation_dialog.layout", parWindowManager) { getWidget(mMessage, "Message"); getWidget(mOkButton, "OkButton"); diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 8115835f4..113b3dee9 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -103,7 +103,7 @@ namespace MWGui } Console::Console(int w, int h, const Compiler::Extensions& extensions) - : Layout("openmw_console_layout.xml"), + : Layout("openmw_console.layout"), mCompilerContext (MWScript::CompilerContext::Type_Console) { setCoord(10,10, w-10, h/2); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 36ed16b0e..54a1bb73b 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -592,7 +592,7 @@ MWWorld::ContainerStore& ContainerBase::getContainerStore() ContainerWindow::ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) - , WindowBase("openmw_container_window_layout.xml", parWindowManager) + , WindowBase("openmw_container_window.layout", parWindowManager) { getWidget(mTakeButton, "TakeButton"); getWidget(mCloseButton, "CloseButton"); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index e0a9bb908..8a0d40d3e 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -8,7 +8,7 @@ namespace MWGui { CountDialog::CountDialog(WindowManager& parWindowManager) : - WindowBase("openmw_count_window_layout.xml", parWindowManager) + WindowBase("openmw_count_window.layout", parWindowManager) { getWidget(mSlider, "CountSlider"); getWidget(mItemEdit, "ItemEdit"); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 55bd49beb..acf0bf130 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -43,7 +43,7 @@ std::string::size_type find_str_ci(const std::string& str, const std::string& su DialogueWindow::DialogueWindow(WindowManager& parWindowManager) - : WindowBase("openmw_dialogue_window_layout.xml", parWindowManager) + : WindowBase("openmw_dialogue_window.layout", parWindowManager) , mEnabled(true) , mShowTrade(false) { diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index aa66cd579..5d808b5a7 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -19,7 +19,7 @@ namespace MWGui /* This file contains the dialouge window - Layout is defined by resources/mygui/openmw_dialogue_window_layout.xml. + Layout is defined by resources/mygui/openmw_dialogue_window.layout. */ namespace MWGui diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 4ad9845f0..c5ea2949b 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -20,7 +20,7 @@ using namespace MWGui; HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) - : Layout("openmw_hud_layout.xml") + : Layout("openmw_hud.layout") , health(NULL) , magicka(NULL) , stamina(NULL) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index a26a958bd..c981e2b90 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -42,7 +42,7 @@ namespace MWGui InventoryWindow::InventoryWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) - , WindowPinnableBase("openmw_inventory_window_layout.xml", parWindowManager) + , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) , mTrading(false) { static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 7f9000bc7..b5468ae44 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -80,7 +80,7 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) - : WindowBase("openmw_journal_layout.xml", parWindowManager) + : WindowBase("openmw_journal.layout", parWindowManager) , lastPos(0) , mVisible(false) { diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index b32f2d900..06c59396f 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -7,7 +7,7 @@ namespace MWGui { public: MainMenu(int w, int h) - : Layout("openmw_mainmenu_layout.xml") + : Layout("openmw_mainmenu.layout") { setCoord(0,0,w,h); } diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index a51b66e2b..4ebeb3874 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -155,7 +155,7 @@ void LocalMapBase::setPlayerDir(const float x, const float y) // ------------------------------------------------------------------------------------------ MapWindow::MapWindow(WindowManager& parWindowManager) : - MWGui::WindowPinnableBase("openmw_map_window_layout.xml", parWindowManager), + MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager), mGlobal(false) { setCoord(500,0,320,300); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 007f966aa..68326a3c3 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -147,7 +147,7 @@ int MessageBoxManager::readPressedButton () MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) - : Layout("openmw_messagebox_layout.xml") + : Layout("openmw_messagebox.layout") , mMessageBoxManager(parMessageBoxManager) , cMessage(message) { @@ -177,7 +177,7 @@ MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::strin size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box mMainWidget->setSize(size); - size.width -= 15; // this is to center the text (see messagebox_layout.xml, Widget type="Edit" position="-2 -3 0 0") + size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0") mMessageWidget->setSize(size); } @@ -205,7 +205,7 @@ int MessageBox::getHeight () InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons) - : Layout("openmw_interactive_messagebox_layout.xml") + : Layout("openmw_interactive_messagebox.layout") , mMessageBoxManager(parMessageBoxManager) , mButtonPressed(-1) { diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index dea365ac2..9ae453016 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -17,7 +17,7 @@ using namespace MWGui; using namespace Widgets; RaceDialog::RaceDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_race_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_race.layout", parWindowManager) , genderIndex(0) , faceIndex(0) , hairIndex(0) diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index bcd3b5185..217ce15aa 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -14,7 +14,7 @@ namespace MWGui /* This file contains the dialog for choosing a race. - Layout is defined by resources/mygui/openmw_chargen_race_layout.xml. + Layout is defined by resources/mygui/openmw_chargen_race.layout. */ namespace MWGui diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 97a3a6e15..f9792ea34 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -20,7 +20,7 @@ using namespace Widgets; const int ReviewDialog::lineHeight = 18; ReviewDialog::ReviewDialog(WindowManager& parWindowManager) - : WindowBase("openmw_chargen_review_layout.xml", parWindowManager) + : WindowBase("openmw_chargen_review.layout", parWindowManager) , lastPos(0) { // Centre dialog diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 6118168d5..454a6c5a5 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -12,7 +12,7 @@ namespace MWGui /* This file contains the dialog for reviewing the generated character. -Layout is defined by resources/mygui/openmw_chargen_review_layout.xml. +Layout is defined by resources/mygui/openmw_chargen_review.layout. */ namespace MWGui diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 92b18d953..464407216 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -11,7 +11,7 @@ using namespace MWGui; ScrollWindow::ScrollWindow (WindowManager& parWindowManager) : - WindowBase("openmw_scroll_layout.xml", parWindowManager) + WindowBase("openmw_scroll.layout", parWindowManager) { getWidget(mTextView, "TextView"); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 6ceed664e..818af9942 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -75,7 +75,7 @@ namespace namespace MWGui { SettingsWindow::SettingsWindow(WindowManager& parWindowManager) : - WindowBase("openmw_settings_window_layout.xml", parWindowManager) + WindowBase("openmw_settings_window.layout", parWindowManager) { getWidget(mOkButton, "OkButton"); getWidget(mResolutionList, "ResolutionList"); diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 909798f1a..ef155f563 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -39,7 +39,7 @@ namespace namespace MWGui { SpellWindow::SpellWindow(WindowManager& parWindowManager) - : WindowPinnableBase("openmw_spell_window_layout.xml", parWindowManager) + : WindowPinnableBase("openmw_spell_window.layout", parWindowManager) , mHeight(0) , mWidth(0) { diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 3584af7c7..0330c9093 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -19,7 +19,7 @@ using namespace MWGui; const int StatsWindow::lineHeight = 18; StatsWindow::StatsWindow (WindowManager& parWindowManager) - : WindowPinnableBase("openmw_stats_window_layout.xml", parWindowManager) + : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) , skillAreaWidget(NULL) , skillClientWidget(NULL) , skillScrollerWidget(NULL) diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 7d84a9b9f..17852ba5a 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -4,7 +4,7 @@ using namespace MWGui; TextInputDialog::TextInputDialog(WindowManager& parWindowManager) - : WindowBase("openmw_text_input_layout.xml", parWindowManager) + : WindowBase("openmw_text_input.layout", parWindowManager) { // Centre dialog center(); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 7ec444168..8709a9920 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -14,7 +14,7 @@ using namespace MWGui; using namespace MyGUI; ToolTips::ToolTips(WindowManager* windowManager) : - Layout("openmw_tooltips.xml") + Layout("openmw_tooltips.layout") , mGameMode(true) , mWindowManager(windowManager) , mFullHelp(false) @@ -564,8 +564,6 @@ void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) if (attributeId == -1) return; - const ESM::Attribute* attr = MWBase::Environment::get().getWorld()->getStore().attributes.search(attributeId); - assert(attr); std::string icon = ESM::Attribute::attributeIcons[attributeId]; std::string name = ESM::Attribute::gmstAttributeIds[attributeId]; std::string desc = ESM::Attribute::gmstAttributeDescIds[attributeId]; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index a42da60d1..485b79a7d 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -14,7 +14,7 @@ namespace MWGui { TradeWindow::TradeWindow(WindowManager& parWindowManager) : - WindowBase("openmw_trade_window_layout.xml", parWindowManager) + WindowBase("openmw_trade_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop , mCurrentBalance(0) { diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index dad4afb46..f41cdf54e 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -15,52 +15,52 @@ configure_file("${SDIR}/openmw_box.skin.xml" "${DDIR}/openmw_box.skin.xml" COPYO configure_file("${SDIR}/openmw_button.skin.xml" "${DDIR}/openmw_button.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_list.skin.xml" "${DDIR}/openmw_list.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_edit.skin.xml" "${DDIR}/openmw_edit.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_console_layout.xml" "${DDIR}/openmw_console_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_console.layout" "${DDIR}/openmw_console.layout" COPYONLY) configure_file("${SDIR}/openmw_console.skin.xml" "${DDIR}/openmw_console.skin.xml" COPYONLY) configure_file("${SDIR}/openmw.font.xml" "${DDIR}/openmw.font.xml" COPYONLY) configure_file("${SDIR}/openmw_hud_box.skin.xml" "${DDIR}/openmw_hud_box.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_hud_energybar.skin.xml" "${DDIR}/openmw_hud_energybar.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_hud_layout.xml" "${DDIR}/openmw_hud_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_text_input_layout.xml" "${DDIR}/openmw_text_input_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_infobox_layout.xml" "${DDIR}/openmw_infobox_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_race_layout.xml" "${DDIR}/openmw_chargen_race_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_class_layout.xml" "${DDIR}/openmw_chargen_class_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_generate_class_result_layout.xml" "${DDIR}/openmw_chargen_generate_class_result_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_create_class_layout.xml" "${DDIR}/openmw_chargen_create_class_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_select_specialization_layout.xml" "${DDIR}/openmw_chargen_select_specialization_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_select_attribute_layout.xml" "${DDIR}/openmw_chargen_select_attribute_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_select_skill_layout.xml" "${DDIR}/openmw_chargen_select_skill_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_class_description_layout.xml" "${DDIR}/openmw_chargen_class_description_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_birth_layout.xml" "${DDIR}/openmw_chargen_birth_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_chargen_review_layout.xml" "${DDIR}/openmw_chargen_review_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_dialogue_window_layout.xml" "${DDIR}/openmw_dialogue_window_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_hud.layout" "${DDIR}/openmw_hud.layout" COPYONLY) +configure_file("${SDIR}/openmw_text_input.layout" "${DDIR}/openmw_text_input.layout" COPYONLY) +configure_file("${SDIR}/openmw_infobox.layout" "${DDIR}/openmw_infobox.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_race.layout" "${DDIR}/openmw_chargen_race.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_class.layout" "${DDIR}/openmw_chargen_class.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_generate_class_result.layout" "${DDIR}/openmw_chargen_generate_class_result.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_create_class.layout" "${DDIR}/openmw_chargen_create_class.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_select_specialization.layout" "${DDIR}/openmw_chargen_select_specialization.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_select_attribute.layout" "${DDIR}/openmw_chargen_select_attribute.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_select_skill.layout" "${DDIR}/openmw_chargen_select_skill.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_class_description.layout" "${DDIR}/openmw_chargen_class_description.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_birth.layout" "${DDIR}/openmw_chargen_birth.layout" COPYONLY) +configure_file("${SDIR}/openmw_chargen_review.layout" "${DDIR}/openmw_chargen_review.layout" COPYONLY) +configure_file("${SDIR}/openmw_dialogue_window.layout" "${DDIR}/openmw_dialogue_window.layout" COPYONLY) configure_file("${SDIR}/openmw_dialogue_window_skin.xml" "${DDIR}/openmw_dialogue_window_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_inventory_window_layout.xml" "${DDIR}/openmw_inventory_window_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_container_window_layout.xml" "${DDIR}/openmw_container_window_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_inventory_window.layout" "${DDIR}/openmw_inventory_window.layout" COPYONLY) +configure_file("${SDIR}/openmw_container_window.layout" "${DDIR}/openmw_container_window.layout" COPYONLY) configure_file("${SDIR}/openmw_layers.xml" "${DDIR}/openmw_layers.xml" COPYONLY) -configure_file("${SDIR}/openmw_mainmenu_layout.xml" "${DDIR}/openmw_mainmenu_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_mainmenu.layout" "${DDIR}/openmw_mainmenu.layout" COPYONLY) configure_file("${SDIR}/openmw_mainmenu_skin.xml" "${DDIR}/openmw_mainmenu_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_map_window_layout.xml" "${DDIR}/openmw_map_window_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_map_window.layout" "${DDIR}/openmw_map_window.layout" COPYONLY) configure_file("${SDIR}/openmw_map_window_skin.xml" "${DDIR}/openmw_map_window_skin.xml" COPYONLY) configure_file("${SDIR}/openmw.pointer.xml" "${DDIR}/openmw.pointer.xml" COPYONLY) configure_file("${SDIR}/openmw_progress.skin.xml" "${DDIR}/openmw_progress.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_stats_window_layout.xml" "${DDIR}/openmw_stats_window_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_stats_window.layout" "${DDIR}/openmw_stats_window.layout" COPYONLY) configure_file("${SDIR}/openmw_text.skin.xml" "${DDIR}/openmw_text.skin.xml" COPYONLY) configure_file("${SDIR}/openmw_windows.skin.xml" "${DDIR}/openmw_windows.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_messagebox_layout.xml" "${DDIR}/openmw_messagebox_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_interactive_messagebox_layout.xml" "${DDIR}/openmw_interactive_messagebox_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_journal_layout.xml" "${DDIR}/openmw_journal_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_messagebox.layout" "${DDIR}/openmw_messagebox.layout" COPYONLY) +configure_file("${SDIR}/openmw_interactive_messagebox.layout" "${DDIR}/openmw_interactive_messagebox.layout" COPYONLY) +configure_file("${SDIR}/openmw_journal.layout" "${DDIR}/openmw_journal.layout" COPYONLY) configure_file("${SDIR}/openmw_journal_skin.xml" "${DDIR}/openmw_journal_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_tooltips.xml" "${DDIR}/openmw_tooltips.xml" COPYONLY) -configure_file("${SDIR}/openmw_scroll_layout.xml" "${DDIR}/openmw_scroll_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_tooltips.layout" "${DDIR}/openmw_tooltips.layout" COPYONLY) +configure_file("${SDIR}/openmw_scroll.layout" "${DDIR}/openmw_scroll.layout" COPYONLY) configure_file("${SDIR}/openmw_scroll_skin.xml" "${DDIR}/openmw_scroll_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_book_layout.xml" "${DDIR}/openmw_book_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_count_window_layout.xml" "${DDIR}/openmw_count_window_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_trade_window_layout.xml" "${DDIR}/openmw_trade_window_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_settings_window_layout.xml" "${DDIR}/openmw_settings_window_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_confirmation_dialog_layout.xml" "${DDIR}/openmw_confirmation_dialog_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_alchemy_window_layout.xml" "${DDIR}/openmw_alchemy_window_layout.xml" COPYONLY) -configure_file("${SDIR}/openmw_spell_window_layout.xml" "${DDIR}/openmw_spell_window_layout.xml" COPYONLY) +configure_file("${SDIR}/openmw_book.layout" "${DDIR}/openmw_book.layout" COPYONLY) +configure_file("${SDIR}/openmw_count_window.layout" "${DDIR}/openmw_count_window.layout" COPYONLY) +configure_file("${SDIR}/openmw_trade_window.layout" "${DDIR}/openmw_trade_window.layout" COPYONLY) +configure_file("${SDIR}/openmw_settings_window.layout" "${DDIR}/openmw_settings_window.layout" COPYONLY) +configure_file("${SDIR}/openmw_confirmation_dialog.layout" "${DDIR}/openmw_confirmation_dialog.layout" COPYONLY) +configure_file("${SDIR}/openmw_alchemy_window.layout" "${DDIR}/openmw_alchemy_window.layout" COPYONLY) +configure_file("${SDIR}/openmw_spell_window.layout" "${DDIR}/openmw_spell_window.layout" COPYONLY) configure_file("${SDIR}/atlas1.cfg" "${DDIR}/atlas1.cfg" COPYONLY) configure_file("${SDIR}/smallbars.png" "${DDIR}/smallbars.png" COPYONLY) configure_file("${SDIR}/EBGaramond-Regular.ttf" "${DDIR}/EBGaramond-Regular.ttf" COPYONLY) diff --git a/files/mygui/openmw_alchemy_window_layout.xml b/files/mygui/openmw_alchemy_window.layout similarity index 100% rename from files/mygui/openmw_alchemy_window_layout.xml rename to files/mygui/openmw_alchemy_window.layout diff --git a/files/mygui/openmw_book_layout.xml b/files/mygui/openmw_book.layout similarity index 100% rename from files/mygui/openmw_book_layout.xml rename to files/mygui/openmw_book.layout diff --git a/files/mygui/openmw_chargen_birth_layout.xml b/files/mygui/openmw_chargen_birth.layout similarity index 100% rename from files/mygui/openmw_chargen_birth_layout.xml rename to files/mygui/openmw_chargen_birth.layout diff --git a/files/mygui/openmw_chargen_class_layout.xml b/files/mygui/openmw_chargen_class.layout similarity index 100% rename from files/mygui/openmw_chargen_class_layout.xml rename to files/mygui/openmw_chargen_class.layout diff --git a/files/mygui/openmw_chargen_class_description_layout.xml b/files/mygui/openmw_chargen_class_description.layout similarity index 100% rename from files/mygui/openmw_chargen_class_description_layout.xml rename to files/mygui/openmw_chargen_class_description.layout diff --git a/files/mygui/openmw_chargen_create_class_layout.xml b/files/mygui/openmw_chargen_create_class.layout similarity index 100% rename from files/mygui/openmw_chargen_create_class_layout.xml rename to files/mygui/openmw_chargen_create_class.layout diff --git a/files/mygui/openmw_chargen_generate_class_result_layout.xml b/files/mygui/openmw_chargen_generate_class_result.layout similarity index 100% rename from files/mygui/openmw_chargen_generate_class_result_layout.xml rename to files/mygui/openmw_chargen_generate_class_result.layout diff --git a/files/mygui/openmw_chargen_race_layout.xml b/files/mygui/openmw_chargen_race.layout similarity index 100% rename from files/mygui/openmw_chargen_race_layout.xml rename to files/mygui/openmw_chargen_race.layout diff --git a/files/mygui/openmw_chargen_review_layout.xml b/files/mygui/openmw_chargen_review.layout similarity index 100% rename from files/mygui/openmw_chargen_review_layout.xml rename to files/mygui/openmw_chargen_review.layout diff --git a/files/mygui/openmw_chargen_select_attribute_layout.xml b/files/mygui/openmw_chargen_select_attribute.layout similarity index 100% rename from files/mygui/openmw_chargen_select_attribute_layout.xml rename to files/mygui/openmw_chargen_select_attribute.layout diff --git a/files/mygui/openmw_chargen_select_skill_layout.xml b/files/mygui/openmw_chargen_select_skill.layout similarity index 100% rename from files/mygui/openmw_chargen_select_skill_layout.xml rename to files/mygui/openmw_chargen_select_skill.layout diff --git a/files/mygui/openmw_chargen_select_specialization_layout.xml b/files/mygui/openmw_chargen_select_specialization.layout similarity index 100% rename from files/mygui/openmw_chargen_select_specialization_layout.xml rename to files/mygui/openmw_chargen_select_specialization.layout diff --git a/files/mygui/openmw_confirmation_dialog_layout.xml b/files/mygui/openmw_confirmation_dialog.layout similarity index 100% rename from files/mygui/openmw_confirmation_dialog_layout.xml rename to files/mygui/openmw_confirmation_dialog.layout diff --git a/files/mygui/openmw_console_layout.xml b/files/mygui/openmw_console.layout similarity index 100% rename from files/mygui/openmw_console_layout.xml rename to files/mygui/openmw_console.layout diff --git a/files/mygui/openmw_container_window_layout.xml b/files/mygui/openmw_container_window.layout similarity index 100% rename from files/mygui/openmw_container_window_layout.xml rename to files/mygui/openmw_container_window.layout diff --git a/files/mygui/openmw_count_window_layout.xml b/files/mygui/openmw_count_window.layout similarity index 100% rename from files/mygui/openmw_count_window_layout.xml rename to files/mygui/openmw_count_window.layout diff --git a/files/mygui/openmw_dialogue_window_layout.xml b/files/mygui/openmw_dialogue_window.layout similarity index 100% rename from files/mygui/openmw_dialogue_window_layout.xml rename to files/mygui/openmw_dialogue_window.layout diff --git a/files/mygui/openmw_hud_layout.xml b/files/mygui/openmw_hud.layout similarity index 100% rename from files/mygui/openmw_hud_layout.xml rename to files/mygui/openmw_hud.layout diff --git a/files/mygui/openmw_infobox_layout.xml b/files/mygui/openmw_infobox.layout similarity index 100% rename from files/mygui/openmw_infobox_layout.xml rename to files/mygui/openmw_infobox.layout diff --git a/files/mygui/openmw_interactive_messagebox_layout.xml b/files/mygui/openmw_interactive_messagebox.layout similarity index 100% rename from files/mygui/openmw_interactive_messagebox_layout.xml rename to files/mygui/openmw_interactive_messagebox.layout diff --git a/files/mygui/openmw_inventory_window_layout.xml b/files/mygui/openmw_inventory_window.layout similarity index 100% rename from files/mygui/openmw_inventory_window_layout.xml rename to files/mygui/openmw_inventory_window.layout diff --git a/files/mygui/openmw_journal_layout.xml b/files/mygui/openmw_journal.layout similarity index 100% rename from files/mygui/openmw_journal_layout.xml rename to files/mygui/openmw_journal.layout diff --git a/files/mygui/openmw_mainmenu_layout.xml b/files/mygui/openmw_mainmenu.layout similarity index 100% rename from files/mygui/openmw_mainmenu_layout.xml rename to files/mygui/openmw_mainmenu.layout diff --git a/files/mygui/openmw_map_window_layout.xml b/files/mygui/openmw_map_window.layout similarity index 100% rename from files/mygui/openmw_map_window_layout.xml rename to files/mygui/openmw_map_window.layout diff --git a/files/mygui/openmw_messagebox_layout.xml b/files/mygui/openmw_messagebox.layout similarity index 100% rename from files/mygui/openmw_messagebox_layout.xml rename to files/mygui/openmw_messagebox.layout diff --git a/files/mygui/openmw_scroll_layout.xml b/files/mygui/openmw_scroll.layout similarity index 100% rename from files/mygui/openmw_scroll_layout.xml rename to files/mygui/openmw_scroll.layout diff --git a/files/mygui/openmw_settings_window_layout.xml b/files/mygui/openmw_settings_window.layout similarity index 100% rename from files/mygui/openmw_settings_window_layout.xml rename to files/mygui/openmw_settings_window.layout diff --git a/files/mygui/openmw_spell_window_layout.xml b/files/mygui/openmw_spell_window.layout similarity index 100% rename from files/mygui/openmw_spell_window_layout.xml rename to files/mygui/openmw_spell_window.layout diff --git a/files/mygui/openmw_stats_window_layout.xml b/files/mygui/openmw_stats_window.layout similarity index 100% rename from files/mygui/openmw_stats_window_layout.xml rename to files/mygui/openmw_stats_window.layout diff --git a/files/mygui/openmw_text_input_layout.xml b/files/mygui/openmw_text_input.layout similarity index 100% rename from files/mygui/openmw_text_input_layout.xml rename to files/mygui/openmw_text_input.layout diff --git a/files/mygui/openmw_tooltips.xml b/files/mygui/openmw_tooltips.layout similarity index 100% rename from files/mygui/openmw_tooltips.xml rename to files/mygui/openmw_tooltips.layout diff --git a/files/mygui/openmw_trade_window_layout.xml b/files/mygui/openmw_trade_window.layout similarity index 100% rename from files/mygui/openmw_trade_window_layout.xml rename to files/mygui/openmw_trade_window.layout From 4c39fefd1ed3b65a18352a14d71ff01fc426b004 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Jul 2012 12:30:50 +0200 Subject: [PATCH 038/688] Issue #107: World is accessed only through the interface class from now on; some include cleanup --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 3 +- apps/openmw/mwbase/environment.cpp | 8 ++-- apps/openmw/mwbase/environment.hpp | 13 +++--- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 6 +-- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/misc.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 8 ++-- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanager.cpp | 18 ++++---- apps/openmw/mwdialogue/journal.cpp | 12 +++--- apps/openmw/mwdialogue/journalentry.cpp | 24 +++++------ apps/openmw/mwdialogue/journalentry.hpp | 14 ++----- apps/openmw/mwdialogue/quest.cpp | 17 ++++---- apps/openmw/mwdialogue/quest.hpp | 6 +-- apps/openmw/mwdialogue/topic.cpp | 4 +- apps/openmw/mwdialogue/topic.hpp | 7 +--- apps/openmw/mwgui/alchemywindow.cpp | 4 +- apps/openmw/mwgui/charactercreation.hpp | 6 ++- apps/openmw/mwgui/confirmationdialog.cpp | 2 +- apps/openmw/mwgui/console.cpp | 3 ++ apps/openmw/mwgui/container.cpp | 8 +++- apps/openmw/mwgui/countdialog.cpp | 4 +- apps/openmw/mwgui/hud.cpp | 10 +++-- apps/openmw/mwgui/inventorywindow.cpp | 15 ++++--- apps/openmw/mwgui/journalwindow.cpp | 9 ++-- apps/openmw/mwgui/referenceinterface.cpp | 5 ++- apps/openmw/mwgui/settingswindow.cpp | 6 ++- apps/openmw/mwgui/spellwindow.cpp | 9 +++- apps/openmw/mwgui/stats_window.cpp | 11 +++-- apps/openmw/mwgui/tooltips.cpp | 16 ++++---- apps/openmw/mwgui/tradewindow.cpp | 3 +- apps/openmw/mwmechanics/activespells.cpp | 5 ++- apps/openmw/mwmechanics/mechanicsmanager.cpp | 6 +-- apps/openmw/mwmechanics/spells.cpp | 5 ++- apps/openmw/mwmechanics/spellsuccess.hpp | 6 ++- apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/debugging.cpp | 16 +++++--- apps/openmw/mwrender/localmap.cpp | 14 ++++--- apps/openmw/mwrender/npcanimation.cpp | 5 ++- apps/openmw/mwrender/renderingmanager.cpp | 22 +++++----- apps/openmw/mwrender/sky.cpp | 3 +- apps/openmw/mwrender/terrain.cpp | 10 +++-- apps/openmw/mwrender/terrain.hpp | 2 + apps/openmw/mwscript/aiextensions.cpp | 2 + apps/openmw/mwscript/animationextensions.cpp | 2 +- apps/openmw/mwscript/cellextensions.cpp | 4 +- apps/openmw/mwscript/compilercontext.cpp | 8 +++- apps/openmw/mwscript/controlextensions.cpp | 1 + apps/openmw/mwscript/globalscripts.cpp | 3 ++ apps/openmw/mwscript/interpretercontext.cpp | 4 +- apps/openmw/mwscript/interpretercontext.hpp | 3 +- apps/openmw/mwscript/miscextensions.cpp | 8 ++-- apps/openmw/mwscript/ref.hpp | 2 +- apps/openmw/mwscript/soundextensions.cpp | 3 +- apps/openmw/mwsound/soundmanager.cpp | 4 +- apps/openmw/mwworld/actionequip.cpp | 11 ++--- apps/openmw/mwworld/actionopen.cpp | 7 ++-- apps/openmw/mwworld/actiontake.cpp | 3 +- apps/openmw/mwworld/actionteleport.cpp | 3 +- apps/openmw/mwworld/cells.cpp | 12 +++--- apps/openmw/mwworld/cells.hpp | 23 +++++------ apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 3 +- apps/openmw/mwworld/physicssystem.cpp | 21 +++++----- apps/openmw/mwworld/player.cpp | 21 +++++----- apps/openmw/mwworld/player.hpp | 10 +++-- apps/openmw/mwworld/scene.cpp | 41 +++++++++---------- apps/openmw/mwworld/scene.hpp | 3 +- apps/openmw/mwworld/weather.cpp | 12 +++--- .../mwworld/{world.cpp => worldimp.cpp} | 6 +-- .../mwworld/{world.hpp => worldimp.hpp} | 4 +- libs/openengine/bullet/CMotionState.cpp | 1 - libs/openengine/bullet/physic.cpp | 3 +- libs/openengine/bullet/trace.h | 5 +-- 85 files changed, 331 insertions(+), 279 deletions(-) rename apps/openmw/mwworld/{world.cpp => worldimp.cpp} (99%) rename apps/openmw/mwworld/{world.hpp => worldimp.hpp} (99%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 770596c69..9534ecc90 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -47,7 +47,7 @@ add_openmw_dir (mwsound ) add_openmw_dir (mwworld - refdata world physicssystem scene globals class action nullaction actionteleport + refdata worldimp physicssystem scene globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellfunctors cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 70e69b99c..45b4ab514 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -38,10 +38,10 @@ #include "mwsound/soundmanager.hpp" -#include "mwworld/world.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" #include "mwworld/cellstore.hpp" +#include "mwworld/worldimp.hpp" #include "mwclass/classes.hpp" @@ -51,6 +51,7 @@ #include "mwmechanics/mechanicsmanager.hpp" #include "mwbase/environment.hpp" +#include "mwbase/world.hpp" void OMW::Engine::executeLocalScripts() diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 792e240f4..7218f22eb 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -9,13 +9,13 @@ #include "../mwsound/soundmanager.hpp" -#include "../mwworld/world.hpp" - #include "../mwdialogue/dialoguemanager.hpp" #include "../mwdialogue/journal.hpp" #include "../mwmechanics/mechanicsmanager.hpp" +#include "world.hpp" + MWBase::Environment *MWBase::Environment::sThis = 0; MWBase::Environment::Environment() @@ -32,7 +32,7 @@ MWBase::Environment::~Environment() sThis = 0; } -void MWBase::Environment::setWorld (MWWorld::World *world) +void MWBase::Environment::setWorld (World *world) { mWorld = world; } @@ -77,7 +77,7 @@ void MWBase::Environment::setFrameDuration (float duration) mFrameDuration = duration; } -MWWorld::World *MWBase::Environment::getWorld() const +MWBase::World *MWBase::Environment::getWorld() const { assert (mWorld); return mWorld; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 521beee0a..ad5a6f655 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -32,13 +32,10 @@ namespace MWInput struct MWInputManager; } -namespace MWWorld -{ - class World; -} - namespace MWBase { + class World; + /// \brief Central hub for mw-subsystems /// /// This class allows each mw-subsystem to access any others subsystem's top-level manager class. @@ -49,7 +46,7 @@ namespace MWBase { static Environment *sThis; - MWWorld::World *mWorld; + World *mWorld; MWSound::SoundManager *mSoundManager; MWScript::ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; @@ -71,7 +68,7 @@ namespace MWBase ~Environment(); - void setWorld (MWWorld::World *world); + void setWorld (World *world); void setSoundManager (MWSound::SoundManager *soundManager); @@ -90,7 +87,7 @@ namespace MWBase void setFrameDuration (float duration); ///< Set length of current frame in seconds. - MWWorld::World *getWorld() const; + World *getWorld() const; MWSound::SoundManager *getSoundManager() const; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 3321ab154..55f5bc11f 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -4,11 +4,11 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionalchemy.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index c3beed9ea..9e029d005 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -5,15 +5,15 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" -#include "../mwbase/environment.hpp" - #include "../mwrender/objects.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 6ad680b65..780e2e0f5 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -4,10 +4,10 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c063dd1b1..04a1f3ee6 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -4,12 +4,12 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/tooltips.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 25e00fa3d..0c8682df4 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -4,12 +4,12 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/customdata.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 6b302f2b6..9148d3575 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -4,12 +4,12 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/actionteleport.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 8bf526e77..bc68e46b2 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -4,10 +4,10 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 61af188c5..9fa11be5e 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -4,13 +4,13 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 17dfff1e9..b35172f2f 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -4,12 +4,12 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index ff6acb6b5..1163b43b2 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -6,10 +6,10 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5d5a59d27..e1b8541a1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -9,6 +9,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" @@ -16,13 +19,12 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" -#include "../mwgui/window_manager.hpp" +#include "../mwrender/actors.hpp" -#include "../mwbase/environment.hpp" +#include "../mwgui/window_manager.hpp" namespace { diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 8b0bbcde8..cd393f388 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -4,10 +4,10 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index feac83787..a8bfdead1 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -4,12 +4,12 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index e38dc68a5..4a4550ba1 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -4,10 +4,10 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 73e8107fc..90f98afb9 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -4,12 +4,12 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/cellstore.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 7f0765843..98562c053 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -10,9 +10,9 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/refdata.hpp" #include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" @@ -121,24 +121,24 @@ namespace } template - bool checkGlobal (char comp, const std::string& name, T value, MWWorld::World& world) + bool checkGlobal (char comp, const std::string& name, T value) { - switch (world.getGlobalVariableType (name)) + switch (MWBase::Environment::get().getWorld()->getGlobalVariableType (name)) { case 's': - return selectCompare (comp, world.getGlobalVariable (name).mShort, value); + return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mShort, value); case 'l': - return selectCompare (comp, world.getGlobalVariable (name).mLong, value); + return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mLong, value); case 'f': - return selectCompare (comp, world.getGlobalVariable (name).mFloat, value); + return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat, value); case ' ': - world.getGlobalVariable (name); // trigger exception + MWBase::Environment::get().getWorld()->getGlobalVariable (name); // trigger exception break; default: @@ -309,12 +309,12 @@ namespace MWDialogue if (select.type==ESM::VT_Short || select.type==ESM::VT_Int || select.type==ESM::VT_Long) { - if (!checkGlobal (comp, toLower (name), select.i, *MWBase::Environment::get().getWorld())) + if (!checkGlobal (comp, toLower (name), select.i)) return false; } else if (select.type==ESM::VT_Float) { - if (!checkGlobal (comp, toLower (name), select.f, *MWBase::Environment::get().getWorld())) + if (!checkGlobal (comp, toLower (name), select.f)) return false; } else diff --git a/apps/openmw/mwdialogue/journal.cpp b/apps/openmw/mwdialogue/journal.cpp index eb828a76c..ad10be3aa 100644 --- a/apps/openmw/mwdialogue/journal.cpp +++ b/apps/openmw/mwdialogue/journal.cpp @@ -2,12 +2,11 @@ #include "journal.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/messagebox.hpp" -#include "../mwworld/world.hpp" - namespace MWDialogue { Quest& Journal::getQuest (const std::string& id) @@ -30,14 +29,13 @@ namespace MWDialogue void Journal::addEntry (const std::string& id, int index) { - StampedJournalEntry entry = - StampedJournalEntry::makeFromQuest (id, index, *MWBase::Environment::get().getWorld()); + StampedJournalEntry entry = StampedJournalEntry::makeFromQuest (id, index); mJournal.push_back (entry); Quest& quest = getQuest (id); - quest.addEntry (entry, *MWBase::Environment::get().getWorld()); // we are doing slicing on purpose here + quest.addEntry (entry); // we are doing slicing on purpose here std::vector empty; std::string notification = MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sJournalEntry")->str; @@ -48,7 +46,7 @@ namespace MWDialogue { Quest& quest = getQuest (id); - quest.setIndex (index, *MWBase::Environment::get().getWorld()); + quest.setIndex (index); } void Journal::addTopic (const std::string& topicId, const std::string& infoId) @@ -63,7 +61,7 @@ namespace MWDialogue iter = result.first; } - iter->second.addEntry (JournalEntry (topicId, infoId), *MWBase::Environment::get().getWorld()); + iter->second.addEntry (JournalEntry (topicId, infoId)); } int Journal::getJournalIndex (const std::string& id) const diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 4eb6b8001..9d58687e1 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -5,7 +5,8 @@ #include -#include "../mwworld/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" namespace MWDialogue { @@ -27,16 +28,14 @@ namespace MWDialogue throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic); } - JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index, - const MWWorld::World& world) + JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) { - return JournalEntry (topic, idFromIndex (topic, index, world)); + return JournalEntry (topic, idFromIndex (topic, index)); } - std::string JournalEntry::idFromIndex (const std::string& topic, int index, - const MWWorld::World& world) + std::string JournalEntry::idFromIndex (const std::string& topic, int index) { - const ESM::Dialogue *dialogue = world.getStore().dialogs.find (topic); + const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().dialogs.find (topic); for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) @@ -57,13 +56,12 @@ namespace MWDialogue : JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) {} - StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index, - const MWWorld::World& world) + StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index) { - int day = world.getGlobalVariable ("dayspassed").mLong; - int month = world.getGlobalVariable ("day").mLong; - int dayOfMonth = world.getGlobalVariable ("month").mLong; + int day = MWBase::Environment::get().getWorld()->getGlobalVariable ("dayspassed").mLong; + int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong; + int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; - return StampedJournalEntry (topic, idFromIndex (topic, index, world), day, month, dayOfMonth); + return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth); } } diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 11b715630..9a3270439 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -8,11 +8,6 @@ namespace ESMS struct ESMStore; } -namespace MWWorld -{ - class World; -} - namespace MWDialogue { /// \brief A quest or dialogue entry @@ -27,11 +22,9 @@ namespace MWDialogue std::string getText (const ESMS::ESMStore& store) const; - static JournalEntry makeFromQuest (const std::string& topic, int index, - const MWWorld::World& world); + static JournalEntry makeFromQuest (const std::string& topic, int index); - static std::string idFromIndex (const std::string& topic, int index, - const MWWorld::World& world); + static std::string idFromIndex (const std::string& topic, int index); }; /// \biref A quest entry with a timestamp. @@ -46,8 +39,7 @@ namespace MWDialogue StampedJournalEntry (const std::string& topic, const std::string& infoId, int day, int month, int dayOfMonth); - static StampedJournalEntry makeFromQuest (const std::string& topic, int index, - const MWWorld::World& world); + static StampedJournalEntry makeFromQuest (const std::string& topic, int index); }; } diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 1f387e862..484c2c19c 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -3,7 +3,8 @@ #include -#include "../mwworld/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" namespace MWDialogue { @@ -15,9 +16,9 @@ namespace MWDialogue : Topic (topic), mIndex (0), mFinished (false) {} - const std::string Quest::getName (const MWWorld::World& world) const + const std::string Quest::getName() const { - const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic); + const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().dialogs.find (mTopic); for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) @@ -32,9 +33,9 @@ namespace MWDialogue return mIndex; } - void Quest::setIndex (int index, const MWWorld::World& world) + void Quest::setIndex (int index) { - const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic); + const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().dialogs.find (mTopic); for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) @@ -58,11 +59,11 @@ namespace MWDialogue return mFinished; } - void Quest::addEntry (const JournalEntry& entry, const MWWorld::World& world) + void Quest::addEntry (const JournalEntry& entry) { int index = -1; - const ESM::Dialogue *dialogue = world.getStore().dialogs.find (entry.mTopic); + const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().dialogs.find (entry.mTopic); for (std::vector::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) @@ -75,7 +76,7 @@ namespace MWDialogue if (index==-1) throw std::runtime_error ("unknown journal entry for topic " + mTopic); - setIndex (index, world); + setIndex (index); for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter) if (*iter==entry.mInfoId) diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index c162c03f4..5fcc894ea 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -17,17 +17,17 @@ namespace MWDialogue Quest (const std::string& topic); - const std::string getName (const MWWorld::World& world) const; + const std::string getName() const; ///< May be an empty string int getIndex() const; - void setIndex (int index, const MWWorld::World& world); + void setIndex (int index); ///< Calling this function with a non-existant index while throw an exception. bool isFinished() const; - virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world); + virtual void addEntry (const JournalEntry& entry); ///< Add entry and adjust index accordingly. /// /// \note Redundant entries are ignored, but the index is still adjusted. diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index 8f165d3c8..8c1dfafb8 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -3,8 +3,6 @@ #include -#include "../mwworld/world.hpp" - namespace MWDialogue { Topic::Topic() @@ -17,7 +15,7 @@ namespace MWDialogue Topic::~Topic() {} - void Topic::addEntry (const JournalEntry& entry, const MWWorld::World& world) + void Topic::addEntry (const JournalEntry& entry) { if (entry.mTopic!=mTopic) throw std::runtime_error ("topic does not match: " + mTopic); diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index c085f1ed9..3ad15903f 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -6,11 +6,6 @@ #include "journalentry.hpp" -namespace MWWorld -{ - class World; -} - namespace MWDialogue { /// \brief Collection of seen responses for a topic @@ -34,7 +29,7 @@ namespace MWDialogue virtual ~Topic(); - virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world); + virtual void addEntry (const JournalEntry& entry); ///< Add entry /// /// \note Redundant entries are ignored. diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 6fedc8d1d..1267109b8 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -3,10 +3,12 @@ #include #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" + #include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" + #include "../mwsound/soundmanager.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 02b48ffe2..e9c90877e 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -3,10 +3,12 @@ #include "window_manager.hpp" +#include + +#include "../mwbase/world.hpp" + #include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/stat.hpp" -#include "../mwworld/world.hpp" -#include namespace MWGui { diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 5e12c3296..8663c141b 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -3,7 +3,7 @@ #include #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 8115835f4..5fc9b6c96 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -3,6 +3,9 @@ #include +#include +#include + #include #include "../mwscript/extensions.hpp" diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 1d4c70de2..0d168a147 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -3,19 +3,23 @@ #include #include #include -#include +#include #include #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwworld/manualref.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" + #include "../mwclass/container.hpp" + #include "../mwinput/inputmanager.hpp" + #include "../mwsound/soundmanager.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index e0a9bb908..72064efaa 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -3,7 +3,7 @@ #include #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" namespace MWGui { @@ -77,7 +77,7 @@ namespace MWGui { if (_sender->getCaption() == "") return; - + unsigned int count; try { diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 4ad9845f0..ebba1661b 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -7,11 +7,13 @@ #include #include "../mwbase/environment.hpp" -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/world.hpp" + #include "../mwworld/class.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/player.hpp" +#include "../mwsound/soundmanager.hpp" + #include "inventorywindow.hpp" #include "window_manager.hpp" #include "container.hpp" @@ -239,7 +241,7 @@ void HUD::onWorldClicked(MyGUI::Widget* _sender) // drop item into the gameworld MWWorld::Ptr object = *mDragAndDrop->mDraggedWidget->getUserData(); - MWWorld::World* world = MWBase::Environment::get().getWorld(); + MWBase::World* world = MWBase::Environment::get().getWorld(); MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); @@ -308,7 +310,7 @@ void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y) float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); - MWWorld::World* world = MWBase::Environment::get().getWorld(); + MWBase::World* world = MWBase::Environment::get().getWorld(); // if we can't drop the object at the wanted position, show the "drop on ground" cursor. bool canDrop = world->canPlaceObject(mouseX, mouseY); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index a26a958bd..b32832f86 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -3,21 +3,24 @@ #include #include #include -#include -#include +#include #include -#include "../mwclass/container.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/player.hpp" -#include "../mwbase/environment.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" + #include "../mwsound/soundmanager.hpp" +#include "../mwclass/container.hpp" + #include "window_manager.hpp" #include "widgets.hpp" #include "bookwindow.hpp" @@ -171,7 +174,7 @@ namespace MWGui /// \todo scripts boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); - + action->execute(); // this is necessary for books/scrolls: if they are already in the player's inventory, diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 7f9000bc7..e49879b0b 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -1,11 +1,14 @@ #include "journalwindow.hpp" -#include "window_manager.hpp" -#include "../mwdialogue/journal.hpp" + #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" + +#include "../mwdialogue/journal.hpp" #include "../mwsound/soundmanager.hpp" +#include "window_manager.hpp" + namespace { struct book diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index c6e710952..f891b3338 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -1,9 +1,10 @@ #include "referenceinterface.hpp" -#include "../mwworld/player.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwworld/player.hpp" + namespace MWGui { ReferenceInterface::ReferenceInterface() diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 6ceed664e..3f2205728 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -11,8 +11,12 @@ #include #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" + +#include "../mwrender/renderingmanager.hpp" + #include "../mwsound/soundmanager.hpp" + #include "../mwinput/inputmanager.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 909798f1a..5bca77e61 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -4,13 +4,18 @@ #include #include -#include "../mwworld/world.hpp" +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwbase/environment.hpp" + #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" + #include "../mwsound/soundmanager.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 3584af7c7..87b0c27fb 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -6,10 +6,13 @@ #include -#include "../mwmechanics/mechanicsmanager.hpp" -#include "../mwworld/world.hpp" -#include "../mwworld/player.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/mechanicsmanager.hpp" #include "window_manager.hpp" #include "tooltips.hpp" @@ -539,7 +542,7 @@ void StatsWindow::updateSkillArea() skillWidgets[skillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); skillWidgets[skillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); } - + addValueItem(mWindowManager.getGameSettingString("sBounty", "Bounty"), boost::lexical_cast(static_cast(bounty)), "normal", coord1, coord2); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 7ec444168..e79436adc 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -1,15 +1,17 @@ #include "tooltips.hpp" -#include "window_manager.hpp" -#include "widgets.hpp" -#include "../mwworld/class.hpp" -#include "../mwworld/world.hpp" -#include "../mwbase/environment.hpp" - #include #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwworld/class.hpp" + +#include "window_manager.hpp" +#include "widgets.hpp" + using namespace MWGui; using namespace MyGUI; @@ -392,7 +394,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) /** * \todo - * the various potion effects should appear in the tooltip depending if the player + * the various potion effects should appear in the tooltip depending if the player * has enough skill in alchemy to know about the effects of this potion. */ diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 847898938..70f8bf57e 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -3,7 +3,8 @@ #include #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" + #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index ced2a5c3f..aedfcd2ea 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -3,9 +3,10 @@ #include -#include "../mwbase/environment.hpp" +#include -#include "../mwworld/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" namespace MWMechanics { diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanager.cpp index 331074ef7..13052d064 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.cpp @@ -3,14 +3,14 @@ #include -#include "../mwgui/window_manager.hpp" - #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/player.hpp" +#include "../mwgui/window_manager.hpp" + namespace MWMechanics { void MechanicsManager::buildPlayer() diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 70eb78639..5d21d96d0 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -1,11 +1,12 @@ #include "spells.hpp" +#include + #include #include "../mwbase/environment.hpp" - -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" #include "magiceffects.hpp" diff --git a/apps/openmw/mwmechanics/spellsuccess.hpp b/apps/openmw/mwmechanics/spellsuccess.hpp index 11ac7cda7..54172a72a 100644 --- a/apps/openmw/mwmechanics/spellsuccess.hpp +++ b/apps/openmw/mwmechanics/spellsuccess.hpp @@ -1,9 +1,11 @@ #ifndef MWMECHANICS_SPELLSUCCESS_H #define MWMECHANICS_SPELLSUCCESS_H -#include "../mwworld/ptr.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" #include "npcstats.hpp" diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 206a051f0..a19d411a4 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,7 +1,7 @@ #include "creatureanimation.hpp" #include "renderconst.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" using namespace Ogre; using namespace NifOgre; diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 9086a9bc4..5fe59f5e5 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -1,18 +1,22 @@ #include "debugging.hpp" -#include +#include #include #include #include #include -#include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone -#include "../mwbase/environment.hpp" -#include "../mwworld/ptr.hpp" #include #include +#include + +#include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone +#include "../mwbase/environment.hpp" + +#include "../mwworld/ptr.hpp" + #include "player.hpp" using namespace Ogre; @@ -162,11 +166,11 @@ Debugging::~Debugging() bool Debugging::toggleRenderMode (int mode){ switch (mode) { - case MWWorld::World::Render_CollisionDebug: + case MWBase::World::Render_CollisionDebug: return mEngine->toggleDebugRendering(); - case MWWorld::World::Render_Pathgrid: + case MWBase::World::Render_Pathgrid: togglePathgrid(); return mPathgridEnabled; } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 2442700bb..9d38f14da 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -1,14 +1,16 @@ #include "localmap.hpp" -#include "renderingmanager.hpp" - -#include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" -#include "../mwgui/window_manager.hpp" -#include "renderconst.hpp" #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwgui/window_manager.hpp" + +#include "renderconst.hpp" +#include "renderingmanager.hpp" + using namespace MWRender; using namespace Ogre; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 5681a3ad1..888b11e1e 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1,8 +1,9 @@ #include "npcanimation.hpp" -#include "../mwworld/world.hpp" -#include "renderconst.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "renderconst.hpp" using namespace Ogre; using namespace NifOgre; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 03bb24635..7ca5f8982 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1,6 +1,6 @@ #include "renderingmanager.hpp" -#include +#include #include "OgreRoot.h" #include "OgreRenderWindow.h" @@ -9,22 +9,24 @@ #include "OgreCamera.h" #include "OgreTextureManager.h" -#include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone -#include "../mwworld/ptr.hpp" -#include "../mwworld/player.hpp" -#include "../mwbase/environment.hpp" #include #include +#include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone +#include "../mwbase/environment.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/player.hpp" + +#include "../mwgui/window_manager.hpp" // FIXME +#include "../mwinput/inputmanager.hpp" // FIXME + #include "shadows.hpp" #include "shaderhelper.hpp" #include "localmap.hpp" #include "water.hpp" #include "compositors.hpp" -#include "../mwgui/window_manager.hpp" // FIXME -#include "../mwinput/inputmanager.hpp" // FIXME - using namespace MWRender; using namespace Ogre; @@ -298,9 +300,9 @@ void RenderingManager::skySetMoonColour (bool red){ bool RenderingManager::toggleRenderMode(int mode) { - if (mode == MWWorld::World::Render_CollisionDebug || mode == MWWorld::World::Render_Pathgrid) + if (mode == MWBase::World::Render_CollisionDebug || mode == MWBase::World::Render_Pathgrid) return mDebugging->toggleRenderMode(mode); - else if (mode == MWWorld::World::Render_Wireframe) + else if (mode == MWBase::World::Render_Wireframe) { if (mRendering.getCamera()->getPolygonMode() == PM_SOLID) { diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 19e45c189..cf4386793 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -11,7 +11,8 @@ #include #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" + #include "renderconst.hpp" #include "renderingmanager.hpp" diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 4dac750c7..4678daa34 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -1,16 +1,18 @@ -#include -#include #include -#include "../mwworld/world.hpp" +#include +#include + +#include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "terrainmaterial.hpp" #include "terrain.hpp" #include "renderconst.hpp" #include "shadows.hpp" -#include +#include "renderingmanager.hpp" using namespace Ogre; diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 273ede084..0693c0cec 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -16,6 +16,8 @@ namespace Ogre{ namespace MWRender{ + class RenderingManager; + /** * Implements the Morrowind terrain using the Ogre Terrain Component * diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 08b1f6f8c..924a0e9dd 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -7,6 +7,8 @@ #include #include +#include "../mwworld/class.hpp" + #include "../mwmechanics/creaturestats.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/animationextensions.cpp b/apps/openmw/mwscript/animationextensions.cpp index 864a2bf1d..6f9253e5d 100644 --- a/apps/openmw/mwscript/animationextensions.cpp +++ b/apps/openmw/mwscript/animationextensions.cpp @@ -9,7 +9,7 @@ #include #include -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" #include "interpretercontext.hpp" #include "ref.hpp" diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index a5db5fd5f..c1d09fac4 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -1,6 +1,8 @@ #include "cellextensions.hpp" +#include + #include #include @@ -8,8 +10,8 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/player.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index 0a6ffd0c7..57e786b11 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -1,9 +1,13 @@ #include "compilercontext.hpp" -#include "../mwbase/environment.hpp" +#include -#include "../mwworld/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" #include "scriptmanager.hpp" diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 1a7b45246..1f5bdcafb 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index eb96ba5e2..3f4cec723 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -3,6 +3,9 @@ #include +#include +#include + #include "interpretercontext.hpp" #include "scriptmanager.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 152fcf85c..5da512778 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -3,13 +3,13 @@ #include #include -#include #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" -#include "../mwworld/world.hpp" +#include "../mwworld/class.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 9fdc3c21a..6d97f7949 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -5,8 +5,9 @@ #include +#include "../mwbase/world.hpp" + #include "../mwworld/ptr.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/action.hpp" namespace MWSound diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index c7569ccdd..0c328e0da 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1,6 +1,8 @@ #include "miscextensions.hpp" +#include + #include #include @@ -102,7 +104,7 @@ namespace MWScript static_cast (runtime.getContext()); bool enabled = - MWBase::Environment::get().getWorld()->toggleRenderMode (MWWorld::World::Render_CollisionDebug); + MWBase::Environment::get().getWorld()->toggleRenderMode (MWBase::World::Render_CollisionDebug); context.report (enabled ? "Collision Mesh Rendering -> On" : "Collision Mesh Rendering -> Off"); @@ -119,7 +121,7 @@ namespace MWScript static_cast (runtime.getContext()); bool enabled = - MWBase::Environment::get().getWorld()->toggleRenderMode (MWWorld::World::Render_Wireframe); + MWBase::Environment::get().getWorld()->toggleRenderMode (MWBase::World::Render_Wireframe); context.report (enabled ? "Wireframe Rendering -> On" : "Wireframe Rendering -> Off"); @@ -135,7 +137,7 @@ namespace MWScript static_cast (runtime.getContext()); bool enabled = - MWBase::Environment::get().getWorld()->toggleRenderMode (MWWorld::World::Render_Pathgrid); + MWBase::Environment::get().getWorld()->toggleRenderMode (MWBase::World::Render_Pathgrid); context.report (enabled ? "Path Grid rendering -> On" : "Path Grid Rendering -> Off"); diff --git a/apps/openmw/mwscript/ref.hpp b/apps/openmw/mwscript/ref.hpp index 28093c4e5..81b1d5ef9 100644 --- a/apps/openmw/mwscript/ref.hpp +++ b/apps/openmw/mwscript/ref.hpp @@ -6,9 +6,9 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" -#include "../mwworld/world.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index c2b45641a..3e05d72d3 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -8,8 +8,7 @@ #include #include "../mwbase/environment.hpp" - -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index ff618ac33..29563ecc1 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -9,10 +9,12 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" -#include "../mwworld/world.hpp" #include "../mwworld/player.hpp" +#include "../mwrender/player.hpp" + #include "sound_output.hpp" #include "sound_decoder.hpp" #include "sound.hpp" diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index f3bb256fd..52b9437fd 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -1,9 +1,11 @@ #include "actionequip.hpp" #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" -#include "../mwworld/inventorystore.hpp" -#include "../mwworld/player.hpp" +#include "../mwbase/world.hpp" + +#include "inventorystore.hpp" +#include "player.hpp" +#include "class.hpp" namespace MWWorld { @@ -14,7 +16,7 @@ namespace MWWorld void ActionEquip::execute () { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::InventoryStore& invStore = static_cast(MWWorld::Class::get(player).getContainerStore(player)); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); // slots that this item can be equipped in std::pair, bool> slots = MWWorld::Class::get(mObject).getEquipmentSlots(mObject); @@ -51,4 +53,3 @@ namespace MWWorld } } } - diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index dd36487dc..a70773af9 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -1,13 +1,14 @@ #include "actionopen.hpp" #include "../mwbase/environment.hpp" -#include "class.hpp" -#include "world.hpp" -#include "containerstore.hpp" + #include "../mwclass/container.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/container.hpp" +#include "class.hpp" +#include "containerstore.hpp" + namespace MWWorld { ActionOpen::ActionOpen (const MWWorld::Ptr& container) : mContainer (container) { diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 9cff42812..39544b35d 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -2,10 +2,11 @@ #include "actiontake.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwgui/window_manager.hpp" #include "class.hpp" -#include "world.hpp" #include "containerstore.hpp" namespace MWWorld diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 6ebbd7b7f..8ae3244f8 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -2,8 +2,7 @@ #include "actionteleport.hpp" #include "../mwbase/environment.hpp" - -#include "world.hpp" +#include "../mwbase/world.hpp" namespace MWWorld { diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 64290d9b7..e2c652ef4 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -4,7 +4,9 @@ #include -#include "world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "class.hpp" #include "containerstore.hpp" @@ -85,8 +87,8 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS return ptr; } -MWWorld::Cells::Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world) -: mStore (store), mReader (reader), mWorld (world), +MWWorld::Cells::Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader) +: mStore (store), mReader (reader), mIdCache (20, std::pair ("", 0)), /// \todo make cache size configurable mIdCacheIndex (0) {} @@ -111,11 +113,11 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) record.water = 0; record.mapColor = 0; - cell = mWorld.createRecord (record); + cell = MWBase::Environment::get().getWorld()->createRecord (record); } result = mExteriors.insert (std::make_pair ( - std::make_pair (x, y), Ptr::CellStore (cell))).first; + std::make_pair (x, y), CellStore (cell))).first; } if (result->second.mState!=Ptr::CellStore::State_Loaded) diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 05367138f..3e1383166 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -18,39 +18,36 @@ namespace ESM namespace MWWorld { - class World; - /// \brief Cell container class Cells { const ESMS::ESMStore& mStore; ESM::ESMReader& mReader; - std::map mInteriors; - std::map, Ptr::CellStore> mExteriors; - MWWorld::World& mWorld; - std::vector > mIdCache; + std::map mInteriors; + std::map, CellStore> mExteriors; + std::vector > mIdCache; std::size_t mIdCacheIndex; Cells (const Cells&); Cells& operator= (const Cells&); - Ptr::CellStore *getCellStore (const ESM::Cell *cell); + CellStore *getCellStore (const ESM::Cell *cell); - void fillContainers (Ptr::CellStore& cellStore); + void fillContainers (CellStore& cellStore); - Ptr getPtrAndCache (const std::string& name, Ptr::CellStore& cellStore); + Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); public: - Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world); + Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader); ///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole /// world - Ptr::CellStore *getExterior (int x, int y); + CellStore *getExterior (int x, int y); - Ptr::CellStore *getInterior (const std::string& name); + CellStore *getInterior (const std::string& name); - Ptr getPtr (const std::string& name, Ptr::CellStore& cellStore); + Ptr getPtr (const std::string& name, CellStore& cellStore); Ptr getPtr (const std::string& name); }; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8ce1b5c8f..1d473246c 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -10,7 +10,7 @@ #include #include "../mwbase/environment.hpp" -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" #include "manualref.hpp" #include "refdata.hpp" diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 6df73004b..b1f7a2e0d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -7,8 +7,7 @@ #include #include "../mwbase/environment.hpp" - -#include "../mwworld/world.hpp" +#include "../mwbase/world.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7c1ff31b4..4d16a6d1c 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -1,18 +1,19 @@ +#include "physicssystem.hpp" + #include -#include "physicssystem.hpp" -#include "../mwworld/ptr.hpp" -#include "../mwworld/world.hpp" // FIXME +#include +#include +#include +#include +#include +#include + #include -#include "OgreRoot.h" -#include "OgreRenderWindow.h" -#include "OgreSceneManager.h" -#include "OgreViewport.h" -#include "OgreCamera.h" -#include "OgreTextureManager.h" - +#include "../mwbase/world.hpp" // FIXME +#include "ptr.hpp" using namespace Ogre; namespace MWWorld diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 91b030d1c..3edcba0c8 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,18 +1,20 @@ #include "player.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwrender/player.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" -#include "world.hpp" #include "class.hpp" namespace MWWorld { - Player::Player (MWRender::Player *renderer, const ESM::NPC *player, MWWorld::World& world) : - mCellStore (0), mRenderer (renderer), mWorld (world), mClass (0), + Player::Player (MWRender::Player *renderer, const ESM::NPC *player, const MWBase::World& world) : + mCellStore (0), mRenderer (renderer), mClass (0), mAutoMove (false), mForwardBackward (0) { mPlayer.base = player; @@ -36,8 +38,8 @@ namespace MWWorld void Player::setPos(float x, float y, float z) { - /// \todo This fcuntion should be removed during the mwrender-refactoring. - mWorld.moveObject (getPlayer(), x, y, z); + /// \todo This fcuntion should be removed during the mwrender-refactoring. + MWBase::Environment::get().getWorld()->moveObject (getPlayer(), x, y, z); } void Player::setRot(float x, float y, float z) @@ -90,14 +92,13 @@ namespace MWWorld MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; } - void Player::setUpDown(int value) - { - MWWorld::Ptr ptr = getPlayer(); - + void Player::setUpDown(int value) + { + MWWorld::Ptr ptr = getPlayer(); MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value; - } + } void Player::toggleRunning() { diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index c8f60a2ee..7521342cf 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -9,6 +9,11 @@ #include "../mwmechanics/drawstate.hpp" +namespace MWBase +{ + class World; +} + namespace MWRender { class Player; @@ -16,15 +21,12 @@ namespace MWRender namespace MWWorld { - class World; - /// \brief NPC object representing the player and additional player data class Player { LiveCellRef mPlayer; MWWorld::Ptr::CellStore *mCellStore; MWRender::Player *mRenderer; - MWWorld::World& mWorld; std::string mName; bool mMale; std::string mRace; @@ -34,7 +36,7 @@ namespace MWWorld int mForwardBackward; public: - Player(MWRender::Player *renderer, const ESM::NPC *player, MWWorld::World& world); + Player(MWRender::Player *renderer, const ESM::NPC *player, const MWBase::World& world); ~Player(); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bbcbbf2af..0ccb5218e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,7 +1,7 @@ #include "scene.hpp" -#include "world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" /// FIXME #include "../mwmechanics/mechanicsmanager.hpp" @@ -9,12 +9,12 @@ #include "../mwgui/window_manager.hpp" -#include "../mwworld/world.hpp" /// FIXME #include "../mwworld/manualref.hpp" /// FIXME #include "ptr.hpp" #include "player.hpp" #include "class.hpp" +#include "localscripts.hpp" #include "cellfunctors.hpp" @@ -86,7 +86,7 @@ namespace MWWorld if (!((*iter)->cell->data.flags & ESM::Cell::Interior)) { - ESM::Land* land = mWorld->getStore().lands.search((*iter)->cell->data.gridX,(*iter)->cell->data.gridY); + ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().lands.search((*iter)->cell->data.gridX,(*iter)->cell->data.gridY); if (land) mPhysics->removeHeightField( (*iter)->cell->data.gridX, (*iter)->cell->data.gridY ); } @@ -95,7 +95,7 @@ namespace MWWorld mRendering.removeCell(*iter); //mPhysics->removeObject("Unnamed_43"); - mWorld->getLocalScripts().clearCell (*iter); + MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); MWBase::Environment::get().getMechanicsManager()->dropActors (*iter); MWBase::Environment::get().getSoundManager()->stopSound (*iter); mActiveCells.erase(*iter); @@ -107,7 +107,7 @@ namespace MWWorld void Scene::loadCell (Ptr::CellStore *cell) { // register local scripts - mWorld->getLocalScripts().addCell (cell); + MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell); @@ -124,7 +124,7 @@ namespace MWWorld if (!(cell->cell->data.flags & ESM::Cell::Interior)) { - ESM::Land* land = mWorld->getStore().lands.search(cell->cell->data.gridX,cell->cell->data.gridY); + ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().lands.search(cell->cell->data.gridX,cell->cell->data.gridY); if (land) mPhysics->addHeightField (land->landData->heights, cell->cell->data.gridX, cell->cell->data.gridY, @@ -146,14 +146,14 @@ namespace MWWorld mPhysics->setCurrentWater(hasWater, cell->cell->water); if (adjustPlayerPos) { - mWorld->getPlayer().setPos (position.pos[0], position.pos[1], position.pos[2]); - mWorld->getPlayer().setRot (position.rot[0], position.rot[1], position.rot[2]); + MWBase::Environment::get().getWorld()->getPlayer().setPos (position.pos[0], position.pos[1], position.pos[2]); + MWBase::Environment::get().getWorld()->getPlayer().setRot (position.rot[0], position.rot[1], position.rot[2]); } - mWorld->getPlayer().setCell (cell); + MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); - MWBase::Environment::get().getMechanicsManager()->addActor (mWorld->getPlayer().getPlayer()); - MWBase::Environment::get().getMechanicsManager()->watchActor (mWorld->getPlayer().getPlayer()); + MWBase::Environment::get().getMechanicsManager()->addActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWBase::Environment::get().getMechanicsManager()->watchActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); MWBase::Environment::get().getWindowManager()->changeCell( mCurrentCell ); } @@ -163,7 +163,7 @@ namespace MWWorld mRendering.preCellChange(mCurrentCell); // remove active - MWBase::Environment::get().getMechanicsManager()->removeActor (mWorld->getPlayer().getPlayer()); + MWBase::Environment::get().getMechanicsManager()->removeActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); CellStoreCollection::iterator active = mActiveCells.begin(); @@ -202,7 +202,7 @@ namespace MWWorld if (iter==mActiveCells.end()) { - Ptr::CellStore *cell = mWorld->getExterior(x, y); + CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); loadCell (cell); } @@ -228,10 +228,10 @@ namespace MWWorld // adjust player - playerCellChange (mWorld->getExterior(X, Y), position, adjustPlayerPos); + playerCellChange (MWBase::Environment::get().getWorld()->getExterior(X, Y), position, adjustPlayerPos); // Sky system - mWorld->adjustSky(); + MWBase::Environment::get().getWorld()->adjustSky(); mRendering.switchToExterior(); @@ -239,9 +239,8 @@ namespace MWWorld } //We need the ogre renderer and a scene node. - Scene::Scene (World *world, MWRender::RenderingManager& rendering, PhysicsSystem *physics) - : mCurrentCell (0), mCellChanged (false), mWorld(world), - mPhysics(physics), mRendering(rendering) + Scene::Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics) + : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) { } @@ -263,7 +262,7 @@ namespace MWWorld { std::cout << "Changing to interior\n"; - Ptr::CellStore *cell = mWorld->getInterior(cellName); + CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); // remove active CellStoreCollection::iterator active = mActiveCells.begin(); @@ -287,7 +286,7 @@ namespace MWWorld mRendering.configureFog(*cell); // Sky system - mWorld->adjustSky(); + MWBase::Environment::get().getWorld()->adjustSky(); mCellChanged = true; } @@ -297,7 +296,7 @@ namespace MWWorld int x = 0; int y = 0; - mWorld->positionToIndex (position.pos[0], position.pos[1], x, y); + MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); changeCell (x, y, position, true); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 373b20ed5..52449a24d 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -59,7 +59,6 @@ namespace MWWorld Ptr::CellStore* mCurrentCell; // the cell, the player is in CellStoreCollection mActiveCells; bool mCellChanged; - World *mWorld; PhysicsSystem *mPhysics; MWRender::RenderingManager& mRendering; @@ -69,7 +68,7 @@ namespace MWWorld public: - Scene (World *world, MWRender::RenderingManager& rendering, PhysicsSystem *physics); + Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics); ~Scene(); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 3d6547d6a..21f991050 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,9 +1,4 @@ #include "weather.hpp" -#include "world.hpp" -#include "player.hpp" - -#include "../mwrender/renderingmanager.hpp" -#include "../mwsound/soundmanager.hpp" #include #include @@ -12,6 +7,13 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwrender/renderingmanager.hpp" + +#include "../mwsound/soundmanager.hpp" + +#include "player.hpp" using namespace Ogre; using namespace MWWorld; diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/worldimp.cpp similarity index 99% rename from apps/openmw/mwworld/world.cpp rename to apps/openmw/mwworld/worldimp.cpp index b0067eabf..6f9f9936c 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,4 +1,4 @@ -#include "world.hpp" +#include "worldimp.hpp" #include #include @@ -179,7 +179,7 @@ namespace MWWorld const std::string& master, const boost::filesystem::path& resDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm, *this), + mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm), mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); @@ -211,7 +211,7 @@ namespace MWWorld mGlobalVariables->setInt ("chargenstate", 1); } - mWorldScene = new Scene(this, *mRendering, mPhysics); + mWorldScene = new Scene(*mRendering, mPhysics); setFallbackValues(fallbackMap); diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/worldimp.hpp similarity index 99% rename from apps/openmw/mwworld/world.hpp rename to apps/openmw/mwworld/worldimp.hpp index 526a2d7a6..b38bc6e2a 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWWORLD_WORLD_H -#define GAME_MWWORLD_WORLD_H +#ifndef GAME_MWWORLD_WORLDIMP_H +#define GAME_MWWORLD_WORLDIMP_H #include #include diff --git a/libs/openengine/bullet/CMotionState.cpp b/libs/openengine/bullet/CMotionState.cpp index d7746cbc5..dc28d9e5f 100644 --- a/libs/openengine/bullet/CMotionState.cpp +++ b/libs/openengine/bullet/CMotionState.cpp @@ -4,7 +4,6 @@ #include #include #include -//#include namespace OEngine { namespace Physic diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 1bc4341a0..500e8a7ef 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -3,7 +3,6 @@ #include #include #include -//#include #include "CMotionState.h" #include "OgreRoot.h" #include "btKinematicCharacterController.h" @@ -286,7 +285,7 @@ namespace Physic minh = h; maxh = h; } - + if (h>maxh) maxh = h; if (h #include #include -//#include #include #include - + enum traceWorldType { collisionWorldTrace = 1, @@ -33,7 +32,7 @@ struct NewPhysTraceResults float fraction; bool startSolid; //const Object* hitObj; -}; +}; struct traceResults { Ogre::Vector3 endpos; From 5a7b95bf7ac94201f9ac38dd92da0867a097f850 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Jul 2012 13:15:20 +0200 Subject: [PATCH 039/688] Issue #107: cleaned up includes in apps/openmw/mwworld/class.hpp --- apps/openmw/mwclass/activator.cpp | 3 +++ apps/openmw/mwclass/apparatus.cpp | 2 ++ apps/openmw/mwclass/armor.cpp | 3 +++ apps/openmw/mwclass/book.cpp | 3 +++ apps/openmw/mwclass/clothing.cpp | 2 ++ apps/openmw/mwclass/container.cpp | 4 +++- apps/openmw/mwclass/creature.cpp | 4 ++++ apps/openmw/mwclass/door.cpp | 2 ++ apps/openmw/mwclass/ingredient.cpp | 2 ++ apps/openmw/mwclass/light.cpp | 2 ++ apps/openmw/mwclass/lockpick.cpp | 2 ++ apps/openmw/mwclass/misc.cpp | 2 ++ apps/openmw/mwclass/npc.cpp | 3 +++ apps/openmw/mwclass/potion.cpp | 2 ++ apps/openmw/mwclass/probe.cpp | 2 ++ apps/openmw/mwclass/repair.cpp | 2 ++ apps/openmw/mwclass/static.cpp | 2 ++ apps/openmw/mwclass/weapon.cpp | 2 ++ apps/openmw/mwgui/alchemywindow.hpp | 1 + apps/openmw/mwgui/hud.cpp | 2 ++ apps/openmw/mwrender/npcanimation.cpp | 2 ++ apps/openmw/mwworld/cells.cpp | 2 ++ apps/openmw/mwworld/class.cpp | 2 ++ apps/openmw/mwworld/class.hpp | 13 +++++++------ apps/openmw/mwworld/inventorystore.cpp | 2 ++ apps/openmw/mwworld/player.cpp | 2 ++ apps/openmw/mwworld/weather.cpp | 3 ++- apps/openmw/mwworld/worldimp.hpp | 2 ++ 28 files changed, 67 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index d8b8d54e8..81a47ccb0 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -7,10 +7,13 @@ #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwgui/window_manager.hpp" +#include "../mwgui/tooltips.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 55f5bc11f..7e3c3b8f9 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -10,8 +10,10 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/actionalchemy.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 9e029d005..380c596d7 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -13,10 +13,13 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwgui/window_manager.hpp" +#include "../mwgui/tooltips.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 780e2e0f5..a37da0fd7 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -9,10 +9,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwgui/window_manager.hpp" +#include "../mwgui/tooltips.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 04a1f3ee6..6c34b5e56 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -11,11 +11,13 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/tooltips.hpp" #include "../mwgui/window_manager.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 0c8682df4..8dd27db42 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -11,12 +11,14 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/actionopen.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" -#include "../mwworld/actionopen.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index ff96692d0..a5a4f337a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -13,8 +13,12 @@ #include "../mwworld/actiontalk.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/physicssystem.hpp" + +#include "../mwrender/renderinginterface.hpp" #include "../mwgui/window_manager.hpp" +#include "../mwgui/tooltips.hpp" namespace { diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 9148d3575..b0bba2c03 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -11,11 +11,13 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/actionteleport.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bc68e46b2..01146fe67 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -9,11 +9,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 9fa11be5e..15cd89ac2 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -12,6 +12,7 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -19,6 +20,7 @@ #include "../mwsound/soundmanager.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index b35172f2f..d3d60315f 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -11,11 +11,13 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 1163b43b2..8484a5dd1 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -11,11 +11,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e1b8541a1..d4f711885 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -21,10 +21,13 @@ #include "../mwworld/actiontalk.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwrender/actors.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwgui/window_manager.hpp" +#include "../mwgui/tooltips.hpp" namespace { diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index cd393f388..5b446fbe9 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -9,11 +9,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index a8bfdead1..f3a8406f5 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -11,11 +11,13 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 4a4550ba1..464ba1091 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -9,11 +9,13 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index dbc697f8b..9b166b076 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -4,8 +4,10 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 90f98afb9..099312d2c 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -11,11 +11,13 @@ #include "../mwworld/actionequip.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/physicssystem.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" #include "../mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index c01a18e41..81c33a96d 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -3,6 +3,7 @@ #include "window_base.hpp" #include "container.hpp" +#include "widgets.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index ebba1661b..8762a9b7a 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -14,6 +14,8 @@ #include "../mwsound/soundmanager.hpp" +#include "../mwgui/widgets.hpp" + #include "inventorywindow.hpp" #include "window_manager.hpp" #include "container.hpp" diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 888b11e1e..6fc61904c 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1,5 +1,7 @@ #include "npcanimation.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index e2c652ef4..74a91f25f 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -4,6 +4,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index fe39406fe..13b40ddbb 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -9,6 +9,8 @@ #include "nullaction.hpp" #include "containerstore.hpp" +#include "../mwgui/tooltips.hpp" + namespace MWWorld { std::map > Class::sClasses; diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 46781d516..fe7b5dc80 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -8,11 +8,6 @@ #include #include "action.hpp" -#include "refdata.hpp" -#include "physicssystem.hpp" - -#include "../mwrender/renderinginterface.hpp" -#include "../mwgui/tooltips.hpp" namespace Ogre { @@ -21,7 +16,7 @@ namespace Ogre namespace MWRender { - class CellRenderImp; + class RenderingInterface; } namespace MWMechanics @@ -31,11 +26,17 @@ namespace MWMechanics struct Movement; } +namespace MWGui +{ + struct ToolTipInfo; +} + namespace MWWorld { class Ptr; class ContainerStore; class InventoryStore; + class PhysicsSystem; /// \brief Base class for referenceable esm records class Class diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index b1f7a2e0d..9d99227ed 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -6,6 +6,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 3edcba0c8..0eeebad4e 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -1,6 +1,8 @@ #include "player.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 21f991050..6c275ff80 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -2,10 +2,11 @@ #include #include -#include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b38bc6e2a..d64b3a373 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -6,6 +6,8 @@ #include +#include + #include #include "../mwrender/debugging.hpp" From 87667ab57e55481255a45e25ddefe3235bc2f674 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Jul 2012 13:55:53 +0200 Subject: [PATCH 040/688] Issue #107: Ptr related include cleanup --- apps/openmw/mwgui/referenceinterface.hpp | 2 +- apps/openmw/mwgui/window_manager.cpp | 8 ++++--- apps/openmw/mwgui/window_manager.hpp | 8 +++---- apps/openmw/mwmechanics/actors.hpp | 10 ++++++--- apps/openmw/mwmechanics/mechanicsmanager.hpp | 7 +++++- apps/openmw/mwrender/actors.hpp | 12 +++++++--- apps/openmw/mwrender/debugging.hpp | 19 ++++++++++------ apps/openmw/mwrender/localmap.hpp | 15 ++++++++----- apps/openmw/mwrender/objects.cpp | 3 +++ apps/openmw/mwrender/objects.hpp | 23 ++++++++++++-------- apps/openmw/mwrender/renderingmanager.hpp | 21 +++++++++--------- apps/openmw/mwrender/terrain.cpp | 2 ++ apps/openmw/mwrender/terrain.hpp | 14 ++++++++---- apps/openmw/mwsound/soundmanager.hpp | 2 +- apps/openmw/mwworld/localscripts.hpp | 6 +++-- apps/openmw/mwworld/player.hpp | 6 +++-- apps/openmw/mwworld/scene.cpp | 2 +- apps/openmw/mwworld/scene.hpp | 17 +++++++-------- 18 files changed, 111 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwgui/referenceinterface.hpp b/apps/openmw/mwgui/referenceinterface.hpp index 40844b238..aba9071f3 100644 --- a/apps/openmw/mwgui/referenceinterface.hpp +++ b/apps/openmw/mwgui/referenceinterface.hpp @@ -22,7 +22,7 @@ namespace MWGui MWWorld::Ptr mPtr; private: - MWWorld::Ptr::CellStore* mCurrentPlayerCell; + MWWorld::CellStore* mCurrentPlayerCell; }; } diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index f62a3fa0f..7a8b72886 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -26,14 +26,16 @@ #include "../mwbase/environment.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/cellstore.hpp" + #include "console.hpp" #include "journalwindow.hpp" #include "charactercreation.hpp" #include -#include -#include +#include #include using namespace MWGui; @@ -82,7 +84,7 @@ WindowManager::WindowManager( // Set up the GUI system mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath); gui = mGuiManager->getGui(); - + //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index fd1e61021..03ffa6b59 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -22,7 +22,6 @@ #include #include "../mwmechanics/stat.hpp" -#include "../mwworld/ptr.hpp" #include "mode.hpp" @@ -39,7 +38,8 @@ namespace Compiler namespace MWWorld { - class World; + class Ptr; + class CellStore; } namespace MWMechanics @@ -181,7 +181,7 @@ namespace MWGui void setBounty (int bounty); ///< set the current bounty value void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty - void changeCell(MWWorld::Ptr::CellStore* cell); ///< change the active cell + void changeCell(MWWorld::CellStore* cell); ///< change the active cell void setPlayerPos(const float x, const float y); ///< set player position in map space void setPlayerDir(const float x, const float y); ///< set player view direction in map space @@ -302,7 +302,7 @@ namespace MWGui /** * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, - * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result + * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result */ void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); }; diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 1be29463f..82f8a943c 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -5,13 +5,17 @@ #include #include -#include "../mwworld/ptr.hpp" - namespace Ogre { class Vector3; } +namespace MWWorld +{ + class Ptr; + class CellStore; +} + namespace MWMechanics { class Actors @@ -35,7 +39,7 @@ namespace MWMechanics /// /// \note Ignored, if \a ptr is not a registered actor. - void dropActors (const MWWorld::Ptr::CellStore *cellStore); + void dropActors (const MWWorld::CellStore *cellStore); ///< Deregister all actors in the given cell. void update (std::vector >& movement, diff --git a/apps/openmw/mwmechanics/mechanicsmanager.hpp b/apps/openmw/mwmechanics/mechanicsmanager.hpp index 62bb4cf7e..97bb369fd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.hpp @@ -15,6 +15,11 @@ namespace Ogre class Vector3; } +namespace MWWorld +{ + class CellStore; +} + namespace MWMechanics { class MechanicsManager @@ -43,7 +48,7 @@ namespace MWMechanics void removeActor (const MWWorld::Ptr& ptr); ///< Deregister an actor for stats management - void dropActors (const MWWorld::Ptr::CellStore *cellStore); + void dropActors (const MWWorld::CellStore *cellStore); ///< Deregister all actors in the given cell. void watchActor (const MWWorld::Ptr& ptr); diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index c66492780..63cd3baa1 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -8,16 +8,22 @@ #include "components/nifogre/ogre_nif_loader.hpp" #include "../mwworld/refdata.hpp" -#include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" + #include "npcanimation.hpp" #include "creatureanimation.hpp" #include +namespace MWWorld +{ + class Ptr; + class CellStore; +} + namespace MWRender{ class Actors{ OEngine::Render::OgreRenderer &mRend; - std::map mCellSceneNodes; + std::map mCellSceneNodes; Ogre::SceneNode* mMwRoot; std::map mAllActors; @@ -33,7 +39,7 @@ namespace MWRender{ bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? - void removeCell(MWWorld::Ptr::CellStore* store); + void removeCell(MWWorld::CellStore* store); void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number = 1); diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index e12c0647c..d312b6d54 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -4,11 +4,15 @@ #include #include #include -#include "../mwworld/ptr.hpp" #include #include +namespace ESM +{ + struct Pathgrid; +} + namespace Ogre { class Camera; @@ -22,7 +26,8 @@ namespace Ogre namespace MWWorld { - class World; + class Ptr; + class CellStore; } namespace MWRender @@ -39,7 +44,7 @@ namespace MWRender void togglePathgrid(); - typedef std::vector CellList; + typedef std::vector CellList; CellList mActiveCells; Ogre::SceneNode *mMwRoot; @@ -50,8 +55,8 @@ namespace MWRender ExteriorPathgridNodes mExteriorPathgridNodes; Ogre::SceneNode *mInteriorPathgridNode; - void enableCellPathgrid(MWWorld::Ptr::CellStore *store); - void disableCellPathgrid(MWWorld::Ptr::CellStore *store); + void enableCellPathgrid(MWWorld::CellStore *store); + void disableCellPathgrid(MWWorld::CellStore *store); // utility void destroyCellPathgridNode(Ogre::SceneNode *node); @@ -70,8 +75,8 @@ namespace MWRender ~Debugging(); bool toggleRenderMode (int mode); - void cellAdded(MWWorld::Ptr::CellStore* store); - void cellRemoved(MWWorld::Ptr::CellStore* store); + void cellAdded(MWWorld::CellStore* store); + void cellRemoved(MWWorld::CellStore* store); }; diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index 9e03988f3..c5cd908fc 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -1,10 +1,15 @@ #ifndef _GAME_RENDER_LOCALMAP_H #define _GAME_RENDER_LOCALMAP_H -#include "../mwworld/ptr.hpp" - #include +#include + +namespace MWWorld +{ + class CellStore; +} + namespace MWRender { class RenderingManager; @@ -24,7 +29,7 @@ namespace MWRender * or rendered if it is not already cached. * @param exterior cell */ - void requestMap (MWWorld::Ptr::CellStore* cell); + void requestMap (MWWorld::CellStore* cell); /** * Request the local map for an interior cell. @@ -33,7 +38,7 @@ namespace MWRender * @param interior cell * @param bounding box of the cell */ - void requestMap (MWWorld::Ptr::CellStore* cell, + void requestMap (MWWorld::CellStore* cell, Ogre::AxisAlignedBox bounds); /** @@ -51,7 +56,7 @@ namespace MWRender * new cell, as well as when the game is quit. * @param current cell */ - void saveFogOfWar(MWWorld::Ptr::CellStore* cell); + void saveFogOfWar(MWWorld::CellStore* cell); private: OEngine::Render::OgreRenderer* mRendering; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index c78a98ef5..03bb925c3 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -4,6 +4,9 @@ #include #include + +#include "../mwworld/ptr.hpp" + #include "renderconst.hpp" using namespace MWRender; diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 05634e4b0..e240b11c9 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -1,10 +1,15 @@ #ifndef _GAME_RENDER_OBJECTS_H #define _GAME_RENDER_OBJECTS_H +#include + #include -#include "../mwworld/refdata.hpp" -#include "../mwworld/ptr.hpp" +namespace MWWorld +{ + class Ptr; + class CellStore; +} namespace MWRender{ @@ -45,10 +50,10 @@ struct LightInfo class Objects{ OEngine::Render::OgreRenderer &mRenderer; - std::map mCellSceneNodes; - std::map mStaticGeometry; - std::map mStaticGeometrySmall; - std::map mBounds; + std::map mCellSceneNodes; + std::map mStaticGeometry; + std::map mStaticGeometrySmall; + std::map mBounds; std::vector mLights; Ogre::SceneNode* mMwRoot; bool mIsStatic; @@ -79,14 +84,14 @@ public: void update (const float dt); ///< per-frame update - Ogre::AxisAlignedBox getDimensions(MWWorld::Ptr::CellStore*); + Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*); ///< get a bounding box that encloses all objects in the specified cell bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? - void removeCell(MWWorld::Ptr::CellStore* store); - void buildStaticGeometry(MWWorld::Ptr::CellStore &cell); + void removeCell(MWWorld::CellStore* store); + void buildStaticGeometry(MWWorld::CellStore &cell); void setMwRoot(Ogre::SceneNode* root); }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2c1b21dd5..9aaba3803 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -20,8 +20,6 @@ #include #include -#include "../mwworld/ptr.hpp" - #include #include "renderinginterface.hpp" @@ -41,7 +39,8 @@ namespace Ogre namespace MWWorld { - class World; + class Ptr; + class CellStore; } namespace MWRender @@ -79,18 +78,18 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList OEngine::Render::Fader* getFader(); - void removeCell (MWWorld::Ptr::CellStore *store); + void removeCell (MWWorld::CellStore *store); /// \todo this function should be removed later. Instead the rendering subsystems should track /// when rebatching is needed and update automatically at the end of each frame. - void cellAdded (MWWorld::Ptr::CellStore *store); - void waterAdded(MWWorld::Ptr::CellStore *store); + void cellAdded (MWWorld::CellStore *store); + void waterAdded(MWWorld::CellStore *store); void removeWater(); static const bool useMRT(); - void preCellChange (MWWorld::Ptr::CellStore* store); + void preCellChange (MWWorld::CellStore* store); ///< this event is fired immediately before changing cell void addObject (const MWWorld::Ptr& ptr); @@ -105,7 +104,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void toggleWater(); /// \param store Cell the object was in previously (\a ptr has already been updated to the new cell). - void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store); + void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store); void update (float duration); @@ -136,13 +135,13 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList int skyGetMasserPhase() const; int skyGetSecundaPhase() const; void skySetMoonColour (bool red); - void configureAmbient(MWWorld::Ptr::CellStore &mCell); + void configureAmbient(MWWorld::CellStore &mCell); - void requestMap (MWWorld::Ptr::CellStore* cell); + void requestMap (MWWorld::CellStore* cell); ///< request the local map for a cell /// configure fog according to cell - void configureFog(MWWorld::Ptr::CellStore &mCell); + void configureFog(MWWorld::CellStore &mCell); /// configure fog manually void configureFog(const float density, const Ogre::ColourValue& colour); diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 4678daa34..cbc2f33b6 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 0693c0cec..7ac48047d 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -3,9 +3,10 @@ #include #include -#include "terrainmaterial.hpp" -#include "../mwworld/ptr.hpp" +#include + +#include "terrainmaterial.hpp" namespace Ogre{ class SceneManager; @@ -14,6 +15,11 @@ namespace Ogre{ class Terrain; } +namespace MWWorld +{ + class CellStore; +} + namespace MWRender{ class RenderingManager; @@ -32,8 +38,8 @@ namespace MWRender{ void setDiffuse(const Ogre::ColourValue& diffuse); void setAmbient(const Ogre::ColourValue& ambient); - void cellAdded(MWWorld::Ptr::CellStore* store); - void cellRemoved(MWWorld::Ptr::CellStore* store); + void cellAdded(MWWorld::CellStore* store); + void cellRemoved(MWWorld::CellStore* store); private: Ogre::TerrainGlobalOptions* mTerrainGlobals; Ogre::TerrainGroup mTerrainGroup; diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 562b2af00..ff360122b 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -130,7 +130,7 @@ namespace MWSound void stopSound3D(MWWorld::Ptr reference); ///< Stop the given object from playing all sounds. - void stopSound(const MWWorld::Ptr::CellStore *cell); + void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. void stopSound(const std::string& soundId); diff --git a/apps/openmw/mwworld/localscripts.hpp b/apps/openmw/mwworld/localscripts.hpp index 1ea2cf4d5..78f65e356 100644 --- a/apps/openmw/mwworld/localscripts.hpp +++ b/apps/openmw/mwworld/localscripts.hpp @@ -13,6 +13,8 @@ namespace ESMS namespace MWWorld { + class CellStore; + /// \brief List of active local scripts class LocalScripts { @@ -41,13 +43,13 @@ namespace MWWorld void add (const std::string& scriptName, const Ptr& ptr); ///< Add script to collection of active local scripts. - void addCell (Ptr::CellStore *cell); + void addCell (CellStore *cell); ///< Add all local scripts in a cell. void clear(); ///< Clear active local scripts collection. - void clearCell (Ptr::CellStore *cell); + void clearCell (CellStore *cell); ///< Remove all scripts belonging to \a cell. void remove (const Ptr& ptr); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 7521342cf..b1692bd81 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -21,11 +21,13 @@ namespace MWRender namespace MWWorld { + class CellStore; + /// \brief NPC object representing the player and additional player data class Player { LiveCellRef mPlayer; - MWWorld::Ptr::CellStore *mCellStore; + MWWorld::CellStore *mCellStore; MWRender::Player *mRenderer; std::string mName; bool mMale; @@ -46,7 +48,7 @@ namespace MWWorld /// Set where the player is looking at. Uses Morrowind (euler) angles void setRot(float x, float y, float z); - void setCell (MWWorld::Ptr::CellStore *cellStore) + void setCell (MWWorld::CellStore *cellStore) { mCellStore = cellStore; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 0ccb5218e..39496def3 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -338,7 +338,7 @@ namespace MWWorld /// \todo this whole code needs major clean up, and doesn't belong in this class. - void Scene::insertObject(MWWorld::Ptr ptr, Ptr::CellStore* cell) + void Scene::insertObject (const Ptr& ptr, CellStore* cell) { std::string type = ptr.getTypeName(); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 52449a24d..64a8c9f8e 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -12,8 +12,6 @@ #include "../mwrender/renderinginterface.hpp" #include "physicssystem.hpp" -#include "refdata.hpp" -#include "ptr.hpp" #include "globals.hpp" namespace Ogre @@ -45,24 +43,25 @@ namespace MWRender namespace MWWorld { class Player; + class CellStore; + class Ptr; class Scene { - public: - typedef std::set CellStoreCollection; + typedef std::set CellStoreCollection; private: //OEngine::Render::OgreRenderer& mRenderer; - Ptr::CellStore* mCurrentCell; // the cell, the player is in + CellStore* mCurrentCell; // the cell, the player is in CellStoreCollection mActiveCells; bool mCellChanged; PhysicsSystem *mPhysics; MWRender::RenderingManager& mRendering; - void playerCellChange (Ptr::CellStore *cell, const ESM::Position& position, + void playerCellChange (CellStore *cell, const ESM::Position& position, bool adjustPlayerPos = true); @@ -74,13 +73,13 @@ namespace MWWorld void unloadCell (CellStoreCollection::iterator iter); - void loadCell (Ptr::CellStore *cell); + void loadCell (CellStore *cell); void changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos); ///< Move from exterior to interior or from interior cell to a different /// interior cell. - Ptr::CellStore* getCurrentCell (); + CellStore* getCurrentCell (); const CellStoreCollection& getActiveCells () const; @@ -99,7 +98,7 @@ namespace MWWorld /// this method is only meant for dropping objects into the gameworld from a container /// and thus only handles object types that can be placed in a container - void insertObject(MWWorld::Ptr object, Ptr::CellStore* cell); + void insertObject (const Ptr& object, CellStore* cell); void update (float duration); From c85aaafac2fd07592d80de7636b471851ec85a27 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Jul 2012 15:32:38 +0200 Subject: [PATCH 041/688] more include cleanup (most removing Ogre.h) --- apps/openmw/mwgui/tooltips.cpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 2 ++ apps/openmw/mwrender/actors.cpp | 4 +-- apps/openmw/mwrender/animation.cpp | 5 +++ apps/openmw/mwrender/creatureanimation.cpp | 5 +++ apps/openmw/mwrender/debugging.cpp | 1 + apps/openmw/mwrender/localmap.cpp | 1 + apps/openmw/mwrender/npcanimation.cpp | 4 +++ apps/openmw/mwrender/objects.cpp | 5 +++ apps/openmw/mwrender/player.cpp | 7 ++++ apps/openmw/mwrender/player.hpp | 6 ++-- apps/openmw/mwrender/renderingmanager.cpp | 17 ++++++---- apps/openmw/mwrender/shaderhelper.cpp | 1 + apps/openmw/mwrender/sky.cpp | 7 ++++ apps/openmw/mwrender/terrain.cpp | 1 + apps/openmw/mwrender/water.cpp | 9 ++++++ apps/openmw/mwrender/water.hpp | 19 +++++++++-- apps/openmw/mwworld/refdata.cpp | 2 ++ apps/openmw/mwworld/refdata.hpp | 7 ++-- components/nif/data.hpp | 37 +++++++++++----------- components/nifogre/ogre_nif_loader.cpp | 18 ++++++++--- components/nifogre/ogre_nif_loader.hpp | 11 +++---- libs/openengine/bullet/BtOgre.cpp | 4 +++ libs/openengine/bullet/BtOgreGP.h | 5 ++- libs/openengine/bullet/pmove.h | 4 +-- 25 files changed, 136 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index e79436adc..34dec2d0b 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -2,6 +2,8 @@ #include +#include + #include #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8f8fd6871..95747dbbf 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -3,6 +3,8 @@ #include +#include + #include #include "../mwworld/class.hpp" diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 152cf3277..b37921b0c 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -1,7 +1,7 @@ #include "actors.hpp" + #include - - +#include using namespace Ogre; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index fc6258208..f0a6ab683 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1,5 +1,10 @@ #include "animation.hpp" +#include +#include +#include +#include +#include namespace MWRender{ std::map Animation::mUniqueIDs; diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index a19d411a4..e1fa7868c 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,4 +1,9 @@ #include "creatureanimation.hpp" + +#include +#include +#include + #include "renderconst.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 5fe59f5e5..91b217a36 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 9d38f14da..dd0351721 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6fc61904c..fa88b7277 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1,5 +1,9 @@ #include "npcanimation.hpp" +#include +#include +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 03bb925c3..fb2bfb3c5 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -1,6 +1,11 @@ #include "objects.hpp" #include +#include +#include +#include +#include +#include #include #include diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index d6baac4b5..27ae28e14 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -1,6 +1,8 @@ #include "player.hpp" +#include + namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) @@ -24,4 +26,9 @@ namespace MWRender pitchNode->setOrientation(xr); yawNode->setOrientation(yr); } + + std::string Player::getHandle() const + { + return mNode->getName(); + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 406bedb0a..0ba936f6f 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -1,12 +1,12 @@ #ifndef GAME_MWRENDER_PLAYER_H #define GAME_MWRENDER_PLAYER_H -#include -#include +#include namespace Ogre { class Camera; + class SceneNode; } namespace MWRender @@ -26,7 +26,7 @@ namespace MWRender /// Set where the player is looking at. Uses Morrowind (euler) angles void setRot(float x, float y, float z); - std::string getHandle() const { return mNode->getName(); } + std::string getHandle() const; Ogre::SceneNode* getNode() {return mNode;} }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7ca5f8982..5b76f5ae2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -2,12 +2,17 @@ #include -#include "OgreRoot.h" -#include "OgreRenderWindow.h" -#include "OgreSceneManager.h" -#include "OgreViewport.h" -#include "OgreCamera.h" -#include "OgreTextureManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/apps/openmw/mwrender/shaderhelper.cpp b/apps/openmw/mwrender/shaderhelper.cpp index 59154a295..1f9086db4 100644 --- a/apps/openmw/mwrender/shaderhelper.cpp +++ b/apps/openmw/mwrender/shaderhelper.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index cf4386793..45e48240b 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -4,9 +4,16 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index cbc2f33b6..f14db34e6 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -2,6 +2,7 @@ #include #include +#include #include diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index dc9e1fbee..94fb3d6d9 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -1,5 +1,14 @@ #include "water.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + #include "sky.hpp" #include "renderingmanager.hpp" #include "compositors.hpp" diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 4d617ea47..71f261f2b 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -1,19 +1,34 @@ #ifndef GAME_MWRENDER_WATER_H #define GAME_MWRENDER_WATER_H -#include +#include +#include +#include +#include +#include +#include + #include #include #include "renderconst.hpp" +namespace Ogre +{ + class Camera; + class SceneManager; + class SceneNode; + class Entity; + class Vector3; + struct RenderTargetEvent; +}; namespace MWRender { class SkyManager; class RenderingManager; - /// Water rendering + /// Water rendering class Water : public Ogre::RenderTargetListener, public Ogre::RenderQueueListener { static const int CELL_SIZE = 8192; diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index a8aa058bc..14ddd3ec5 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -1,6 +1,8 @@ #include "refdata.hpp" +#include + #include "customdata.hpp" #include "cellstore.hpp" diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 30d676f13..41521e47a 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -3,12 +3,15 @@ #include -#include - #include #include "../mwscript/locals.hpp" +namespace Ogre +{ + class SceneNode; +} + namespace ESM { class Script; diff --git a/components/nif/data.hpp b/components/nif/data.hpp index df9079758..10337d2c9 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -25,8 +25,9 @@ #define _NIF_DATA_H_ #include "controlled.hpp" -#include -#include + +#include +#include namespace Nif { @@ -466,7 +467,7 @@ public: std::vector > getAdditionalVertices(){ return additionalVertices; } - + void read(NIFFile *nif) { int morphCount = nif->getInt(); @@ -481,7 +482,7 @@ void read(NIFFile *nif) float z = nif->getFloat(); initialVertices.push_back(Ogre::Vector3(x, y, z)); } - + for(int i=1; igetInt(); @@ -543,8 +544,8 @@ class NiKeyframeData : public Record std::vector tbcscale; int stype; - - + + public: void clone(NiKeyframeData c) { @@ -559,11 +560,11 @@ public: translist1 = c.getTranslist1(); - translist2 = c.getTranslist2(); + translist2 = c.getTranslist2(); translist3 = c.getTranslist3(); transtime = c.gettTime(); - + bonename = c.getBonename(); @@ -611,7 +612,7 @@ public: Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); quats.push_back(quat); rottime.push_back(time); - //if(time == 0.0 || time > 355.5) + //if(time == 0.0 || time > 355.5) // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; } } @@ -632,7 +633,7 @@ public: quats.push_back(quat); rottime.push_back(time); tbc.push_back(vec); - //if(time == 0.0 || time > 355.5) + //if(time == 0.0 || time > 355.5) // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; } @@ -661,7 +662,7 @@ public: // Then translation count = nif->getInt(); - + if(count) { ttype = nif->getInt(); @@ -700,7 +701,7 @@ public: translist2.push_back(trans2); translist3.push_back(trans3); } - + //nif->getFloatLen(count*10); // trans1 + forward + backward } else if(ttype == 3){ @@ -729,12 +730,12 @@ public: { stype = nif->getInt(); - + for(int i = 0; i < count; i++){ - - + + //int size = 0; - if(stype >= 1 && stype < 4) + if(stype >= 1 && stype < 4) { float time = nif->getFloat(); float scale = nif->getFloat(); @@ -759,10 +760,10 @@ public: //size = 5; // 1 + tbc } - + } } - else + else stype = 0; } int getRtype(){ diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 331701c2a..895c51a0d 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -25,6 +25,14 @@ #include "ogre_nif_loader.hpp" +#include +#include +#include +#include +#include +#include +#include + #include #include @@ -230,7 +238,7 @@ void NIFLoader::createMaterial(const String &name, //tech->setSchemeName("blahblah"); Pass* pass = tech->createPass(); pass->setVertexProgram("Ogre/BasicVertexPrograms/AmbientOneTexture");*/ - + // This assigns the texture to this material. If the texture name is // a file name, and this file exists (in a resource directory), it @@ -478,7 +486,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std bind->setBinding(nextBuf++, vbuf); } - + // Vertex colors if (data->colors.length) { @@ -795,7 +803,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou std::list vertexBoneAssignments; Nif::NiTriShapeCopy copy = shape->clone(); - + if(!shape->controller.empty()) { Nif::Controller* cont = shape->controller.getPtr(); @@ -984,7 +992,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou } if(!mSkel.isNull() ){ int boneIndex; - + boneIndex = mSkel->getNumBones() - 1; for(int i = 0; i < numVerts; i++){ VertexBoneAssignment vba; @@ -1388,7 +1396,7 @@ void NIFLoader::loadResource(Resource *resource) vba.boneIndex = boneIndex; vba.vertexIndex = 0; vba.weight = 1; - + (*iter)->addBoneAssignment(vba); } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 794459e46..d73948fa8 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -26,12 +26,10 @@ #include #include -#include + +#include #include -#include -#include -#include -#include +#include #include #include "../nif/nif_file.hpp" @@ -45,7 +43,6 @@ #include #include // For warning messages -#include #include using namespace boost::algorithm; @@ -193,7 +190,7 @@ class NIFLoader : Ogre::ManualResourceLoader std::vector mS; std::vector needBoneAssignments; bool inTheSkeletonTree; - + }; diff --git a/libs/openengine/bullet/BtOgre.cpp b/libs/openengine/bullet/BtOgre.cpp index 618739083..f50465159 100644 --- a/libs/openengine/bullet/BtOgre.cpp +++ b/libs/openengine/bullet/BtOgre.cpp @@ -17,6 +17,10 @@ #include "BtOgreGP.h" #include "BtOgreExtras.h" +#include +#include +#include + using namespace Ogre; namespace BtOgre { diff --git a/libs/openengine/bullet/BtOgreGP.h b/libs/openengine/bullet/BtOgreGP.h index f0534de4b..4ce2f181e 100644 --- a/libs/openengine/bullet/BtOgreGP.h +++ b/libs/openengine/bullet/BtOgreGP.h @@ -19,7 +19,10 @@ #include "btBulletDynamicsCommon.h" #include "BtOgreExtras.h" -#include "Ogre.h" + +#include +#include +#include namespace BtOgre { diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index 6cedd3599..0fdb8f0d8 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -6,12 +6,12 @@ which was released under the GNU GPL (v2) in 2005. Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. */ -#include #include #include #include "trace.h" #include "physic.hpp" +#include //#include "GameMath.h" //#include "GameTime.h" @@ -154,7 +154,7 @@ struct playerMove KEYUP }; - playercmd() : forwardmove(0), rightmove(0), upmove(0), serverTime(50), ducking(false), + playercmd() : forwardmove(0), rightmove(0), upmove(0), serverTime(50), ducking(false), activating(false), lastActivatingState(false), procActivating(NO_CHANGE), dropping(false), lastDroppingState(false), procDropping(NO_CHANGE) { From 205ed0ce5b327ba828ef37bdf398798c7070a6b6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 3 Jul 2012 15:53:42 +0200 Subject: [PATCH 042/688] post merge fix --- apps/openmw/mwscript/statsextensions.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 7f749185c..15519b3a6 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -3,6 +3,8 @@ #include +#include + #include #include From 085891879cde4ac00bcafbe1249fc25cc1ff595d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Jul 2012 16:44:20 +0200 Subject: [PATCH 043/688] fixed another resolution change bug introduced recently --- apps/openmw/mwgui/settingswindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 5bb57359b..5e25d5ece 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -49,7 +49,7 @@ namespace void parseResolution (int &x, int &y, const std::string& str) { std::vector split; - boost::algorithm::split (split, str, boost::is_any_of("x@")); + boost::algorithm::split (split, str, boost::is_any_of("@(x")); assert (split.size() >= 2); boost::trim(split[0]); boost::trim(split[1]); From 78efc31641cf3af5ba623c0d22648c41fff921c6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Jul 2012 16:51:29 +0200 Subject: [PATCH 044/688] different default coordinates for container window to prevent it from going invsible when changing resolution (bug #308), fixed some windows not centering when resolution change --- apps/openmw/mwgui/container.cpp | 5 +---- apps/openmw/mwgui/window_manager.cpp | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 27f024944..44cb12d2d 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -616,12 +616,9 @@ ContainerWindow::ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dr mCloseButton->setCoord(600-20-closeButtonWidth, mCloseButton->getCoord().top, closeButtonWidth, mCloseButton->getCoord().height); mTakeButton->setCoord(600-20-closeButtonWidth-takeButtonWidth-8, mTakeButton->getCoord().top, takeButtonWidth, mTakeButton->getCoord().height); - int w = MyGUI::RenderManager::getInstance().getViewSize().width; - //int h = MyGUI::RenderManager::getInstance().getViewSize().height; - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ContainerWindow::onWindowResize); - setCoord(w-600,0,600,300); + setCoord(200,0,600,300); } ContainerWindow::~ContainerWindow() diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 7a8b72886..62207d7d7 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -640,6 +640,9 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector hud->onResChange(x, y); console->onResChange(x, y); mSettingsWindow->center(); + mAlchemyWindow->center(); + mScrollWindow->center(); + mBookWindow->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); } } From 24399a45c117550620d2028fa572fd2dbac330cf Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Jul 2012 22:27:22 +0200 Subject: [PATCH 045/688] add submodule with current code --- .gitmodules | 3 +++ CMakeLists.txt | 3 +++ extern/shiny | 1 + 3 files changed, 7 insertions(+) create mode 100644 .gitmodules create mode 160000 extern/shiny diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..f53c97677 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "extern/shiny"] + path = extern/shiny + url = git@github.com:scrawl/shiny.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 84cef306e..569e28675 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,6 +432,9 @@ if(WIN32) include(CPack) endif(WIN32) +# Extern +add_subdirectory (extern/shiny) + # Components add_subdirectory (components) diff --git a/extern/shiny b/extern/shiny new file mode 160000 index 000000000..51b573fc6 --- /dev/null +++ b/extern/shiny @@ -0,0 +1 @@ +Subproject commit 51b573fc66ac9a61fe780070692cc600a89f51dd From a1e48b0febcfaf23ecf00b9e91b183ae1cd80fd8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 3 Jul 2012 22:48:16 +0200 Subject: [PATCH 046/688] builds now --- CMakeLists.txt | 2 +- apps/openmw/CMakeLists.txt | 3 ++- apps/openmw/mwrender/renderingmanager.cpp | 8 ++++++++ apps/openmw/mwrender/renderingmanager.hpp | 7 +++++++ extern/shiny | 2 +- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 569e28675..e56550430 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,7 +189,7 @@ endif() find_package(OGRE REQUIRED) find_package(MyGUI REQUIRED) -find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread) +find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread wave) find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 9534ecc90..911bd70d5 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -101,7 +101,8 @@ target_link_libraries(openmw ${BULLET_LIBRARIES} ${MYGUI_LIBRARIES} ${MYGUI_PLATFORM_LIBRARIES} - components + "shiny" + "shiny.OgrePlatform" ) # Fix for not visible pthreads functions for linker with glibc 2.15 diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5b76f5ae2..8672946fc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -14,6 +14,9 @@ #include #include +#include +#include + #include #include @@ -47,6 +50,11 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mWater = 0; + // material system + sh::OgrePlatform* platform = new sh::OgrePlatform("General", "./"); + platform->setCacheFolder ("./"); + mFactory = new sh::Factory(platform); + //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog configureFog(1, ColourValue(1,1,1)); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 9aaba3803..db5a731b4 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -43,6 +43,11 @@ namespace MWWorld class CellStore; } +namespace sh +{ + class Factory; +} + namespace MWRender { @@ -174,6 +179,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList private: + sh::Factory* mFactory; + void setAmbientMode(); void setMenuTransparency(float val); diff --git a/extern/shiny b/extern/shiny index 51b573fc6..392894931 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 51b573fc66ac9a61fe780070692cc600a89f51dd +Subproject commit 3928949316713d0c8aaf1ad564734d24ad773be9 From 4ea6530772fed9069d61c9ee9e5201e3e0635de6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Jul 2012 00:26:57 +0200 Subject: [PATCH 047/688] hello world. --- apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwrender/sky.cpp | 3 +- cmake/OpenMWMacros.cmake | 8 ++ components/nifogre/ogre_nif_loader.cpp | 83 +++++--------------- files/CMakeLists.txt | 17 ++-- files/materials/core.h | 96 +++++++++++++++++++++++ files/materials/objects.mat | 29 +++++++ files/materials/objects.shader | 27 +++++++ files/materials/objects.shaderset | 15 ++++ files/mygui/CMakeLists.txt | 64 +-------------- 10 files changed, 207 insertions(+), 139 deletions(-) create mode 100644 files/materials/core.h create mode 100644 files/materials/objects.mat create mode 100644 files/materials/objects.shader create mode 100644 files/materials/objects.shaderset diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8672946fc..bfa27e42f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -51,7 +51,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mWater = 0; // material system - sh::OgrePlatform* platform = new sh::OgrePlatform("General", "./"); + sh::OgrePlatform* platform = new sh::OgrePlatform("General", (resDir / "materials").string()); platform->setCacheFolder ("./"); mFactory = new sh::Factory(platform); @@ -270,10 +270,12 @@ void RenderingManager::setWaterHeight(const float height) void RenderingManager::skyEnable () { + /* if(mSkyManager) mSkyManager->enable(); mOcclusionQuery->setSunNode(mSkyManager->getSunNode()); + */ } void RenderingManager::skyDisable () diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 45e48240b..e1e8d6a5c 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -416,7 +416,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) void SkyManager::create() { /// \todo preload all the textures and meshes that are used for sky rendering - +/* // Create overlay used for thunderstorm MaterialPtr material = MaterialManager::getSingleton().create( "ThunderMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); Pass* pass = material->getTechnique(0)->getPass(0); @@ -696,6 +696,7 @@ void SkyManager::create() mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); mCreated = true; + */ } SkyManager::~SkyManager() diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index 024338d3a..c2567830d 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -22,3 +22,11 @@ endforeach (f) endforeach (u) source_group ("components\\${dir}" FILES ${files}) endmacro (add_component_dir) + +macro (copy_all_files file_expression destination_dir) +file (GLOB ALL "${file_expression}") +foreach (f ${ALL}) +get_filename_component(filename ${f} NAME) +configure_file(${f} ${destination_dir}/${filename} COPYONLY) +endforeach (f) +endmacro (copy_all_files) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 895c51a0d..a83a03a17 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -33,6 +33,8 @@ #include #include +#include + #include #include @@ -226,35 +228,32 @@ void NIFLoader::createMaterial(const String &name, int alphaFlags, float alphaTest, const String &texName) { - MaterialPtr material = MaterialManager::getSingleton().create(name, resourceGroup); - - - //Hardware Skinning code, textures may be the wrong color if enabled - - /* if(!mSkel.isNull()){ - material->removeAllTechniques(); - - Ogre::Technique* tech = material->createTechnique(); - //tech->setSchemeName("blahblah"); - Pass* pass = tech->createPass(); - pass->setVertexProgram("Ogre/BasicVertexPrograms/AmbientOneTexture");*/ - - - // This assigns the texture to this material. If the texture name is - // a file name, and this file exists (in a resource directory), it - // will automatically be loaded when needed. If not (such as for - // internal NIF textures that we might support later), we should - // already have inserted a manual loader for the texture. + sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (name, "openmw_objects_base"); + instance->setProperty ("ambient", sh::makeProperty ( + new sh::Vector3(ambient.array[0], ambient.array[1], ambient.array[2]))); + + instance->setProperty ("diffuse", sh::makeProperty ( + new sh::Vector4(diffuse.array[0], diffuse.array[1], diffuse.array[2], alpha))); + + instance->setProperty ("specular", sh::makeProperty ( + new sh::Vector4(specular.array[0], specular.array[1], specular.array[2], glossiness))); + + instance->setProperty ("emissive", sh::makeProperty ( + new sh::Vector3(emissive.array[0], emissive.array[1], emissive.array[2]))); + + instance->setProperty ("diffuseMap", sh::makeProperty(texName)); +/* if (!texName.empty()) { Pass *pass = material->getTechnique(0)->getPass(0); /*TextureUnitState *txt =*/ + /* pass->createTextureUnitState(texName); pass->setVertexColourTracking(TVC_DIFFUSE); - +*/ // As of yet UNTESTED code from Chris: /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); @@ -282,7 +281,7 @@ void NIFLoader::createMaterial(const String &name, else pass->setDepthWriteEnabled(true); */ - +/* // Add transparency if NiAlphaProperty was present if (alphaFlags != -1) { @@ -315,48 +314,8 @@ void NIFLoader::createMaterial(const String &name, material->getTechnique(0)->setShadowCasterMaterial("depth_shadow_caster_noalpha"); } } + */ - if (Settings::Manager::getBool("enabled", "Shadows")) - { - bool split = Settings::Manager::getBool("split", "Shadows"); - const int numsplits = 3; - for (int i = 0; i < (split ? numsplits : 1); ++i) - { - TextureUnitState* tu = material->getTechnique(0)->getPass(0)->createTextureUnitState(); - tu->setName("shadowMap" + StringConverter::toString(i)); - tu->setContentType(TextureUnitState::CONTENT_SHADOW); - tu->setTextureAddressingMode(TextureUnitState::TAM_BORDER); - tu->setTextureBorderColour(ColourValue::White); - } - } - - if (Settings::Manager::getBool("shaders", "Objects")) - { - material->getTechnique(0)->getPass(0)->setVertexProgram("main_vp"); - material->getTechnique(0)->getPass(0)->setFragmentProgram("main_fp"); - - material->getTechnique(0)->getPass(0)->setFog(true); // force-disable fixed function fog, it is calculated in shader - } - - // Create a fallback technique without shadows and without mrt - Technique* tech2 = material->createTechnique(); - tech2->setSchemeName("Fallback"); - Pass* pass2 = tech2->createPass(); - pass2->createTextureUnitState(texName); - pass2->setVertexColourTracking(TVC_DIFFUSE); - if (Settings::Manager::getBool("shaders", "Objects")) - { - pass2->setVertexProgram("main_fallback_vp"); - pass2->setFragmentProgram("main_fallback_fp"); - pass2->setFog(true); // force-disable fixed function fog, it is calculated in shader - } - - // Add material bells and whistles - material->setAmbient(ambient.array[0], ambient.array[1], ambient.array[2]); - material->setDiffuse(diffuse.array[0], diffuse.array[1], diffuse.array[2], alpha); - material->setSpecular(specular.array[0], specular.array[1], specular.array[2], alpha); - material->setSelfIllumination(emissive.array[0], emissive.array[1], emissive.array[2]); - material->setShininess(glossiness); } // Takes a name and adds a unique part to it. This is just used to diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index 8ab3d5b51..ce8bc9fbd 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -1,16 +1,9 @@ project(resources) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/caustic_0.png "${OpenMW_BINARY_DIR}/resources/water/caustic_0.png" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/underwater.cg "${OpenMW_BINARY_DIR}/resources/water/underwater.cg" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/perlinvolume.dds "${OpenMW_BINARY_DIR}/resources/water/perlinvolume.dds" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.compositor "${OpenMW_BINARY_DIR}/resources/water/water.compositor" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.material "${OpenMW_BINARY_DIR}/resources/water/water.material" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/WaterNormal2.tga "${OpenMW_BINARY_DIR}/resources/water/WaterNormal2.tga" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.cg "${OpenMW_BINARY_DIR}/resources/water/water.cg" COPYONLY) +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water/* "${OpenMW_BINARY_DIR}/resources/water/") -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/gbuffer.cg "${OpenMW_BINARY_DIR}/resources/gbuffer/gbuffer.cg" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/gbuffer.material "${OpenMW_BINARY_DIR}/resources/gbuffer/gbuffer.material" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/gbuffer.compositor "${OpenMW_BINARY_DIR}/resources/gbuffer/gbuffer.compositor" COPYONLY) +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/* "${OpenMW_BINARY_DIR}/resources/gbuffer/") -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/shadows/depthshadowcaster.material "${OpenMW_BINARY_DIR}/resources/shadows/depthshadowcaster.material" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/shadows/depthshadowcaster.cg "${OpenMW_BINARY_DIR}/resources/shadows/depthshadowcaster.cg" COPYONLY) +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/shadows/* "${OpenMW_BINARY_DIR}/resources/shadows/") + +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/materials/* "${OpenMW_BINARY_DIR}/resources/materials/") diff --git a/files/materials/core.h b/files/materials/core.h new file mode 100644 index 000000000..2894ca382 --- /dev/null +++ b/files/materials/core.h @@ -0,0 +1,96 @@ +#if SH_HLSL == 1 || SH_CG == 1 + + #define shTexture2D sampler2D + #define shSample(tex, coord) tex2D(tex, coord) + #define shLerp(a, b, t) lerp(a, b, t) + #define shSaturate(a) saturate(a) + + #define shSampler2D(name) , uniform sampler2D name : register(s@shCounter(0)) @shUseSampler(name) + + #define shMatrixMult(m, v) mul(m, v) + + #define shUniform(s) , uniform s + + #define shInput(type, name) , in type name : TEXCOORD@shCounter(1) + #define shOutput(type, name) , out type name : TEXCOORD@shCounter(2) + + #ifdef SH_VERTEX_SHADER + + #define shOutputPosition oPosition + #define shInputPosition iPosition + + + #define SH_BEGIN_PROGRAM \ + void main( \ + float4 iPosition : POSITION \ + , out float4 oPosition : POSITION + + #define SH_START_PROGRAM \ + ) \ + + #endif + + #ifdef SH_FRAGMENT_SHADER + + #define shOutputColor oColor + + #define SH_BEGIN_PROGRAM \ + void main( \ + out float4 oColor : COLOR + + #define SH_START_PROGRAM \ + ) \ + + #endif + +#endif + +#if SH_GLSL == 1 + @shGlslVersion(420) + + #define float2 vec2 + #define float3 vec3 + #define float4 vec4 + #define int2 ivec2 + #define int3 ivec3 + #define int4 ivec4 + #define shTexture2D sampler2D + #define shSample(tex, coord) texture(tex, coord) + #define shLerp(a, b, t) mix(a, b, t) + #define shSaturate(a) clamp(a, 0.0, 1.0) + + #define shUniform(s) uniform s; + + #define shSampler2D(name) uniform sampler2D name; @shUseSampler(name) + + #define shMatrixMult(m, v) m * v + + #define shInputPosition vertex + #define shOutputPosition gl_Position + #define shOutputColor oColor + + #define float4x4 mat4 + + #define shInput(type, name) in type name; + #define shOutput(type, name) out type name; + + + #ifdef SH_VERTEX_SHADER + + #define SH_BEGIN_PROGRAM \ + in float4 shInputPosition; + #define SH_START_PROGRAM \ + void main(void) + + #endif + + #ifdef SH_FRAGMENT_SHADER + + #define SH_BEGIN_PROGRAM \ + out float4 oColor; + #define SH_START_PROGRAM \ + void main(void) + + + #endif +#endif diff --git a/files/materials/objects.mat b/files/materials/objects.mat new file mode 100644 index 000000000..564018724 --- /dev/null +++ b/files/materials/objects.mat @@ -0,0 +1,29 @@ +material openmw_objects_base +{ + diffuse 1.0 1.0 1.0 1.0 + specular 0.4 0.4 0.4 32 + ambient 1.0 1.0 1.0 + emissive 0.0 0.0 0.0 + has_vertex_colour false + diffuseMap black.png + + pass + { + vertex_program openmw_objects_vertex + fragment_program openmw_objects_fragment + + diffuse $diffuse + specular $specular + ambient $ambient + emissive $emissive + + ffp_vertex_colour_ambient $has_vertex_colour + has_vertex_colour $has_vertex_colour + + texture_unit diffuseMap + { + texture $diffuseMap + create_in_ffp true + } + } +} diff --git a/files/materials/objects.shader b/files/materials/objects.shader new file mode 100644 index 000000000..7bd91e526 --- /dev/null +++ b/files/materials/objects.shader @@ -0,0 +1,27 @@ +#include "core.h" + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shInput(float2, uv0) + shOutput(float2, UV) + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + } + +#else + + SH_BEGIN_PROGRAM + shSampler2D(diffuseMap) + shInput(float2, UV) + + SH_START_PROGRAM + { + // shOutputColor = float4(1.0, 0.0, 0.0, 1.0); + shOutputColor = shSample(diffuseMap, UV); + } + +#endif diff --git a/files/materials/objects.shaderset b/files/materials/objects.shaderset new file mode 100644 index 000000000..7d01973e3 --- /dev/null +++ b/files/materials/objects.shaderset @@ -0,0 +1,15 @@ +shader_set openmw_objects_vertex +{ + source objects.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set openmw_objects_fragment +{ + source objects.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index f41cdf54e..b8a04a31c 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -1,68 +1,6 @@ -# Minimal MyGUI build system for OpenMW # Copy resource files into the build directory set(SDIR ${CMAKE_CURRENT_SOURCE_DIR}) set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) -configure_file("${SDIR}/bigbars.png" "${DDIR}/bigbars.png" COPYONLY) -configure_file("${SDIR}/black.png" "${DDIR}/black.png" COPYONLY) -configure_file("${SDIR}/core.skin" "${DDIR}/core.skin" COPYONLY) -configure_file("${SDIR}/core.xml" "${DDIR}/core.xml" COPYONLY) -configure_file("${SDIR}/mwgui.png" "${DDIR}/mwgui.png" COPYONLY) -configure_file("${SDIR}/openmw_resources.xml" "${DDIR}/openmw_resources.xml" COPYONLY) -configure_file("${SDIR}/openmw_settings.xml" "${DDIR}/openmw_settings.xml" COPYONLY) -configure_file("${SDIR}/openmw_box.skin.xml" "${DDIR}/openmw_box.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_button.skin.xml" "${DDIR}/openmw_button.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_list.skin.xml" "${DDIR}/openmw_list.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_edit.skin.xml" "${DDIR}/openmw_edit.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_console.layout" "${DDIR}/openmw_console.layout" COPYONLY) -configure_file("${SDIR}/openmw_console.skin.xml" "${DDIR}/openmw_console.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw.font.xml" "${DDIR}/openmw.font.xml" COPYONLY) -configure_file("${SDIR}/openmw_hud_box.skin.xml" "${DDIR}/openmw_hud_box.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_hud_energybar.skin.xml" "${DDIR}/openmw_hud_energybar.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_hud.layout" "${DDIR}/openmw_hud.layout" COPYONLY) -configure_file("${SDIR}/openmw_text_input.layout" "${DDIR}/openmw_text_input.layout" COPYONLY) -configure_file("${SDIR}/openmw_infobox.layout" "${DDIR}/openmw_infobox.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_race.layout" "${DDIR}/openmw_chargen_race.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_class.layout" "${DDIR}/openmw_chargen_class.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_generate_class_result.layout" "${DDIR}/openmw_chargen_generate_class_result.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_create_class.layout" "${DDIR}/openmw_chargen_create_class.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_select_specialization.layout" "${DDIR}/openmw_chargen_select_specialization.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_select_attribute.layout" "${DDIR}/openmw_chargen_select_attribute.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_select_skill.layout" "${DDIR}/openmw_chargen_select_skill.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_class_description.layout" "${DDIR}/openmw_chargen_class_description.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_birth.layout" "${DDIR}/openmw_chargen_birth.layout" COPYONLY) -configure_file("${SDIR}/openmw_chargen_review.layout" "${DDIR}/openmw_chargen_review.layout" COPYONLY) -configure_file("${SDIR}/openmw_dialogue_window.layout" "${DDIR}/openmw_dialogue_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_dialogue_window_skin.xml" "${DDIR}/openmw_dialogue_window_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_inventory_window.layout" "${DDIR}/openmw_inventory_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_container_window.layout" "${DDIR}/openmw_container_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_layers.xml" "${DDIR}/openmw_layers.xml" COPYONLY) -configure_file("${SDIR}/openmw_mainmenu.layout" "${DDIR}/openmw_mainmenu.layout" COPYONLY) -configure_file("${SDIR}/openmw_mainmenu_skin.xml" "${DDIR}/openmw_mainmenu_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_map_window.layout" "${DDIR}/openmw_map_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_map_window_skin.xml" "${DDIR}/openmw_map_window_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw.pointer.xml" "${DDIR}/openmw.pointer.xml" COPYONLY) -configure_file("${SDIR}/openmw_progress.skin.xml" "${DDIR}/openmw_progress.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_stats_window.layout" "${DDIR}/openmw_stats_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_text.skin.xml" "${DDIR}/openmw_text.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_windows.skin.xml" "${DDIR}/openmw_windows.skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_messagebox.layout" "${DDIR}/openmw_messagebox.layout" COPYONLY) -configure_file("${SDIR}/openmw_interactive_messagebox.layout" "${DDIR}/openmw_interactive_messagebox.layout" COPYONLY) -configure_file("${SDIR}/openmw_journal.layout" "${DDIR}/openmw_journal.layout" COPYONLY) -configure_file("${SDIR}/openmw_journal_skin.xml" "${DDIR}/openmw_journal_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_tooltips.layout" "${DDIR}/openmw_tooltips.layout" COPYONLY) -configure_file("${SDIR}/openmw_scroll.layout" "${DDIR}/openmw_scroll.layout" COPYONLY) -configure_file("${SDIR}/openmw_scroll_skin.xml" "${DDIR}/openmw_scroll_skin.xml" COPYONLY) -configure_file("${SDIR}/openmw_book.layout" "${DDIR}/openmw_book.layout" COPYONLY) -configure_file("${SDIR}/openmw_count_window.layout" "${DDIR}/openmw_count_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_trade_window.layout" "${DDIR}/openmw_trade_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_settings_window.layout" "${DDIR}/openmw_settings_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_confirmation_dialog.layout" "${DDIR}/openmw_confirmation_dialog.layout" COPYONLY) -configure_file("${SDIR}/openmw_alchemy_window.layout" "${DDIR}/openmw_alchemy_window.layout" COPYONLY) -configure_file("${SDIR}/openmw_spell_window.layout" "${DDIR}/openmw_spell_window.layout" COPYONLY) -configure_file("${SDIR}/atlas1.cfg" "${DDIR}/atlas1.cfg" COPYONLY) -configure_file("${SDIR}/smallbars.png" "${DDIR}/smallbars.png" COPYONLY) -configure_file("${SDIR}/EBGaramond-Regular.ttf" "${DDIR}/EBGaramond-Regular.ttf" COPYONLY) -configure_file("${SDIR}/Obliviontt.zip" "${DDIR}/Obliviontt.zip" COPYONLY) -configure_file("${SDIR}/VeraMono.ttf" "${DDIR}/VeraMono.ttf" COPYONLY) +copy_all_files(${SDIR}/* ${DDIR}) From de9b7a51de1ee7528a3ae2ed260f15c526602755 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Jul 2012 00:33:09 +0200 Subject: [PATCH 048/688] change glsl #version, remove CG from required libraries --- CMakeLists.txt | 4 ++-- files/materials/core.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e56550430..51b2a45ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,7 +194,7 @@ find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) IF(OGRE_STATIC) -find_package(Cg REQUIRED) +find_package(Cg) IF(WIN32) set(OGRE_PLUGIN_INCLUDE_DIRS ${OGRE_Plugin_CgProgramManager_INCLUDE_DIRS} ${OGRE_Plugin_OctreeSceneManager_INCLUDE_DIRS} ${OGRE_Plugin_ParticleFX_INCLUDE_DIRS} ${OGRE_RenderSystem_Direct3D9_INCLUDE_DIRS} ${OGRE_RenderSystem_GL_INCLUDE_DIRS}) ELSE(WIN32) @@ -347,7 +347,7 @@ if(DPKG_PROGRAM) SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS "nvidia-cg-toolkit (>= 2.1), libboost-filesystem1.46.1 (>= 1.46.1), libboost-program-options1.46.1 (>= 1.46.1), libboost-system1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") + SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-filesystem1.46.1 (>= 1.46.1), libboost-program-options1.46.1 (>= 1.46.1), libboost-system1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") diff --git a/files/materials/core.h b/files/materials/core.h index 2894ca382..2f51925a0 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -46,7 +46,7 @@ #endif #if SH_GLSL == 1 - @shGlslVersion(420) + @shGlslVersion(130) #define float2 vec2 #define float3 vec3 From d8d00123ea34662282e461dd3253dbd4b90fe908 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 3 Jul 2012 18:37:04 -0700 Subject: [PATCH 049/688] Watch for empty children node refs when setting parents --- components/nif/nif_file.cpp | 2 +- components/nif/node.hpp | 6 +++++- components/nif/record_ptr.hpp | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 48dd76510..42aa43f8b 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -203,7 +203,7 @@ void NiSkinInstance::post(NIFFile *nif) root->makeRootBone(data->trafo); - for(int i=0; ifail("Oops: Missing bone! Don't know how to handle this."); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index e4cc9291e..5a2847b6c 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -149,7 +149,11 @@ struct NiNode : Node effects.post(nif); for(size_t i = 0;i < children.length();i++) - children[i].parent = this; + { + // Why would a unique list of children contain empty refs? + if(children.has(i)) + children[i].parent = this; + } } }; diff --git a/components/nif/record_ptr.hpp b/components/nif/record_ptr.hpp index ac91e25e3..755094147 100644 --- a/components/nif/record_ptr.hpp +++ b/components/nif/record_ptr.hpp @@ -53,6 +53,7 @@ public: // Store the index for later index = nif->getInt(); + assert(index >= -1); } /// Resolve index to pointer @@ -117,11 +118,10 @@ public: bool has(size_t index) { - assert(index >= 0 && index < static_cast (list.size())); return !list.at(index).empty(); } - int length() + size_t length() { return list.size(); } }; From f8e3213996fc91583c6c1e935441709cdab43ec6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Jul 2012 16:57:14 +0200 Subject: [PATCH 050/688] fog, mrt depth --- extern/shiny | 2 +- files/materials/core.h | 13 +++++++--- files/materials/objects.mat | 5 ++++ files/materials/objects.shader | 45 ++++++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/extern/shiny b/extern/shiny index 392894931..8d95f5346 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 3928949316713d0c8aaf1ad564734d24ad773be9 +Subproject commit 8d95f53464a779c7da643228ace02ae28ec6a503 diff --git a/files/materials/core.h b/files/materials/core.h index 2f51925a0..20127d3de 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -14,6 +14,8 @@ #define shInput(type, name) , in type name : TEXCOORD@shCounter(1) #define shOutput(type, name) , out type name : TEXCOORD@shCounter(2) + #define shNormalInput(type) , in type normal : NORMAL + #ifdef SH_VERTEX_SHADER #define shOutputPosition oPosition @@ -32,7 +34,9 @@ #ifdef SH_FRAGMENT_SHADER - #define shOutputColor oColor + #define shOutputColor(num) oColor##num + + #define shDeclareMrtOutput(num) , out float4 oColor##num : COLOR##num #define SH_BEGIN_PROGRAM \ void main( \ @@ -67,13 +71,14 @@ #define shInputPosition vertex #define shOutputPosition gl_Position - #define shOutputColor oColor + #define shOutputColor(num) oColor##num #define float4x4 mat4 #define shInput(type, name) in type name; #define shOutput(type, name) out type name; + #define shNormalInput(type) in type normal; #ifdef SH_VERTEX_SHADER @@ -86,8 +91,10 @@ #ifdef SH_FRAGMENT_SHADER + #define shDeclareMrtOutput(num) out vec4 oColor##num; + #define SH_BEGIN_PROGRAM \ - out float4 oColor; + out float4 oColor0; #define SH_START_PROGRAM \ void main(void) diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 564018724..69b89a7e5 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -6,6 +6,8 @@ material openmw_objects_base emissive 0.0 0.0 0.0 has_vertex_colour false diffuseMap black.png + fog true + is_transparent false // real transparency, alpha rejection doesn't count here pass { @@ -16,9 +18,12 @@ material openmw_objects_base specular $specular ambient $ambient emissive $emissive + fog $fog ffp_vertex_colour_ambient $has_vertex_colour has_vertex_colour $has_vertex_colour + + is_transparent $is_transparent texture_unit diffuseMap { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 7bd91e526..08e9913d9 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -1,15 +1,36 @@ #include "core.h" + +#define FOG @shPropertyBool(fog) +#define MRT @shPropertyNotBool(is_transparent) + +#if MRT +#define NEED_DEPTH +#endif + +#if FOG +#define NEED_DEPTH +#endif + #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) shInput(float2, uv0) shOutput(float2, UV) + shNormalInput(float4) + shOutput(float4, normalPassthrough) + #ifdef NEED_DEPTH + shOutput(float, depthPassthrough) + #endif SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); UV = uv0; + normalPassthrough = normal; + #ifdef NEED_DEPTH + depthPassthrough = shOutputPosition.z; + #endif } #else @@ -17,11 +38,31 @@ SH_BEGIN_PROGRAM shSampler2D(diffuseMap) shInput(float2, UV) + shDeclareMrtOutput(1) + shInput(float4, normalPassthrough) + #ifdef NEED_DEPTH + shInput(float, depthPassthrough) + #endif + + shUniform(float far) @shAutoConstant(far, far_clip_distance) + +#if FOG + shUniform(float3 fogColor) @shAutoConstant(fogColor, fog_colour) + shUniform(float4 fogParams) @shAutoConstant(fogParams, fog_params) + +#endif SH_START_PROGRAM { - // shOutputColor = float4(1.0, 0.0, 0.0, 1.0); - shOutputColor = shSample(diffuseMap, UV); + //shOutputColor(0) = float4((normalize(normalPassthrough.xyz)+float3(1.0,1.0,1.0)) / 2.f, 1.0); + shOutputColor(0) = shSample(diffuseMap, UV); + +#if FOG + float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); + shOutputColor(0).xyz = shLerp (shOutputColor(0).xyz, fogColor, fogValue); +#endif + + shOutputColor(1) = float4(depthPassthrough / far,1,1,1); } #endif From 19ecc7f890b6a92267c73ede17edb2bc109e8147 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 4 Jul 2012 17:28:22 +0200 Subject: [PATCH 051/688] fix performance --- components/nifogre/ogre_nif_loader.cpp | 4 +++- extern/shiny | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index a83a03a17..26a18b6b4 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -228,6 +228,9 @@ void NIFLoader::createMaterial(const String &name, int alphaFlags, float alphaTest, const String &texName) { + if (texName.empty()) + return; + sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (name, "openmw_objects_base"); instance->setProperty ("ambient", sh::makeProperty ( new sh::Vector3(ambient.array[0], ambient.array[1], ambient.array[2]))); @@ -243,7 +246,6 @@ void NIFLoader::createMaterial(const String &name, instance->setProperty ("diffuseMap", sh::makeProperty(texName)); - /* if (!texName.empty()) { diff --git a/extern/shiny b/extern/shiny index 8d95f5346..6080431c8 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 8d95f53464a779c7da643228ace02ae28ec6a503 +Subproject commit 6080431c8ce37c6044dd7d1e319de3c7b8adfd69 From 9bd888d9a1a6572222bfa5f1a29e3cf840590b58 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Jul 2012 00:39:25 +0200 Subject: [PATCH 052/688] transparency --- components/nifogre/ogre_nif_loader.cpp | 27 ++++++++++ extern/shiny | 2 +- files/materials/atmosphere.shader | 0 files/materials/atmosphere.shaderset | 0 files/materials/clouds.shader | 0 files/materials/clouds.shaderset | 0 files/materials/moon.shader | 0 files/materials/moon.shaderset | 0 files/materials/objects.mat | 7 +++ files/materials/objects.shader | 12 ++--- files/materials/sky.mat | 73 ++++++++++++++++++++++++++ files/materials/sun.shader | 0 files/materials/sun.shaderset | 0 files/materials/water.mat | 30 +++++++++++ files/materials/water.shader | 0 files/materials/water.shaderset | 0 16 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 files/materials/atmosphere.shader create mode 100644 files/materials/atmosphere.shaderset create mode 100644 files/materials/clouds.shader create mode 100644 files/materials/clouds.shaderset create mode 100644 files/materials/moon.shader create mode 100644 files/materials/moon.shaderset create mode 100644 files/materials/sky.mat create mode 100644 files/materials/sun.shader create mode 100644 files/materials/sun.shaderset create mode 100644 files/materials/water.mat create mode 100644 files/materials/water.shader create mode 100644 files/materials/water.shaderset diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 26a18b6b4..d1102087d 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -246,6 +246,33 @@ void NIFLoader::createMaterial(const String &name, instance->setProperty ("diffuseMap", sh::makeProperty(texName)); + // Add transparency if NiAlphaProperty was present + if (alphaFlags != -1) + { + // The 237 alpha flags are by far the most common. Check + // NiAlphaProperty in nif/property.h if you need to decode + // other values. 237 basically means normal transparencly. + if (alphaFlags == 237) + { + NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); + if (result.first) + { + instance->setProperty("alpha_rejection_func", sh::makeProperty(new sh::StringValue("greater_equal"))); + instance->setProperty("alpha_rejection_value", sh::makeProperty(new sh::IntValue(result.second))); + } + else + { + // Enable transparency + instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); + instance->setProperty("depth_write", sh::makeProperty(new sh::BooleanValue(false))); + } + } + else + warn("Unhandled alpha setting for texture " + texName); + + } + + /* if (!texName.empty()) { diff --git a/extern/shiny b/extern/shiny index 6080431c8..3a1b8e2ae 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 6080431c8ce37c6044dd7d1e319de3c7b8adfd69 +Subproject commit 3a1b8e2aefa746ef0922e78f4be3ff736ce92fc3 diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/atmosphere.shaderset b/files/materials/atmosphere.shaderset new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/clouds.shaderset b/files/materials/clouds.shaderset new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/moon.shader b/files/materials/moon.shader new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/moon.shaderset b/files/materials/moon.shaderset new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 69b89a7e5..3fcc12d62 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -7,7 +7,11 @@ material openmw_objects_base has_vertex_colour false diffuseMap black.png fog true + is_transparent false // real transparency, alpha rejection doesn't count here + scene_blend default + alpha_rejection_value default + alpha_rejection_func default pass { @@ -24,6 +28,9 @@ material openmw_objects_base has_vertex_colour $has_vertex_colour is_transparent $is_transparent + scene_blend $scene_blend + alpha_rejection_value $alpha_rejection_value + alpha_rejection_func $alpha_rejection_func texture_unit diffuseMap { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 08e9913d9..9ec09840d 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -20,17 +20,17 @@ shOutput(float2, UV) shNormalInput(float4) shOutput(float4, normalPassthrough) - #ifdef NEED_DEPTH +#ifdef NEED_DEPTH shOutput(float, depthPassthrough) - #endif +#endif SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); UV = uv0; normalPassthrough = normal; - #ifdef NEED_DEPTH +#ifdef NEED_DEPTH depthPassthrough = shOutputPosition.z; - #endif +#endif } #else @@ -41,9 +41,9 @@ shDeclareMrtOutput(1) shInput(float4, normalPassthrough) - #ifdef NEED_DEPTH +#ifdef NEED_DEPTH shInput(float, depthPassthrough) - #endif +#endif shUniform(float far) @shAutoConstant(far, far_clip_distance) diff --git a/files/materials/sky.mat b/files/materials/sky.mat new file mode 100644 index 000000000..a82816438 --- /dev/null +++ b/files/materials/sky.mat @@ -0,0 +1,73 @@ +material openmw_moon +{ + pass + { + vertex_program moon_vertex + fragment_program moon_fragment + + texture_unit diffuseMap + { + texture $diffuseMap + create_in_ffp true + } + } +} + +material openmw_clouds +{ + pass + { + vertex_program clouds_vertex + fragment_program clouds_fragment + + // second diffuse map is used for weather transitions + texture_unit diffuseMap1 + { + texture $diffuseMap1 + create_in_ffp true + } + + texture_unit diffuseMap2 + { + texture $diffuseMap2 + } + } +} + +material openmw_atmosphere +{ + pass + { + vertex_program atmosphere_vertex + fragment_program atmosphere_fragment + } +} + +material openmw_stars +{ + pass + { + vertex_program stars_vertex + fragment_program stars_fragment + + texture_unit diffuseMap + { + diffuseMap $diffuseMap + } + } +} + +// used for both sun and sun glare +material openmw_sun +{ + pass + { + vertex_program sun_vertex + fragment_program sun_fragment + + texture unit diffuseMap + { + diffuseMap $diffuseMap + } + } +} diff --git a/files/materials/sun.shader b/files/materials/sun.shader new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/sun.shaderset b/files/materials/sun.shaderset new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/water.mat b/files/materials/water.mat new file mode 100644 index 000000000..c701e5ffe --- /dev/null +++ b/files/materials/water.mat @@ -0,0 +1,30 @@ +// note: the fixed function water is created manually, not here + +material openmw_water +{ + pass + { + vertex_program water_vertex + fragment_program water_fragment + + texture_unit reflectionMap + { + texture_alias WaterReflection + } + + texture_unit refractionMap + { + texture_alias WaterRefraction + } + + texture_unit depthMap + { + + } + + texture_unit normalMap + { + texture + } + } +} diff --git a/files/materials/water.shader b/files/materials/water.shader new file mode 100644 index 000000000..e69de29bb diff --git a/files/materials/water.shaderset b/files/materials/water.shaderset new file mode 100644 index 000000000..e69de29bb From 8e683c2e05fac7c8aad3e95b9c756bdeb9dbd55f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Jul 2012 01:38:33 +0200 Subject: [PATCH 053/688] vertex colors --- components/nifogre/ogre_nif_loader.cpp | 14 ++++++++++---- components/nifogre/ogre_nif_loader.hpp | 3 ++- files/materials/core.h | 8 +++++++- files/materials/objects.shader | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index d1102087d..eb1714b61 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -226,7 +226,7 @@ void NIFLoader::createMaterial(const String &name, const Vector &emissive, float glossiness, float alpha, int alphaFlags, float alphaTest, - const String &texName) + const String &texName, bool vertexColor) { if (texName.empty()) return; @@ -246,6 +246,9 @@ void NIFLoader::createMaterial(const String &name, instance->setProperty ("diffuseMap", sh::makeProperty(texName)); + if (vertexColor) + instance->setProperty ("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); + // Add transparency if NiAlphaProperty was present if (alphaFlags != -1) { @@ -272,7 +275,6 @@ void NIFLoader::createMaterial(const String &name, } - /* if (!texName.empty()) { @@ -668,6 +670,8 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou NiTexturingProperty *t = NULL; NiMaterialProperty *m = NULL; NiAlphaProperty *a = NULL; + // can't make any sense of these values, so ignoring them for now + //NiVertexColorProperty *v = NULL; // Scan the property list for material information PropertyList &list = shape->props; @@ -685,6 +689,8 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou m = static_cast(pr); else if (pr->recType == RC_NiAlphaProperty) a = static_cast(pr); + //else if (pr->recType == RC_NiVertexColorProperty) + //v = static_cast(pr); } // Texture @@ -755,7 +761,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou { //std::cout << "new"; createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, - d->glossiness, d->alpha, alphaFlags, alphaTest, texName); + d->glossiness, d->alpha, alphaFlags, alphaTest, texName, shape->data->colors.length != 0); MaterialMap.insert(std::make_pair(texName,material)); } } @@ -771,7 +777,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou } createMaterial(material, one, one, zero, zero, 0.0, 1.0, - alphaFlags, alphaTest, texName); + alphaFlags, alphaTest, texName, shape->data->colors.length != 0); } } } // End of material block, if(!hidden) ... diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index d73948fa8..fadb30af7 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -142,7 +142,8 @@ class NIFLoader : Ogre::ManualResourceLoader const Nif::Vector &emissive, float glossiness, float alpha, int alphaFlags, float alphaTest, - const Ogre::String &texName); + const Ogre::String &texName, + bool vertexColor); void findRealTexture(Ogre::String &texName); diff --git a/files/materials/core.h b/files/materials/core.h index 20127d3de..fe0172cd9 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -15,6 +15,8 @@ #define shOutput(type, name) , out type name : TEXCOORD@shCounter(2) #define shNormalInput(type) , in type normal : NORMAL + + #define shColourInput(type) , in type colour : COLOR #ifdef SH_VERTEX_SHADER @@ -40,7 +42,7 @@ #define SH_BEGIN_PROGRAM \ void main( \ - out float4 oColor : COLOR + out float4 oColor0 : COLOR #define SH_START_PROGRAM \ ) \ @@ -69,7 +71,9 @@ #define shMatrixMult(m, v) m * v + // automatically recognized by ogre when the input name equals this #define shInputPosition vertex + #define shOutputPosition gl_Position #define shOutputColor(num) oColor##num @@ -78,7 +82,9 @@ #define shInput(type, name) in type name; #define shOutput(type, name) out type name; + // automatically recognized by ogre when the input name equals this #define shNormalInput(type) in type normal; + #define shColourInput(type) in type colour; #ifdef SH_VERTEX_SHADER diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 9ec09840d..9a78bb455 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -12,6 +12,8 @@ #define NEED_DEPTH #endif +#define HAS_VERTEXCOLOR @shPropertyBool(has_vertex_colour) + #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM @@ -22,6 +24,10 @@ shOutput(float4, normalPassthrough) #ifdef NEED_DEPTH shOutput(float, depthPassthrough) +#endif +#if HAS_VERTEXCOLOR + shColourInput(float4) + shOutput(float4, colorPassthrough) #endif SH_START_PROGRAM { @@ -31,6 +37,10 @@ #ifdef NEED_DEPTH depthPassthrough = shOutputPosition.z; #endif + +#if HAS_VERTEXCOLOR + colorPassthrough = colour; +#endif } #else @@ -50,13 +60,20 @@ #if FOG shUniform(float3 fogColor) @shAutoConstant(fogColor, fog_colour) shUniform(float4 fogParams) @shAutoConstant(fogParams, fog_params) +#endif +#ifdef HAS_VERTEXCOLOR + shInput(float4, colorPassthrough) #endif SH_START_PROGRAM { //shOutputColor(0) = float4((normalize(normalPassthrough.xyz)+float3(1.0,1.0,1.0)) / 2.f, 1.0); shOutputColor(0) = shSample(diffuseMap, UV); +#if HAS_VERTEXCOLOR + shOutputColor(0).xyz *= colorPassthrough.xyz; +#endif + #if FOG float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); shOutputColor(0).xyz = shLerp (shOutputColor(0).xyz, fogColor, fogValue); From 1a5203749f6800e6674583ecdfb82f890fec80ff Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 5 Jul 2012 17:13:23 +0200 Subject: [PATCH 054/688] fix "error in framelistener" when trying to pick up lights that can't be picked up --- apps/openmw/mwgui/inventorywindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index dc5728283..23a96b1d3 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -310,6 +310,9 @@ namespace MWGui && (type != typeid(ESM::Potion).name())) return; + if (MWWorld::Class::get(object).getName(object) == "") // objects without name presented to user can never be picked up + return; + // sound std::string sound = MWWorld::Class::get(object).getUpSoundId(object); MWBase::Environment::get().getSoundManager()->playSound(sound, 1, 1); From 33c48b9481deb090ff411da6228757cb59b4bb4e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 6 Jul 2012 04:28:08 +0200 Subject: [PATCH 055/688] update --- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/water.cpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 91 +++++++------------------- extern/shiny | 2 +- files/materials/core.h | 2 +- files/materials/objects.mat | 23 ++++--- files/materials/objects.shader | 13 ++-- 7 files changed, 48 insertions(+), 87 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index dd0351721..4fa125c9b 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -229,7 +229,7 @@ void LocalMap::render(const float x, const float y, vp->setVisibilityMask(RV_Map); // use fallback techniques without shadows and without mrt - vp->setMaterialScheme("Fallback"); + vp->setMaterialScheme("simple"); rtt->update(); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 94fb3d6d9..666161052 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -336,7 +336,7 @@ void Water::applyRTT() vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f)); vp->setShadowsEnabled(false); // use fallback techniques without shadows and without mrt (currently not implemented for sky and terrain) - //vp->setMaterialScheme("Fallback"); + vp->setMaterialScheme("simple"); rtt->addListener(this); rtt->setActive(true); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index eb1714b61..164b51daf 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -33,6 +33,8 @@ #include #include +#include + #include #include @@ -260,8 +262,8 @@ void NIFLoader::createMaterial(const String &name, NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); if (result.first) { - instance->setProperty("alpha_rejection_func", sh::makeProperty(new sh::StringValue("greater_equal"))); - instance->setProperty("alpha_rejection_value", sh::makeProperty(new sh::IntValue(result.second))); + instance->setProperty("alpha_rejection", + sh::makeProperty(new sh::StringValue("greater_equal " + boost::lexical_cast(result.second)))); } else { @@ -272,81 +274,34 @@ void NIFLoader::createMaterial(const String &name, } else warn("Unhandled alpha setting for texture " + texName); - } -/* - if (!texName.empty()) + // As of yet UNTESTED code from Chris: + /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); + pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); + pass->setDepthCheckEnabled(true); + + // Add transparency if NiAlphaProperty was present + if (alphaFlags != -1) { - Pass *pass = material->getTechnique(0)->getPass(0); - /*TextureUnitState *txt =*/ - /* - pass->createTextureUnitState(texName); - - pass->setVertexColourTracking(TVC_DIFFUSE); -*/ - // As of yet UNTESTED code from Chris: - /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); - pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); - pass->setDepthCheckEnabled(true); - - // Add transparency if NiAlphaProperty was present - if (alphaFlags != -1) + std::cout << "Alpha flags set!" << endl; + if ((alphaFlags&1)) { - std::cout << "Alpha flags set!" << endl; - if ((alphaFlags&1)) - { - pass->setDepthWriteEnabled(false); - pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf), - getBlendFactor((alphaFlags>>5)&0xf)); - } - else - pass->setDepthWriteEnabled(true); - - if ((alphaFlags>>9)&1) - pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), - alphaTest); - - pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1)); + pass->setDepthWriteEnabled(false); + pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf), + getBlendFactor((alphaFlags>>5)&0xf)); } else - pass->setDepthWriteEnabled(true); */ + pass->setDepthWriteEnabled(true); -/* - // Add transparency if NiAlphaProperty was present - if (alphaFlags != -1) - { - // The 237 alpha flags are by far the most common. Check - // NiAlphaProperty in nif/property.h if you need to decode - // other values. 237 basically means normal transparencly. - if (alphaFlags == 237) - { - NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); - if (result.first) - { - pass->setAlphaRejectFunction(CMPF_GREATER_EQUAL); - pass->setAlphaRejectValue(result.second); - } - else - { - // Enable transparency - pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); + if ((alphaFlags>>9)&1) + pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), + alphaTest); - //pass->setDepthCheckEnabled(false); - pass->setDepthWriteEnabled(false); - //std::cout << "alpha 237; material: " << name << " texName: " << texName << std::endl; - } - } - else - warn("Unhandled alpha setting for texture " + texName); - } - else - { - material->getTechnique(0)->setShadowCasterMaterial("depth_shadow_caster_noalpha"); - } + pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1)); } - */ - + else + pass->setDepthWriteEnabled(true); */ } // Takes a name and adds a unique part to it. This is just used to diff --git a/extern/shiny b/extern/shiny index 3a1b8e2ae..8af2cce2d 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 3a1b8e2aefa746ef0922e78f4be3ff736ce92fc3 +Subproject commit 8af2cce2db40aa67edcba78c534a268e595696c8 diff --git a/files/materials/core.h b/files/materials/core.h index fe0172cd9..0ee95057f 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -52,7 +52,7 @@ #endif #if SH_GLSL == 1 - @shGlslVersion(130) + @version 130 #define float2 vec2 #define float3 vec3 diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 3fcc12d62..fc23b947a 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -7,30 +7,35 @@ material openmw_objects_base has_vertex_colour false diffuseMap black.png fog true + mrt_output true + lighting true is_transparent false // real transparency, alpha rejection doesn't count here scene_blend default - alpha_rejection_value default - alpha_rejection_func default + alpha_rejection default pass { vertex_program openmw_objects_vertex fragment_program openmw_objects_fragment + shader_properties + { + fog $fog + mrt_output $mrt_output + lighting $lighting + has_vertex_colour $has_vertex_colour + is_transparent $is_transparent + } + diffuse $diffuse specular $specular ambient $ambient emissive $emissive - fog $fog + scene_blend $scene_blend + alpha_rejection $alpha_rejection ffp_vertex_colour_ambient $has_vertex_colour - has_vertex_colour $has_vertex_colour - - is_transparent $is_transparent - scene_blend $scene_blend - alpha_rejection_value $alpha_rejection_value - alpha_rejection_func $alpha_rejection_func texture_unit diffuseMap { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 9a78bb455..3697e33e5 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -2,13 +2,10 @@ #define FOG @shPropertyBool(fog) -#define MRT @shPropertyNotBool(is_transparent) +#define MRT @shPropertyNotBool(is_transparent) && @shPropertyBool(mrt_output) +#define LIGHTING @shPropertyBool(lighting) -#if MRT -#define NEED_DEPTH -#endif - -#if FOG +#if FOG || MRT #define NEED_DEPTH #endif @@ -48,7 +45,9 @@ SH_BEGIN_PROGRAM shSampler2D(diffuseMap) shInput(float2, UV) +#if MRT shDeclareMrtOutput(1) +#endif shInput(float4, normalPassthrough) #ifdef NEED_DEPTH @@ -79,7 +78,9 @@ shOutputColor(0).xyz = shLerp (shOutputColor(0).xyz, fogColor, fogValue); #endif +#if MRT shOutputColor(1) = float4(depthPassthrough / far,1,1,1); +#endif } #endif From 71c865e2e99d205578720c9cd7f6cf67ff6b0306 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 6 Jul 2012 07:10:10 +0200 Subject: [PATCH 056/688] let there be light --- extern/shiny | 2 +- files/materials/objects.shader | 55 +++++++++++++++++++++++++--- files/materials/openmw.configuration | 15 ++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 files/materials/openmw.configuration diff --git a/extern/shiny b/extern/shiny index 8af2cce2d..ccaaefa59 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 8af2cce2db40aa67edcba78c534a268e595696c8 +Subproject commit ccaaefa59c0500cf790a1f3114c8b5593489afeb diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 3697e33e5..6dd05442c 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -9,6 +9,8 @@ #define NEED_DEPTH #endif +#define NUM_LIGHTS 8 + #define HAS_VERTEXCOLOR @shPropertyBool(has_vertex_colour) #ifdef SH_VERTEX_SHADER @@ -18,10 +20,15 @@ shInput(float2, uv0) shOutput(float2, UV) shNormalInput(float4) - shOutput(float4, normalPassthrough) #ifdef NEED_DEPTH shOutput(float, depthPassthrough) #endif + +#if LIGHTING + shOutput(float3, normalPassthrough) + shOutput(float3, objSpacePositionPassthrough) +#endif + #if HAS_VERTEXCOLOR shColourInput(float4) shOutput(float4, colorPassthrough) @@ -30,11 +37,15 @@ { shOutputPosition = shMatrixMult(wvp, shInputPosition); UV = uv0; - normalPassthrough = normal; + normalPassthrough = normal.xyz; #ifdef NEED_DEPTH depthPassthrough = shOutputPosition.z; #endif +#if LIGHTING + objSpacePositionPassthrough = shInputPosition.xyz; +#endif + #if HAS_VERTEXCOLOR colorPassthrough = colour; #endif @@ -48,13 +59,27 @@ #if MRT shDeclareMrtOutput(1) #endif - shInput(float4, normalPassthrough) #ifdef NEED_DEPTH shInput(float, depthPassthrough) #endif - + shUniform(float far) @shAutoConstant(far, far_clip_distance) + +#if LIGHTING + shInput(float3, normalPassthrough) + shInput(float3, objSpacePositionPassthrough) + shUniform(float4 lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) + shUniform(float passIteration) @shAutoConstant(passIteration, pass_iteration_number) + shUniform(float4 materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) + shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + @shForeach(NUM_LIGHTS) + shUniform(float4 lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) + shUniform(float4 lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) + shUniform(float4 lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) + @shEndForeach +#endif #if FOG shUniform(float3 fogColor) @shAutoConstant(fogColor, fog_colour) @@ -66,9 +91,29 @@ #endif SH_START_PROGRAM { - //shOutputColor(0) = float4((normalize(normalPassthrough.xyz)+float3(1.0,1.0,1.0)) / 2.f, 1.0); shOutputColor(0) = shSample(diffuseMap, UV); +#if LIGHTING + float3 normal = normalize(normalPassthrough); + float3 lightDir, diffuse; + float d; + float3 ambient = materialAmbient.xyz * lightAmbient.xyz; + + @shForeach(NUM_LIGHTS) + + lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePositionPassthrough.xyz * lightPosObjSpace@shIterator.w); + d = length(lightDir); + + lightDir = normalize(lightDir); + + diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); + + @shEndForeach + + shOutputColor(0).xyz *= (ambient + diffuse + materialEmissive.xyz); +#endif + + #if HAS_VERTEXCOLOR shOutputColor(0).xyz *= colorPassthrough.xyz; #endif diff --git a/files/materials/openmw.configuration b/files/materials/openmw.configuration new file mode 100644 index 000000000..6122cd8cb --- /dev/null +++ b/files/materials/openmw.configuration @@ -0,0 +1,15 @@ +configuration water_reflection +{ + fog false + receives_shadows false + mrt_output false +} + +configuration local_map +{ + fog false + receives_shadows false + simple_water true + mrt_output false + lighting false +} From a095572205f2bd153784b7d0cf9930e0247a8667 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 6 Jul 2012 10:31:48 +0200 Subject: [PATCH 057/688] fixed some textures, started with sky --- apps/openmw/mwrender/renderingmanager.cpp | 2 - apps/openmw/mwrender/sky.cpp | 41 ++++++++++++-------- components/nifogre/ogre_nif_loader.cpp | 1 + extern/shiny | 2 +- files/materials/atmosphere.shader | 38 +++++++++++++++++++ files/materials/atmosphere.shaderset | 15 ++++++++ files/materials/clouds.shader | 38 +++++++++++++++++++ files/materials/clouds.shaderset | 15 ++++++++ files/materials/objects.mat | 2 +- files/materials/objects.shader | 11 +++++- files/materials/sky.mat | 46 +++++++++++++++++++++++ files/materials/sun.shader | 39 +++++++++++++++++++ files/materials/sun.shaderset | 15 ++++++++ 13 files changed, 244 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bfa27e42f..174a9e4f8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -270,12 +270,10 @@ void RenderingManager::setWaterHeight(const float height) void RenderingManager::skyEnable () { - /* if(mSkyManager) mSkyManager->enable(); mOcclusionQuery->setSunNode(mSkyManager->getSunNode()); - */ } void RenderingManager::skyDisable () diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index e1e8d6a5c..0a866ac7c 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -416,7 +416,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) void SkyManager::create() { /// \todo preload all the textures and meshes that are used for sky rendering -/* + // Create overlay used for thunderstorm MaterialPtr material = MaterialManager::getSingleton().create( "ThunderMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); Pass* pass = material->getTechnique(0)->getPass(0); @@ -517,6 +517,7 @@ void SkyManager::create() stars_fp->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); stars_fp->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); + /* for (unsigned int i=0; igetNumSubEntities(); ++i) { MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); @@ -531,6 +532,7 @@ void SkyManager::create() mp->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); mStarsMaterials[i] = mp; } + */ // Atmosphere (day) mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); @@ -543,8 +545,9 @@ void SkyManager::create() atmosphere_ent->setVisibilityFlags(RV_Sky); mAtmosphereDay = mRootNode->createChildSceneNode(); mAtmosphereDay->attachObject(atmosphere_ent); - mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); + atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere"); + //mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); + //mAtmosphereMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); // Atmosphere shader HighLevelGpuProgramPtr vshader = mgr.createProgram("Atmosphere_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -570,7 +573,7 @@ void SkyManager::create() vshader->load(); vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); + //mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); HighLevelGpuProgramPtr fshader = mgr.createProgram("Atmosphere_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); @@ -598,7 +601,7 @@ void SkyManager::create() fshader->load(); fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); + // mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); // Clouds NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); @@ -607,8 +610,9 @@ void SkyManager::create() clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); SceneNode* clouds_node = mRootNode->createChildSceneNode(); clouds_node->attachObject(clouds_ent); - mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); - mCloudMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); + //mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); + clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds"); + //mCloudMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); clouds_ent->setCastShadows(false); // Clouds vertex shader @@ -635,7 +639,7 @@ void SkyManager::create() vshader2->setSource(outStream3.str()); vshader2->load(); vshader2->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - mCloudMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader2->getName()); + //mCloudMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader2->getName()); // Clouds fragment shader mCloudFragmentShader = mgr.createProgram("Clouds_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -670,17 +674,19 @@ void SkyManager::create() mCloudFragmentShader->setSource(outStream2.str()); mCloudFragmentShader->load(); mCloudFragmentShader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - mCloudMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(mCloudFragmentShader->getName()); + //mCloudMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(mCloudFragmentShader->getName()); setCloudsOpacity(0.75); ModVertexAlpha(clouds_ent, 1); // I'm not sure if the materials are being used by any other objects // Make a unique "modifiable" copy of the materials to be sure - mCloudMaterial = mCloudMaterial->clone("Clouds"); - clouds_ent->getSubEntity(0)->setMaterial(mCloudMaterial); - mAtmosphereMaterial = mAtmosphereMaterial->clone("Atmosphere"); - atmosphere_ent->getSubEntity(0)->setMaterial(mAtmosphereMaterial); + //mCloudMaterial = mCloudMaterial->clone("Clouds"); + //clouds_ent->getSubEntity(0)->setMaterial(mCloudMaterial); + //mAtmosphereMaterial = mAtmosphereMaterial->clone("Atmosphere"); + //atmosphere_ent->getSubEntity(0)->setMaterial(mAtmosphereMaterial); + + /* mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 0.0); @@ -694,9 +700,9 @@ void SkyManager::create() mCloudMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates(); mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("textures\\tx_sky_cloudy.dds"); mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); +*/ mCreated = true; - */ } SkyManager::~SkyManager() @@ -724,7 +730,7 @@ void SkyManager::update(float duration) if (!mEnabled) return; // UV Scroll the clouds - mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstantFromTime("time", MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); + //mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstantFromTime("time", MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); /// \todo improve this mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); @@ -791,12 +797,14 @@ void SkyManager::setMoonColour (bool red) void SkyManager::setCloudsOpacity(float opacity) { if (!mCreated) return; - mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(opacity)); + //mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(opacity)); } void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mCreated) return; + + /* if (mClouds != weather.mCloudTexture) { mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("textures\\"+weather.mCloudTexture); @@ -857,6 +865,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) mStarsOpacity = weather.mNightFade; } } + */ float strength; float timeofday_angle = std::abs(mSunGlare->getPosition().z/mSunGlare->getPosition().length()); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 164b51daf..76562256f 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -34,6 +34,7 @@ #include #include +#include #include diff --git a/extern/shiny b/extern/shiny index ccaaefa59..1d689a23f 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit ccaaefa59c0500cf790a1f3114c8b5593489afeb +Subproject commit 1d689a23fa685ab680351219c32953a3bed7ac0c diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index e69de29bb..a772e3c5e 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -0,0 +1,38 @@ +#include "core.h" + +#define MRT @shPropertyBool(mrt_output) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shInput(float2, uv0) + shOutput(float2, UV) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + } + +#else + + SH_BEGIN_PROGRAM + shSampler2D(diffuseMap) + shInput(float2, UV) +#if MRT + shDeclareMrtOutput(1) +#endif + shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + + SH_START_PROGRAM + { + shOutputColor(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + +#if MRT + shOutputColor(1) = float4(1,1,1,1); +#endif + } + +#endif diff --git a/files/materials/atmosphere.shaderset b/files/materials/atmosphere.shaderset index e69de29bb..54108dbba 100644 --- a/files/materials/atmosphere.shaderset +++ b/files/materials/atmosphere.shaderset @@ -0,0 +1,15 @@ +shader_set atmosphere_vertex +{ + source atmosphere.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set atmosphere_fragment +{ + source atmosphere.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index e69de29bb..a772e3c5e 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -0,0 +1,38 @@ +#include "core.h" + +#define MRT @shPropertyBool(mrt_output) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shInput(float2, uv0) + shOutput(float2, UV) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + } + +#else + + SH_BEGIN_PROGRAM + shSampler2D(diffuseMap) + shInput(float2, UV) +#if MRT + shDeclareMrtOutput(1) +#endif + shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + + SH_START_PROGRAM + { + shOutputColor(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + +#if MRT + shOutputColor(1) = float4(1,1,1,1); +#endif + } + +#endif diff --git a/files/materials/clouds.shaderset b/files/materials/clouds.shaderset index e69de29bb..5fffb5658 100644 --- a/files/materials/clouds.shaderset +++ b/files/materials/clouds.shaderset @@ -0,0 +1,15 @@ +shader_set clouds_vertex +{ + source clouds.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set clouds_fragment +{ + source clouds.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/materials/objects.mat b/files/materials/objects.mat index fc23b947a..6dfe9db7c 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -39,7 +39,7 @@ material openmw_objects_base texture_unit diffuseMap { - texture $diffuseMap + direct_texture $diffuseMap create_in_ffp true } } diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 6dd05442c..e066b43cc 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -64,7 +64,9 @@ shInput(float, depthPassthrough) #endif +#if MRT shUniform(float far) @shAutoConstant(far, far_clip_distance) +#endif #if LIGHTING shInput(float3, normalPassthrough) @@ -109,12 +111,16 @@ diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); @shEndForeach + +#if HAS_VERTEXCOLOR + ambient *= colorPassthrough.xyz; +#endif shOutputColor(0).xyz *= (ambient + diffuse + materialEmissive.xyz); #endif -#if HAS_VERTEXCOLOR +#if HAS_VERTEXCOLOR && !LIGHTING shOutputColor(0).xyz *= colorPassthrough.xyz; #endif @@ -123,6 +129,9 @@ shOutputColor(0).xyz = shLerp (shOutputColor(0).xyz, fogColor, fogValue); #endif + // prevent negative color output (for example with negative lights) + shOutputColor(0).xyz = max(shOutputColor(0).xyz, float3(0,0,0)); + #if MRT shOutputColor(1) = float4(depthPassthrough / far,1,1,1); #endif diff --git a/files/materials/sky.mat b/files/materials/sky.mat index a82816438..25c9185b7 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -1,10 +1,18 @@ material openmw_moon { + mrt_output true pass { vertex_program moon_vertex fragment_program moon_fragment + polygon_mode_overrideable off + + shader_properties + { + mrt_output $mrt_output + } + texture_unit diffuseMap { texture $diffuseMap @@ -15,10 +23,21 @@ material openmw_moon material openmw_clouds { + mrt_output true pass { vertex_program clouds_vertex fragment_program clouds_fragment + + polygon_mode_overrideable off + + scene_blend alpha_blend + depth_write off + + shader_properties + { + mrt_output $mrt_output + } // second diffuse map is used for weather transitions texture_unit diffuseMap1 @@ -36,19 +55,38 @@ material openmw_clouds material openmw_atmosphere { + mrt_output true pass { vertex_program atmosphere_vertex fragment_program atmosphere_fragment + + polygon_mode_overrideable off + + scene_blend alpha_blend + depth_write off + + shader_properties + { + mrt_output $mrt_output + } } } material openmw_stars { + mrt_output true pass { vertex_program stars_vertex fragment_program stars_fragment + + polygon_mode_overrideable off + + shader_properties + { + mrt_output $mrt_output + } texture_unit diffuseMap { @@ -60,10 +98,18 @@ material openmw_stars // used for both sun and sun glare material openmw_sun { + mrt_output true pass { vertex_program sun_vertex fragment_program sun_fragment + + polygon_mode_overrideable off + + shader_properties + { + mrt_output $mrt_output + } texture unit diffuseMap { diff --git a/files/materials/sun.shader b/files/materials/sun.shader index e69de29bb..811d45031 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -0,0 +1,39 @@ +#include "core.h" + +#define MRT @shPropertyBool(mrt_output) + + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shInput(float2, uv0) + shOutput(float2, UV) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + } + +#else + + SH_BEGIN_PROGRAM + shSampler2D(diffuseMap) + shInput(float2, UV) +#if MRT + shDeclareMrtOutput(1) +#endif + shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + + SH_START_PROGRAM + { + shOutputColor(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + +#if MRT + shOutputColor(1) = float4(1,1,1,1); +#endif + } + +#endif diff --git a/files/materials/sun.shaderset b/files/materials/sun.shaderset index e69de29bb..1b9e92a43 100644 --- a/files/materials/sun.shaderset +++ b/files/materials/sun.shaderset @@ -0,0 +1,15 @@ +shader_set sun_vertex +{ + source sun.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set sun_fragment +{ + source sun.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} From 771863e73bb3d2c68392851daaac630fdc4b078d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Jul 2012 15:50:26 +0200 Subject: [PATCH 058/688] Issue #324: Started turning NpcStats into a proper class; fixed a cmake script bug; fixed a namespace issue --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/npc.cpp | 5 +++-- apps/openmw/mwinput/inputmanager.cpp | 18 +++++++++--------- apps/openmw/mwmechanics/drawstate.hpp | 13 ++++++++----- apps/openmw/mwmechanics/npcstats.cpp | 17 +++++++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 20 +++++++++++++------- apps/openmw/mwworld/player.cpp | 8 ++++---- apps/openmw/mwworld/player.hpp | 4 ++-- 8 files changed, 57 insertions(+), 30 deletions(-) create mode 100644 apps/openmw/mwmechanics/npcstats.cpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 9534ecc90..36e59f6bd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -60,7 +60,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanager stat creaturestats magiceffects movement actors drawstate spells - activespells + activespells npcstats ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d4f711885..3a91ec63f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -233,7 +233,8 @@ namespace MWClass case Combat: - stats.mCombat = set; + // Combat stance ignored for now; need to be determined based on draw state instead of + // being maunally set. break; } } @@ -260,7 +261,7 @@ namespace MWClass case Combat: - return stats.mCombat; + return false; } return false; diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index a2bafcbf7..73cadacd0 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -102,15 +102,15 @@ private: { if (windows.isGuiMode()) return; - DrawState state = player.getDrawState(); - if (state == DrawState_Weapon || state == DrawState_Nothing) + MWMechanics::DrawState state = player.getDrawState(); + if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) { - player.setDrawState(DrawState_Spell); + player.setDrawState(MWMechanics::DrawState_Spell); std::cout << "Player has now readied his hands for spellcasting!\n"; } else { - player.setDrawState(DrawState_Nothing); + player.setDrawState(MWMechanics::DrawState_Nothing); std::cout << "Player does not have any kind of attack ready now.\n"; } } @@ -119,15 +119,15 @@ private: { if (windows.isGuiMode()) return; - DrawState state = player.getDrawState(); - if (state == DrawState_Spell || state == DrawState_Nothing) + MWMechanics::DrawState state = player.getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) { - player.setDrawState(DrawState_Weapon); + player.setDrawState(MWMechanics::DrawState_Weapon); std::cout << "Player is now drawing his weapon.\n"; } else { - player.setDrawState(DrawState_Nothing); + player.setDrawState(MWMechanics::DrawState_Nothing); std::cout << "Player does not have any kind of attack ready now.\n"; } } @@ -336,7 +336,7 @@ private: poller.bind(A_MoveRight, KC_D); poller.bind(A_MoveForward, KC_W); poller.bind(A_MoveBackward, KC_S); - + poller.bind(A_Jump, KC_E); poller.bind(A_Crouch, KC_LCONTROL); } diff --git a/apps/openmw/mwmechanics/drawstate.hpp b/apps/openmw/mwmechanics/drawstate.hpp index 772086d90..94b48fdd8 100644 --- a/apps/openmw/mwmechanics/drawstate.hpp +++ b/apps/openmw/mwmechanics/drawstate.hpp @@ -3,11 +3,14 @@ #undef DrawState -enum DrawState +namespace MWMechanics { - DrawState_Weapon = 0, - DrawState_Spell = 1, - DrawState_Nothing = 2, -}; + enum DrawState + { + DrawState_Weapon = 0, + DrawState_Spell = 1, + DrawState_Nothing = 2, + }; +} #endif diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp new file mode 100644 index 000000000..70db00bb7 --- /dev/null +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -0,0 +1,17 @@ + +#include "npcstats.hpp" + +MWMechanics::NpcStats::NpcStats() +: mForceRun (false), mForceSneak (false), mRun (false), mSneak (false), + mDrawState (DrawState_Nothing) +{} + +MWMechanics::DrawState MWMechanics::NpcStats::getDrawState() const +{ + return mDrawState; +} + +void MWMechanics::NpcStats::setDrawState (DrawState state) +{ + mDrawState = state; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 972863b72..c2907ccb5 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "stat.hpp" #include "drawstate.hpp" @@ -16,10 +17,14 @@ namespace MWMechanics /// \note For technical reasons the spell list and the currently selected spell is also handled by /// CreatureStats, even though they are actually NPC stats. - struct NpcStats + class NpcStats { - // NPCs other than the player can only have one faction. But for the sake of consistency - // we use the same data structure for the PC and the NPCs. + DrawState mDrawState; + + public: + + /// NPCs other than the player can only have one faction. But for the sake of consistency + /// we use the same data structure for the PC and the NPCs. /// \note the faction key must be in lowercase std::map mFactionRank; @@ -29,11 +34,12 @@ namespace MWMechanics bool mForceSneak; bool mRun; bool mSneak; - bool mCombat; - DrawState mDrawState; - NpcStats() : mForceRun (false), mForceSneak (false), mRun (false), mSneak (false), - mCombat (false) , mDrawState(DrawState_Nothing) {} + NpcStats(); + + DrawState getDrawState() const; + + void setDrawState (DrawState state); }; } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 0eeebad4e..d8b3285ef 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -56,10 +56,10 @@ namespace MWWorld mClass = new_class; } - void Player::setDrawState(const DrawState& value) + void Player::setDrawState (MWMechanics::DrawState state) { MWWorld::Ptr ptr = getPlayer(); - MWWorld::Class::get(ptr).getNpcStats(ptr).mDrawState = value; + MWWorld::Class::get(ptr).getNpcStats(ptr).setDrawState (state); } void Player::setAutoMove (bool enable) @@ -111,10 +111,10 @@ namespace MWWorld MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); } - DrawState Player::getDrawState() + MWMechanics::DrawState Player::getDrawState() { MWWorld::Ptr ptr = getPlayer(); - return MWWorld::Class::get(ptr).getNpcStats(ptr).mDrawState; + return MWWorld::Class::get(ptr).getNpcStats(ptr).getDrawState(); } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index b1692bd81..2c87cf91b 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -83,7 +83,7 @@ namespace MWWorld void setClass (const ESM::Class& class_); - void setDrawState(const DrawState& state); + void setDrawState (MWMechanics::DrawState state); std::string getName() const { @@ -115,7 +115,7 @@ namespace MWWorld return mAutoMove; } - DrawState getDrawState(); + MWMechanics::DrawState getDrawState(); /// \todo constness void setAutoMove (bool enable); From d30ba14a176956757610206f2231ec15bd222243 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Jul 2012 18:23:48 +0200 Subject: [PATCH 059/688] Issue #324: Finished turning NpcStats into a proper class --- apps/openmw/mwclass/npc.cpp | 22 +++---- apps/openmw/mwdialogue/dialoguemanager.cpp | 18 +++--- apps/openmw/mwgui/stats_window.cpp | 2 +- apps/openmw/mwmechanics/mechanicsmanager.cpp | 20 +++---- apps/openmw/mwmechanics/npcstats.cpp | 44 +++++++++++++- apps/openmw/mwmechanics/npcstats.hpp | 41 +++++++++---- apps/openmw/mwmechanics/spellsuccess.hpp | 4 +- apps/openmw/mwscript/controlextensions.cpp | 62 ++++++++------------ apps/openmw/mwscript/statsextensions.cpp | 30 +++++----- apps/openmw/mwworld/inventorystore.cpp | 4 +- 10 files changed, 146 insertions(+), 101 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3a91ec63f..d54b9441d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -67,11 +67,11 @@ namespace MWClass boost::algorithm::to_lower(faction); if(ref->base->npdt52.gold != -10) { - data->mNpcStats.mFactionRank[faction] = (int)ref->base->npdt52.rank; + data->mNpcStats.getFactionRanks()[faction] = (int)ref->base->npdt52.rank; } else { - data->mNpcStats.mFactionRank[faction] = (int)ref->base->npdt12.rank; + data->mNpcStats.getFactionRanks()[faction] = (int)ref->base->npdt12.rank; } } @@ -79,7 +79,7 @@ namespace MWClass if(ref->base->npdt52.gold != -10) { for (int i=0; i<27; ++i) - data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]); + data->mNpcStats.getSkill (i).setBase (ref->base->npdt52.skills[i]); data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength); data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence); @@ -201,12 +201,12 @@ namespace MWClass { case Run: - stats.mForceRun = force; + stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceRun, force); break; case Sneak: - stats.mForceSneak = force; + stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak, force); break; case Combat: @@ -223,12 +223,12 @@ namespace MWClass { case Run: - stats.mRun = set; + stats.setMovementFlag (MWMechanics::NpcStats::Flag_Run, set); break; case Sneak: - stats.mSneak = set; + stats.setMovementFlag (MWMechanics::NpcStats::Flag_Sneak, set); break; case Combat: @@ -247,17 +247,17 @@ namespace MWClass { case Run: - if (!ignoreForce && stats.mForceRun) + if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)) return true; - return stats.mRun; + return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Run); case Sneak: - if (!ignoreForce && stats.mForceSneak) + if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)) return true; - return stats.mSneak; + return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Sneak); case Combat: diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 98562c053..64a976d40 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -203,10 +203,10 @@ namespace MWDialogue MWMechanics::NpcStats PCstats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); MWMechanics::NpcStats NPCstats = MWWorld::Class::get(actor).getNpcStats(actor); int sameFaction = 0; - if(!NPCstats.mFactionRank.empty()) + if(!NPCstats.getFactionRanks().empty()) { - std::string NPCFaction = NPCstats.mFactionRank.begin()->first; - if(PCstats.mFactionRank.find(toLower(NPCFaction)) != PCstats.mFactionRank.end()) sameFaction = 1; + std::string NPCFaction = NPCstats.getFactionRanks().begin()->first; + if(PCstats.getFactionRanks().find(toLower(NPCFaction)) != PCstats.getFactionRanks().end()) sameFaction = 1; } if(!selectCompare(comp,sameFaction,select.i)) return false; } @@ -525,8 +525,8 @@ namespace MWDialogue //MWWorld::Class npcClass = MWWorld::Class::get(actor); MWMechanics::NpcStats stats = MWWorld::Class::get(actor).getNpcStats(actor); - std::map::iterator it = stats.mFactionRank.find(toLower(info.npcFaction)); - if(it!=stats.mFactionRank.end()) + std::map::iterator it = stats.getFactionRanks().find(toLower(info.npcFaction)); + if(it!=stats.getFactionRanks().end()) { //check rank if(it->second < (int)info.data.rank) return false; @@ -542,8 +542,8 @@ namespace MWDialogue if(!info.pcFaction.empty()) { MWMechanics::NpcStats stats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - std::map::iterator it = stats.mFactionRank.find(toLower(info.pcFaction)); - if(it!=stats.mFactionRank.end()) + std::map::iterator it = stats.getFactionRanks().find(toLower(info.pcFaction)); + if(it!=stats.getFactionRanks().end()) { //check rank if(it->second < (int)info.data.PCrank) return false; @@ -903,13 +903,13 @@ namespace MWDialogue std::string factionID(""); MWMechanics::NpcStats stats = MWWorld::Class::get(mActor).getNpcStats(mActor); - if(stats.mFactionRank.empty()) + if(stats.getFactionRanks().empty()) { std::cout << "No faction for this actor!"; } else { - factionID = stats.mFactionRank.begin()->first; + factionID = stats.getFactionRanks().begin()->first; } return factionID; } diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 0f8b41b28..be99982d9 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -270,7 +270,7 @@ void StatsWindow::onFrame () MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); - setFactions(PCstats.mFactionRank); + setFactions(PCstats.getFactionRanks()); setBirthSign(MWBase::Environment::get().getWorld()->getPlayer().getBirthsign()); diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanager.cpp index 13052d064..ada05a11b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.cpp @@ -28,7 +28,7 @@ namespace MWMechanics creatureStats.mMagicEffects = MagicEffects(); for (int i=0; i<27; ++i) - npcStats.mSkill[i].setBase (player->npdt52.skills[i]); + npcStats.getSkill (i).setBase (player->npdt52.skills[i]); creatureStats.mAttributes[0].setBase (player->npdt52.strength); creatureStats.mAttributes[1].setBase (player->npdt52.intelligence); @@ -73,8 +73,8 @@ namespace MWMechanics if (index>=0 && index<27) { - npcStats.mSkill[index].setBase ( - npcStats.mSkill[index].getBase() + race->data.bonus[i].bonus); + npcStats.getSkill (index).setBase ( + npcStats.getSkill (index).getBase() + race->data.bonus[i].bonus); } } @@ -124,8 +124,8 @@ namespace MWMechanics if (index>=0 && index<27) { - npcStats.mSkill[index].setBase ( - npcStats.mSkill[index].getBase() + bonus); + npcStats.getSkill (index).setBase ( + npcStats.getSkill (index).getBase() + bonus); } } } @@ -141,8 +141,8 @@ namespace MWMechanics if (index>=0 && index<27) { - npcStats.mSkill[index].setBase ( - npcStats.mSkill[index].getBase() + 5); + npcStats.getSkill (index).setBase ( + npcStats.getSkill (index).getBase() + 5); } } } @@ -236,11 +236,11 @@ namespace MWMechanics //Loop over ESM::Skill::SkillEnum for(int i = 0; i < 27; ++i) { - if(npcStats.mSkill[i] != mWatchedNpc.mSkill[i]) + if(npcStats.getSkill (i) != mWatchedNpc.getSkill (i)) { update = true; - mWatchedNpc.mSkill[i] = npcStats.mSkill[i]; - MWBase::Environment::get().getWindowManager()->setValue((ESM::Skill::SkillEnum)i, npcStats.mSkill[i]); + mWatchedNpc.getSkill (i) = npcStats.getSkill (i); + MWBase::Environment::get().getWindowManager()->setValue((ESM::Skill::SkillEnum)i, npcStats.getSkill (i)); } } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 70db00bb7..cdb977029 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -1,9 +1,10 @@ #include "npcstats.hpp" +#include + MWMechanics::NpcStats::NpcStats() -: mForceRun (false), mForceSneak (false), mRun (false), mSneak (false), - mDrawState (DrawState_Nothing) +: mMovementFlags (0), mDrawState (DrawState_Nothing) {} MWMechanics::DrawState MWMechanics::NpcStats::getDrawState() const @@ -15,3 +16,42 @@ void MWMechanics::NpcStats::setDrawState (DrawState state) { mDrawState = state; } + +bool MWMechanics::NpcStats::getMovementFlag (Flag flag) const +{ + return mMovementFlags & flag; +} + +void MWMechanics::NpcStats::setMovementFlag (Flag flag, bool state) +{ + if (state) + mMovementFlags |= flag; + else + mMovementFlags &= ~flag; +} + +const MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) const +{ + if (index<0 || index>=27) + throw std::runtime_error ("skill index out of range"); + + return mSkill[index]; +} + +MWMechanics::Stat& MWMechanics::NpcStats::getSkill (int index) +{ + if (index<0 || index>=27) + throw std::runtime_error ("skill index out of range"); + + return mSkill[index]; +} + +std::map& MWMechanics::NpcStats::getFactionRanks() +{ + return mFactionRank; +} + +const std::map& MWMechanics::NpcStats::getFactionRanks() const +{ + return mFactionRank; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index c2907ccb5..16a777b71 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -19,27 +19,46 @@ namespace MWMechanics class NpcStats { - DrawState mDrawState; - public: - /// NPCs other than the player can only have one faction. But for the sake of consistency - /// we use the same data structure for the PC and the NPCs. - /// \note the faction key must be in lowercase - std::map mFactionRank; + enum Flag + { + Flag_ForceRun = 1, + Flag_ForceSneak = 2, + Flag_Run = 4, + Flag_Sneak = 8 + }; - Stat mSkill[27]; + private: - bool mForceRun; - bool mForceSneak; - bool mRun; - bool mSneak; + /// NPCs other than the player can only have one faction. But for the sake of consistency + /// we use the same data structure for the PC and the NPCs. + /// \note the faction key must be in lowercase + std::map mFactionRank; + + DrawState mDrawState; + unsigned int mMovementFlags; + Stat mSkill[27]; + + public: NpcStats(); DrawState getDrawState() const; void setDrawState (DrawState state); + + bool getMovementFlag (Flag flag) const; + + void setMovementFlag (Flag flag, bool state); + + const Stat& getSkill (int index) const; + + Stat& getSkill (int index); + + std::map& getFactionRanks(); + + const std::map& getFactionRanks() const; }; } diff --git a/apps/openmw/mwmechanics/spellsuccess.hpp b/apps/openmw/mwmechanics/spellsuccess.hpp index 54172a72a..42a7d5bba 100644 --- a/apps/openmw/mwmechanics/spellsuccess.hpp +++ b/apps/openmw/mwmechanics/spellsuccess.hpp @@ -41,7 +41,7 @@ namespace MWMechanics { const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().magicEffects.find(it->effectID); int _school = effect->data.school; - int _skillLevel = stats.mSkill[spellSchoolToSkill(_school)].getModified(); + int _skillLevel = stats.getSkill (spellSchoolToSkill(_school)).getModified(); if (school == -1) { @@ -78,7 +78,7 @@ namespace MWMechanics NpcStats& stats = MWWorld::Class::get(actor).getNpcStats(actor); CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); - int skillLevel = stats.mSkill[getSpellSchool(spellId, actor)].getModified(); + int skillLevel = stats.getSkill (getSpellSchool(spellId, actor)).getModified(); // Sound magic effect (reduces spell casting chance) int soundMagnitude = creatureStats.mMagicEffects.get (MWMechanics::EffectKey (48)).mMagnitude; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 1f5bdcafb..24d8bcf1e 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -59,54 +59,36 @@ namespace MWScript }; template - class OpClearForceRun : public Interpreter::Opcode0 + class OpClearMovementFlag : public Interpreter::Opcode0 { + MWMechanics::NpcStats::Flag mFlag; + public: + OpClearMovementFlag (MWMechanics::NpcStats::Flag flag) : mFlag (flag) {} + virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - MWWorld::Class::get (ptr).getNpcStats (ptr).mForceRun = false; + MWWorld::Class::get (ptr).getNpcStats (ptr).setMovementFlag (mFlag, false); } }; template - class OpForceRun : public Interpreter::Opcode0 + class OpSetMovementFlag : public Interpreter::Opcode0 { + MWMechanics::NpcStats::Flag mFlag; + public: + OpSetMovementFlag (MWMechanics::NpcStats::Flag flag) : mFlag (flag) {} + virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - MWWorld::Class::get (ptr).getNpcStats (ptr).mForceRun = true; - } - }; - - template - class OpClearForceSneak : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = R()(runtime); - - MWWorld::Class::get (ptr).getNpcStats (ptr).mForceSneak = false; - } - }; - - template - class OpForceSneak : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = R()(runtime); - - MWWorld::Class::get (ptr).getNpcStats (ptr).mForceSneak = true; + MWWorld::Class::get (ptr).getNpcStats (ptr).setMovementFlag (mFlag, true); } }; @@ -165,19 +147,23 @@ namespace MWScript interpreter.installSegment5 (opcodeToggleCollision, new OpToggleCollision); - interpreter.installSegment5 (opcodeClearForceRun, new OpClearForceRun); - interpreter.installSegment5 (opcodeForceRun, new OpForceRun); - interpreter.installSegment5 (opcodeClearForceSneak, new OpClearForceSneak); - interpreter.installSegment5 (opcodeForceSneak, new OpForceSneak); + interpreter.installSegment5 (opcodeClearForceRun, + new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + interpreter.installSegment5 (opcodeForceRun, + new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + interpreter.installSegment5 (opcodeClearForceSneak, + new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + interpreter.installSegment5 (opcodeForceSneak, + new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); interpreter.installSegment5 (opcodeClearForceRunExplicit, - new OpClearForceRun); + new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); interpreter.installSegment5 (opcodeForceRunExplicit, - new OpForceRun); + new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); interpreter.installSegment5 (opcodeClearForceSneakExplicit, - new OpClearForceSneak); + new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); interpreter.installSegment5 (opcodeForceSneakExplicit, - new OpForceSneak); + new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); } } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 15519b3a6..42781d766 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -237,7 +237,7 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = - MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex]. + MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). getModified(); runtime.push (value); @@ -260,7 +260,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex]. + MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). setModified (value, 0); } }; @@ -281,10 +281,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - value += MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex]. + value += MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). getModified(); - MWWorld::Class::get (ptr).getNpcStats (ptr).mSkill[mIndex]. + MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). setModified (value, 0, 100); } }; @@ -373,9 +373,9 @@ namespace MWScript if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - if(MWWorld::Class::get(player).getNpcStats(player).mFactionRank.find(factionID) == MWWorld::Class::get(player).getNpcStats(player).mFactionRank.end()) + if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) == MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { - MWWorld::Class::get(player).getNpcStats(player).mFactionRank[factionID] = 0; + MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = 0; } } } @@ -402,13 +402,13 @@ namespace MWScript if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - if(MWWorld::Class::get(player).getNpcStats(player).mFactionRank.find(factionID) == MWWorld::Class::get(player).getNpcStats(player).mFactionRank.end()) + if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) == MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { - MWWorld::Class::get(player).getNpcStats(player).mFactionRank[factionID] = 0; + MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = 0; } else { - MWWorld::Class::get(player).getNpcStats(player).mFactionRank[factionID] = MWWorld::Class::get(player).getNpcStats(player).mFactionRank[factionID] +1; + MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] +1; } } } @@ -435,9 +435,9 @@ namespace MWScript if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - if(MWWorld::Class::get(player).getNpcStats(player).mFactionRank.find(factionID) != MWWorld::Class::get(player).getNpcStats(player).mFactionRank.end()) + if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) != MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { - MWWorld::Class::get(player).getNpcStats(player).mFactionRank[factionID] = MWWorld::Class::get(player).getNpcStats(player).mFactionRank[factionID] -1; + MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] = MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID] -1; } } } @@ -460,22 +460,22 @@ namespace MWScript } else { - if(MWWorld::Class::get(ptr).getNpcStats(ptr).mFactionRank.empty()) + if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) { //throw exception? } else { - factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).mFactionRank.begin()->first; + factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } boost::algorithm::to_lower(factionID); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if(factionID!="") { - if(MWWorld::Class::get(player).getNpcStats(player).mFactionRank.find(factionID) != MWWorld::Class::get(player).getNpcStats(player).mFactionRank.end()) + if(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().find(factionID) != MWWorld::Class::get(player).getNpcStats(player).getFactionRanks().end()) { - runtime.push(MWWorld::Class::get(player).getNpcStats(player).mFactionRank[factionID]); + runtime.push(MWWorld::Class::get(player).getNpcStats(player).getFactionRanks()[factionID]); } else { diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 9d99227ed..2fd702f6d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -161,10 +161,10 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) if (testSkill!=-1 || oldSkill!=-1 || testSkill!=oldSkill) { - if (stats.mSkill[oldSkill].getModified()>stats.mSkill[testSkill].getModified()) + if (stats.getSkill (oldSkill).getModified()>stats.getSkill (testSkill).getModified()) continue; // rejected, because old item better matched the NPC's skills. - if (stats.mSkill[oldSkill].getModified() Date: Fri, 6 Jul 2012 18:25:16 +0200 Subject: [PATCH 060/688] Issue #324: Change a forward declartion to avoid potential problems with pesky MSVC --- apps/openmw/mwworld/class.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fe7b5dc80..8f2d19b1c 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -22,7 +22,7 @@ namespace MWRender namespace MWMechanics { struct CreatureStats; - struct NpcStats; + class NpcStats; struct Movement; } From 0f41cc499dba44f07c8d92877f81f761ed8c824b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Jul 2012 21:07:04 +0200 Subject: [PATCH 061/688] Issue #324: Added skill gain calculation function --- apps/openmw/mwmechanics/npcstats.cpp | 68 ++++++++++++++++++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 11 +++++ 2 files changed, 79 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index cdb977029..6c95c5372 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -3,6 +3,15 @@ #include +#include +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + MWMechanics::NpcStats::NpcStats() : mMovementFlags (0), mDrawState (DrawState_Nothing) {} @@ -55,3 +64,62 @@ const std::map& MWMechanics::NpcStats::getFactionRanks() const { return mFactionRank; } + +float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& class_, int usageType, + int level) const +{ + if (level<0) + level = static_cast (getSkill (skillIndex).getBase()); + + const ESM::Skill *skill = MWBase::Environment::get().getWorld()->getStore().skills.find (skillIndex); + + float skillFactor = 1; + + if (usageType>=4) + throw std::runtime_error ("skill usage type out of range"); + + if (usageType>0) + { + skillFactor = skill->data.useValue[usageType]; + + if (skillFactor<=0) + throw std::runtime_error ("invalid skill gain factor"); + } + + float typeFactor = + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMiscSkillBonus")->f; + + for (int i=0; i<5; ++i) + if (class_.data.skills[i][0]==skillIndex) + { + typeFactor = + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMinorSkillBonus")->f; + + break; + } + + for (int i=0; i<5; ++i) + if (class_.data.skills[i][1]==skillIndex) + { + typeFactor = + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMajorSkillBonus")->f; + + break; + } + + if (typeFactor<=0) + throw std::runtime_error ("invalid skill type factor"); + + float specialisationFactor = 1; + + if (skill->data.specialization==class_.data.specialization) + { + specialisationFactor = + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fSpecialSkillBonus")->f; + + if (specialisationFactor<=0) + throw std::runtime_error ("invalid skill specialisation factor"); + } + + return 1.0 / (level +1) * (1.0 / skillFactor) * typeFactor * specialisationFactor; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 16a777b71..026404272 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -8,6 +8,11 @@ #include "stat.hpp" #include "drawstate.hpp" +namespace ESM +{ + struct Class; +} + namespace MWMechanics { /// \brief Additional stats for NPCs @@ -59,6 +64,12 @@ namespace MWMechanics std::map& getFactionRanks(); const std::map& getFactionRanks() const; + + float getSkillGain (int skillIndex, const ESM::Class& class_, int usageType = -1, + int level = -1) const; + ///< \param usageType: Usage specific factor, specified in the respective skill record; + /// -1: use a factor of 1.0 instead. + /// \param level Level to base calculation on; -1: use current level. }; } From 113457d9348ded20223560994bbf2ec55b152b9e Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 7 Jul 2012 01:14:18 +0200 Subject: [PATCH 062/688] Fixed some windows issues and got rid of a few tiny warnings while at it. --- apps/openmw/mwdialogue/dialoguemanager.cpp | 2 +- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/referenceinterface.cpp | 4 ++++ apps/openmw/mwgui/referenceinterface.hpp | 1 + apps/openmw/mwgui/tooltips.cpp | 2 +- apps/openmw/mwsound/soundmanager.cpp | 2 +- apps/openmw/mwworld/cells.cpp | 2 +- apps/openmw/mwworld/player.hpp | 3 +++ apps/openmw/mwworld/worldimp.hpp | 2 +- 9 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 98562c053..41ffd1e93 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -706,7 +706,7 @@ namespace MWDialogue } return false; } - catch (const Compiler::SourceException& error) + catch (const Compiler::SourceException& /* error */) { // error has already been reported via error handler } diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index d20a11009..78eefa338 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -286,7 +286,7 @@ void HUD::onWorldClicked(MyGUI::Widget* _sender) { object = MWBase::Environment::get().getWorld()->getPtrViaHandle(handle); } - catch (std::exception& e) + catch (std::exception& /* e */) { return; } diff --git a/apps/openmw/mwgui/referenceinterface.cpp b/apps/openmw/mwgui/referenceinterface.cpp index f891b3338..b1f7affb6 100644 --- a/apps/openmw/mwgui/referenceinterface.cpp +++ b/apps/openmw/mwgui/referenceinterface.cpp @@ -12,6 +12,10 @@ namespace MWGui { } + ReferenceInterface::~ReferenceInterface() + { + } + void ReferenceInterface::checkReferenceAvailable() { if (mPtr.isEmpty()) diff --git a/apps/openmw/mwgui/referenceinterface.hpp b/apps/openmw/mwgui/referenceinterface.hpp index aba9071f3..39574d0f7 100644 --- a/apps/openmw/mwgui/referenceinterface.hpp +++ b/apps/openmw/mwgui/referenceinterface.hpp @@ -13,6 +13,7 @@ namespace MWGui { public: ReferenceInterface(); + virtual ~ReferenceInterface(); void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 7920d88a3..679c7a59e 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -81,7 +81,7 @@ void ToolTips::onFrame(float frameDuration) { mFocusObject = MWBase::Environment::get().getWorld()->getPtrViaHandle(handle); } - catch (std::exception& e) + catch (std::exception /* & e */) { return; } diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 29563ecc1..c6332d9f3 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -90,7 +90,7 @@ namespace MWSound { if(devname.empty()) throw; - std::cout <<"Failed to open device \""<init(); Settings::Manager::setString("device", "Sound", ""); } diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 74a91f25f..cd7ebf79a 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -91,7 +91,7 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS MWWorld::Cells::Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader) : mStore (store), mReader (reader), - mIdCache (20, std::pair ("", 0)), /// \todo make cache size configurable + mIdCache (20, std::pair ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable mIdCacheIndex (0) {} diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index b1692bd81..6f6ee93bc 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -9,6 +9,9 @@ #include "../mwmechanics/drawstate.hpp" +#undef DrawState // How did this get defined again? + // Maybe it's defined by default in every file for windows? + namespace MWBase { class World; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d64b3a373..62a24000e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -188,7 +188,7 @@ namespace MWWorld virtual bool toggleSky(); ///< \return Resulting mode - virtual void changeWeather (const std::string& region, const unsigned int id); + virtual void changeWeather (const std::string& region, unsigned int id); virtual int getCurrentWeather() const; From e517e75d0984b16f94c03bd90869032aa3925447 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 7 Jul 2012 01:25:14 +0200 Subject: [PATCH 063/688] update --- components/nifogre/ogre_nif_loader.cpp | 2 +- extern/shiny | 2 +- files/materials/objects.mat | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 76562256f..d12a78114 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -270,7 +270,7 @@ void NIFLoader::createMaterial(const String &name, { // Enable transparency instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); - instance->setProperty("depth_write", sh::makeProperty(new sh::BooleanValue(false))); + instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue("off"))); } } else diff --git a/extern/shiny b/extern/shiny index 1d689a23f..88a192a67 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 1d689a23fa685ab680351219c32953a3bed7ac0c +Subproject commit 88a192a67d7dff636be4540be4a8654ad33f106a diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 6dfe9db7c..58c9a17bc 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -12,6 +12,7 @@ material openmw_objects_base is_transparent false // real transparency, alpha rejection doesn't count here scene_blend default + depth_write default alpha_rejection default pass @@ -34,6 +35,7 @@ material openmw_objects_base emissive $emissive scene_blend $scene_blend alpha_rejection $alpha_rejection + depth_write $depth_write ffp_vertex_colour_ambient $has_vertex_colour From fe86ce5a2c263b72c85a592e2e8f47dfa613fc80 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 7 Jul 2012 20:53:19 +0200 Subject: [PATCH 064/688] DrawState workaround --- apps/openmw/mwinput/inputmanager.cpp | 4 ++-- apps/openmw/mwmechanics/drawstate.hpp | 5 ++--- apps/openmw/mwmechanics/npcstats.cpp | 4 ++-- apps/openmw/mwmechanics/npcstats.hpp | 6 +++--- apps/openmw/mwworld/player.cpp | 4 ++-- apps/openmw/mwworld/player.hpp | 7 ++----- 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 73cadacd0..3a8315b71 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -102,7 +102,7 @@ private: { if (windows.isGuiMode()) return; - MWMechanics::DrawState state = player.getDrawState(); + MWMechanics::DrawState_ state = player.getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) { player.setDrawState(MWMechanics::DrawState_Spell); @@ -119,7 +119,7 @@ private: { if (windows.isGuiMode()) return; - MWMechanics::DrawState state = player.getDrawState(); + MWMechanics::DrawState_ state = player.getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) { player.setDrawState(MWMechanics::DrawState_Weapon); diff --git a/apps/openmw/mwmechanics/drawstate.hpp b/apps/openmw/mwmechanics/drawstate.hpp index 94b48fdd8..112b6e4f9 100644 --- a/apps/openmw/mwmechanics/drawstate.hpp +++ b/apps/openmw/mwmechanics/drawstate.hpp @@ -1,11 +1,10 @@ #ifndef GAME_MWMECHANICS_DRAWSTATE_H #define GAME_MWMECHANICS_DRAWSTATE_H -#undef DrawState - namespace MWMechanics { - enum DrawState + /// \note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die! + enum DrawState_ { DrawState_Weapon = 0, DrawState_Spell = 1, diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 6c95c5372..08ac12374 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -16,12 +16,12 @@ MWMechanics::NpcStats::NpcStats() : mMovementFlags (0), mDrawState (DrawState_Nothing) {} -MWMechanics::DrawState MWMechanics::NpcStats::getDrawState() const +MWMechanics::DrawState_ MWMechanics::NpcStats::getDrawState() const { return mDrawState; } -void MWMechanics::NpcStats::setDrawState (DrawState state) +void MWMechanics::NpcStats::setDrawState (DrawState_ state) { mDrawState = state; } diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 026404272..1dccdd0d6 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -41,7 +41,7 @@ namespace MWMechanics /// \note the faction key must be in lowercase std::map mFactionRank; - DrawState mDrawState; + DrawState_ mDrawState; unsigned int mMovementFlags; Stat mSkill[27]; @@ -49,9 +49,9 @@ namespace MWMechanics NpcStats(); - DrawState getDrawState() const; + DrawState_ getDrawState() const; - void setDrawState (DrawState state); + void setDrawState (DrawState_ state); bool getMovementFlag (Flag flag) const; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index d8b3285ef..4d508c3e9 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -56,7 +56,7 @@ namespace MWWorld mClass = new_class; } - void Player::setDrawState (MWMechanics::DrawState state) + void Player::setDrawState (MWMechanics::DrawState_ state) { MWWorld::Ptr ptr = getPlayer(); MWWorld::Class::get(ptr).getNpcStats(ptr).setDrawState (state); @@ -111,7 +111,7 @@ namespace MWWorld MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); } - MWMechanics::DrawState Player::getDrawState() + MWMechanics::DrawState_ Player::getDrawState() { MWWorld::Ptr ptr = getPlayer(); return MWWorld::Class::get(ptr).getNpcStats(ptr).getDrawState(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 2a02635b4..ee7c030a5 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -9,9 +9,6 @@ #include "../mwmechanics/drawstate.hpp" -#undef DrawState // How did this get defined again? - // Maybe it's defined by default in every file for windows? - namespace MWBase { class World; @@ -86,7 +83,7 @@ namespace MWWorld void setClass (const ESM::Class& class_); - void setDrawState (MWMechanics::DrawState state); + void setDrawState (MWMechanics::DrawState_ state); std::string getName() const { @@ -118,7 +115,7 @@ namespace MWWorld return mAutoMove; } - MWMechanics::DrawState getDrawState(); /// \todo constness + MWMechanics::DrawState_ getDrawState(); /// \todo constness void setAutoMove (bool enable); From e6716c25c382514eb06e6aeaaf4b2562e34fde7e Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 9 Jul 2012 15:41:19 +0200 Subject: [PATCH 065/688] little correction. --- 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 b623cd527..155bc8d81 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -604,7 +604,7 @@ namespace MWWorld ptr.getCellRef().scale = scale; scale = scale/ptr.getRefData().getBaseNode()->getScale().x; ptr.getRefData().getBaseNode()->setScale(scale,scale,scale); - mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); + mPhysics->scaleObject( ptr.getRefData().getHandle(), scale ); } void World::rotateObject (Ptr& ptr,float x,float y,float z,bool WorldAxis) @@ -636,7 +636,7 @@ namespace MWWorld ptr.getRefData().getPosition().rot[0] = ry.valueRadians(); ptr.getRefData().getPosition().rot[0] = rz.valueRadians(); - mPhysics->rotateObject(Class::get(ptr).getId(ptr),ptr.getRefData().getBaseNode()->getOrientation()); + mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); //ptr.getRefData().getBaseNode()->rotate(ptr.getRefData().getBaseNode()->get //mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); } From 0a67f60a6e334a942cda7f5391b41fcf948e3d06 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 9 Jul 2012 18:47:59 +0200 Subject: [PATCH 066/688] Clean-up --- apps/openmw/mwbase/world.hpp | 6 +- apps/openmw/mwscript/extensions.cpp | 3 + apps/openmw/mwscript/statsextensions.cpp | 57 ------------ .../mwscript/transformationextensions.cpp | 89 +++++++++++++++++++ .../mwscript/transformationextensions.hpp | 25 ++++++ apps/openmw/mwworld/worldimp.cpp | 41 +-------- apps/openmw/mwworld/worldimp.hpp | 6 +- 7 files changed, 124 insertions(+), 103 deletions(-) create mode 100644 apps/openmw/mwscript/transformationextensions.cpp create mode 100644 apps/openmw/mwscript/transformationextensions.hpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 0dcf440f0..51d0a4f8e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -178,11 +178,9 @@ namespace MWBase virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; - virtual void scaleObject (MWWorld::Ptr& ptr, float scale) = 0; + virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; - virtual void rotateObject (MWWorld::Ptr& ptr,float x,float y,float z,bool WorldAxis) = 0; - - virtual void setObjectRotation (MWWorld::Ptr& ptr,float x,float y,float z) = 0; + virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z) = 0; virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const = 0; diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp index 197494146..b7425aca0 100644 --- a/apps/openmw/mwscript/extensions.cpp +++ b/apps/openmw/mwscript/extensions.cpp @@ -15,6 +15,7 @@ #include "controlextensions.hpp" #include "dialogueextensions.hpp" #include "animationextensions.hpp" +#include "transformationextensions.hpp" namespace MWScript { @@ -31,6 +32,7 @@ namespace MWScript Control::registerExtensions (extensions); Dialogue::registerExtensions (extensions); Animation::registerExtensions (extensions); + Transformation::registerExtensions (extensions); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -47,5 +49,6 @@ namespace MWScript Control::installOpcodes (interpreter); Dialogue::installOpcodes (interpreter); Animation::installOpcodes (interpreter); + Transformation::installOpcodes (interpreter); } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 17cd184d7..4e41ae81d 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -505,50 +505,6 @@ namespace MWScript } }; - template - class OpSetScale : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = R()(runtime); - - Interpreter::Type_Float scale = runtime[0].mInteger; - runtime.pop(); - - MWBase::Environment::get().getWorld()->scaleObject(ptr,scale); - } - }; - - template - class OpSetAngle : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = R()(runtime); - - std::string axis = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - Interpreter::Type_Float angle = runtime[0].mInteger; - runtime.pop(); - - if(axis == "X") - { - MWBase::Environment::get().getWorld()->setObjectRotation(ptr,angle,0,0); - } - if(axis == "Y") - { - MWBase::Environment::get().getWorld()->setObjectRotation(ptr,0,angle,0); - } - if(axis == "Z") - { - MWBase::Environment::get().getWorld()->setObjectRotation(ptr,0,0,angle); - } - } - }; const int numberOfAttributes = 8; @@ -596,11 +552,6 @@ namespace MWScript const int opcodeModDisposition = 0x200014d; const int opcodeModDispositionExplicit = 0x200014e; - const int opcodeSetScale = 0x2000164; - const int opcodeSetScaleExplicit = 0x2000165; - const int opcodeSetAngle = 0x2000166; - const int opcodeSetAngleExplicit = 0x2000167; - void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -683,9 +634,6 @@ namespace MWScript extensions.registerInstruction("moddisposition","l",opcodeModDisposition, opcodeModDispositionExplicit); extensions.registerFunction("getpcrank",'l',"/S",opcodeGetPCRank,opcodeGetPCRankExplicit); - - extensions.registerInstruction("setscale","l",opcodeSetScale,opcodeSetScaleExplicit); - extensions.registerInstruction("setangle","Sl",opcodeSetAngle,opcodeSetAngleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -757,11 +705,6 @@ namespace MWScript interpreter.installSegment5(opcodeModDispositionExplicit,new OpModDisposition); interpreter.installSegment3(opcodeGetPCRank,new OpGetPCRank); interpreter.installSegment3(opcodeGetPCRankExplicit,new OpGetPCRank); - - interpreter.installSegment5(opcodeSetScale,new OpSetScale); - interpreter.installSegment5(opcodeSetScaleExplicit,new OpSetScale); - interpreter.installSegment5(opcodeSetAngle,new OpSetAngle); - interpreter.installSegment5(opcodeSetAngleExplicit,new OpSetAngle); } } } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp new file mode 100644 index 000000000..14181a06e --- /dev/null +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -0,0 +1,89 @@ + +#include "statsextensions.hpp" + +#include + +#include + +#include + +#include +#include +#include + +#include "../mwbase/environment.hpp" + +#include "../mwworld/class.hpp" + +#include "interpretercontext.hpp" +#include "ref.hpp" + +namespace MWScript +{ + namespace Transformation + { + template + class OpSetScale : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Float scale = runtime[0].mInteger; + runtime.pop(); + + MWBase::Environment::get().getWorld()->scaleObject(ptr,scale); + } + }; + + template + class OpSetAngle : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float angle = runtime[0].mInteger; + runtime.pop(); + + if(axis == "X") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,0,0); + } + if(axis == "Y") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,0,angle,0); + } + if(axis == "Z") + { + MWBase::Environment::get().getWorld()->rotateObject(ptr,0,0,angle); + } + } + }; + + const int opcodeSetScale = 0x2000164; + const int opcodeSetScaleExplicit = 0x2000165; + const int opcodeSetAngle = 0x2000166; + const int opcodeSetAngleExplicit = 0x2000167; + + void registerExtensions (Compiler::Extensions& extensions) + { + extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); + extensions.registerInstruction("setangle","Sl",opcodeSetAngle,opcodeSetAngleExplicit); + } + + void installOpcodes (Interpreter::Interpreter& interpreter) + { + interpreter.installSegment5(opcodeSetScale,new OpSetScale); + interpreter.installSegment5(opcodeSetScaleExplicit,new OpSetScale); + interpreter.installSegment5(opcodeSetAngle,new OpSetAngle); + interpreter.installSegment5(opcodeSetAngleExplicit,new OpSetAngle); + } + } +} diff --git a/apps/openmw/mwscript/transformationextensions.hpp b/apps/openmw/mwscript/transformationextensions.hpp new file mode 100644 index 000000000..6ee1db1b8 --- /dev/null +++ b/apps/openmw/mwscript/transformationextensions.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_SCRIPT_TRANSFORMATIONEXTENSIONS_H +#define GAME_SCRIPT_TRANSFORMATIONEXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWScript +{ + /// \brief stats-related script functionality (creatures and NPCs) + namespace Transformation + { + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); + } +} + +#endif \ No newline at end of file diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 155bc8d81..c6423563d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -597,7 +597,7 @@ namespace MWWorld mPhysics->moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z)); } - void World::scaleObject (Ptr& ptr, float scale) + void World::scaleObject (const Ptr& ptr, float scale) { MWWorld::Class::get(ptr).adjustScale(ptr,scale); @@ -607,41 +607,7 @@ namespace MWWorld mPhysics->scaleObject( ptr.getRefData().getHandle(), scale ); } - void World::rotateObject (Ptr& ptr,float x,float y,float z,bool WorldAxis) - { - MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); - - if(WorldAxis) - { - ptr.getRefData().getBaseNode()->rotate(Ogre::Vector3::UNIT_X,Ogre::Degree(x)); - ptr.getRefData().getBaseNode()->rotate(Ogre::Vector3::UNIT_X,Ogre::Degree(y)); - ptr.getRefData().getBaseNode()->rotate(Ogre::Vector3::UNIT_X,Ogre::Degree(z)); - } - else - { - Ogre::Matrix3 axis = ptr.getRefData().getBaseNode()->getLocalAxes(); - Ogre::Vector3 xAxis = axis.GetColumn(0); - Ogre::Vector3 yAxis = axis.GetColumn(1); - Ogre::Vector3 zAxis = axis.GetColumn(2); - ptr.getRefData().getBaseNode()->rotate(xAxis,Ogre::Degree(x)); - ptr.getRefData().getBaseNode()->rotate(yAxis,Ogre::Degree(y)); - ptr.getRefData().getBaseNode()->rotate(zAxis,Ogre::Degree(z)); - } - Ogre::Matrix3 rot; - ptr.getRefData().getBaseNode()->getOrientation().ToRotationMatrix(rot); - Ogre::Radian rx,ry,rz; - rot.ToEulerAnglesXYZ(rx,ry,rz); - - ptr.getRefData().getPosition().rot[0] = rx.valueRadians(); - ptr.getRefData().getPosition().rot[0] = ry.valueRadians(); - ptr.getRefData().getPosition().rot[0] = rz.valueRadians(); - - mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); - //ptr.getRefData().getBaseNode()->rotate(ptr.getRefData().getBaseNode()->get - //mPhysics->scaleObject( Class::get(ptr).getId(ptr), scale ); - } - - void World::setObjectRotation (Ptr& ptr,float x,float y,float z) + void World::rotateObject (const Ptr& ptr,float x,float y,float z) { MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); @@ -653,8 +619,7 @@ namespace MWWorld Ogre::Quaternion roty(Ogre::Degree(y),Ogre::Vector3::UNIT_Y); Ogre::Quaternion rotz(Ogre::Degree(z),Ogre::Vector3::UNIT_Z); ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); - mPhysics->rotateObject(Class::get(ptr).getId(ptr),ptr.getRefData().getBaseNode()->getOrientation()); - std::cout << Class::get(ptr).getId(ptr); + mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1f3073d44..8b39a78f1 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -219,11 +219,9 @@ namespace MWWorld virtual void moveObject (const Ptr& ptr, float x, float y, float z); - virtual void scaleObject (Ptr& ptr, float scale); + virtual void scaleObject (const Ptr& ptr, float scale); - virtual void rotateObject (Ptr& ptr,float x,float y,float z,bool WorldAxis); - - virtual void setObjectRotation (Ptr& ptr,float x,float y,float z); + virtual void rotateObject (const Ptr& ptr,float x,float y,float z); virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; From 81d30ff63a119f9d2251688699f1e713576837bf Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 9 Jul 2012 19:20:41 +0200 Subject: [PATCH 067/688] temp commit --- apps/openmw/mwrender/localmap.cpp | 4 ++-- apps/openmw/mwrender/renderingmanager.cpp | 11 ++++++----- apps/openmw/mwrender/shadows.cpp | 6 +++--- apps/openmw/mwrender/water.cpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 2 ++ extern/shiny | 2 +- files/materials/objects.mat | 3 +++ files/materials/objects.shader | 2 +- 8 files changed, 19 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 4fa125c9b..1a4002b12 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -229,9 +229,9 @@ void LocalMap::render(const float x, const float y, vp->setVisibilityMask(RV_Map); // use fallback techniques without shadows and without mrt - vp->setMaterialScheme("simple"); + //vp->setMaterialScheme("local_map"); - rtt->update(); + //rtt->update(); // create "fog of war" texture TexturePtr tex2 = TextureManager::getSingleton().createManual( diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 174a9e4f8..ca26697b7 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -76,9 +76,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const // Load resources ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); - // Due to the huge world size of MW, we'll want camera-relative rendering. - // This prevents precision artifacts when moving very far from the origin. - mRendering.getScene()->setCameraRelativeRendering(true); + // causes light flicker in opengl when moving.. + //mRendering.getScene()->setCameraRelativeRendering(true); // disable unsupported effects const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities(); @@ -525,12 +524,14 @@ Shadows* RenderingManager::getShadows() void RenderingManager::switchToInterior() { - mRendering.getScene()->setCameraRelativeRendering(false); + // causes light flicker in opengl when moving.. + //mRendering.getScene()->setCameraRelativeRendering(false); } void RenderingManager::switchToExterior() { - mRendering.getScene()->setCameraRelativeRendering(true); + // causes light flicker in opengl when moving.. + //mRendering.getScene()->setCameraRelativeRendering(true); } Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds) diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 9a4ae7243..1bea14530 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -53,7 +53,7 @@ void Shadows::recreate() mSceneMgr->setShadowTextureSelfShadow(true); mSceneMgr->setShadowCasterRenderBackFaces(true); - mSceneMgr->setShadowTextureCasterMaterial("depth_shadow_caster"); + // mSceneMgr->setShadowTextureCasterMaterial("openmw_shadowcaster_default"); mSceneMgr->setShadowTexturePixelFormat(PF_FLOAT32_R); mSceneMgr->setShadowDirectionalLightExtrusionDistance(1000000); @@ -111,7 +111,7 @@ void Shadows::recreate() // -------------------------------------------------------------------------------------------------------------------- // --------------------------- Debug overlays to display the content of shadow maps ----------------------------------- // -------------------------------------------------------------------------------------------------------------------- - /* + OverlayManager& mgr = OverlayManager::getSingleton(); Overlay* overlay; @@ -157,7 +157,7 @@ void Shadows::recreate() overlay->add2D(debugPanel); overlay->show(); } - */ + } PSSMShadowCameraSetup* Shadows::getPSSMSetup() diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 666161052..311df9169 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -336,7 +336,7 @@ void Water::applyRTT() vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f)); vp->setShadowsEnabled(false); // use fallback techniques without shadows and without mrt (currently not implemented for sky and terrain) - vp->setMaterialScheme("simple"); + //vp->setMaterialScheme("water_reflection"); rtt->addListener(this); rtt->setActive(true); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index d12a78114..f33ac2364 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -276,6 +276,8 @@ void NIFLoader::createMaterial(const String &name, else warn("Unhandled alpha setting for texture " + texName); } + //else + //instance->getMaterial ()->setShadowCasterMaterial ("openmw_shadowcaster_noalpha"); // As of yet UNTESTED code from Chris: /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); diff --git a/extern/shiny b/extern/shiny index 88a192a67..d25605ee3 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 88a192a67d7dff636be4540be4a8654ad33f106a +Subproject commit d25605ee3a2d67dc0b6367cd150cc6d0c6a3b661 diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 58c9a17bc..324fe1b2a 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -14,6 +14,9 @@ material openmw_objects_base scene_blend default depth_write default alpha_rejection default + shadow_transparency true // use diffuse alpha as mask for shadow + + shadow_caster_material openmw_shadowcaster pass { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index e066b43cc..831699364 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -72,7 +72,7 @@ shInput(float3, normalPassthrough) shInput(float3, objSpacePositionPassthrough) shUniform(float4 lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - shUniform(float passIteration) @shAutoConstant(passIteration, pass_iteration_number) + //shUniform(float passIteration) @shAutoConstant(passIteration, pass_iteration_number) shUniform(float4 materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) From 557e114992458b1d7c2768baaf342a64dc01542e Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 9 Jul 2012 19:28:44 +0200 Subject: [PATCH 068/688] clean-up + getScale/Angle script instructions --- .../mwscript/transformationextensions.cpp | 59 +++++++++++++++++-- apps/openmw/mwworld/worldimp.cpp | 4 +- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 14181a06e..41eba4a99 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -1,6 +1,3 @@ - -#include "statsextensions.hpp" - #include #include @@ -17,6 +14,7 @@ #include "interpretercontext.hpp" #include "ref.hpp" +#include "OgreSceneNode.h" namespace MWScript { @@ -31,13 +29,25 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Float scale = runtime[0].mInteger; + Interpreter::Type_Float scale = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWorld()->scaleObject(ptr,scale); } }; + template + class OpGetScale : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + runtime.push(ptr.getCellRef().scale); + } + }; + template class OpSetAngle : public Interpreter::Opcode0 { @@ -49,7 +59,7 @@ namespace MWScript std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - Interpreter::Type_Float angle = runtime[0].mInteger; + Interpreter::Type_Float angle = runtime[0].mFloat; runtime.pop(); if(axis == "X") @@ -67,15 +77,48 @@ namespace MWScript } }; + template + class OpGetAngle : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + if(axis == "X") + { + runtime.push(ptr.getRefData().getPosition().rot[0]); + } + if(axis == "Y") + { + runtime.push(ptr.getRefData().getPosition().rot[1]); + } + if(axis == "Z") + { + runtime.push(ptr.getRefData().getPosition().rot[0]); + } + } + }; + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; const int opcodeSetAngleExplicit = 0x2000167; + const int opcodeGetScale = 0x2000168; + const int opcodeGetScaleExplicit = 0x2000169; + const int opcodeGetAngle = 0x200016a; + const int opcodeGetAngleExplicit = 0x200016b; void registerExtensions (Compiler::Extensions& extensions) { extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); - extensions.registerInstruction("setangle","Sl",opcodeSetAngle,opcodeSetAngleExplicit); + extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); + extensions.registerInstruction("setangle","Sf",opcodeSetAngle,opcodeSetAngleExplicit); + extensions.registerFunction("getangle",'f',"S",opcodeGetAngle,opcodeGetAngleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -84,6 +127,10 @@ namespace MWScript interpreter.installSegment5(opcodeSetScaleExplicit,new OpSetScale); interpreter.installSegment5(opcodeSetAngle,new OpSetAngle); interpreter.installSegment5(opcodeSetAngleExplicit,new OpSetAngle); + interpreter.installSegment5(opcodeGetScale,new OpGetScale); + interpreter.installSegment5(opcodeGetScaleExplicit,new OpGetScale); + interpreter.installSegment5(opcodeGetAngle,new OpGetAngle); + interpreter.installSegment5(opcodeGetAngleExplicit,new OpGetAngle); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c6423563d..5309fbe40 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -612,8 +612,8 @@ namespace MWWorld MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); ptr.getRefData().getPosition().rot[0] = Ogre::Degree(x).valueRadians(); - ptr.getRefData().getPosition().rot[0] = Ogre::Degree(y).valueRadians(); - ptr.getRefData().getPosition().rot[0] = Ogre::Degree(z).valueRadians(); + ptr.getRefData().getPosition().rot[1] = Ogre::Degree(y).valueRadians(); + ptr.getRefData().getPosition().rot[2] = Ogre::Degree(z).valueRadians(); Ogre::Quaternion rotx(Ogre::Degree(x),Ogre::Vector3::UNIT_X); Ogre::Quaternion roty(Ogre::Degree(y),Ogre::Vector3::UNIT_Y); From b803d0e9493467456aabfe573046d934f159496c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 9 Jul 2012 19:46:36 +0200 Subject: [PATCH 069/688] converted shadow caster shader --- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/shadows.cpp | 2 +- apps/openmw/mwrender/water.cpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 4 +- extern/shiny | 2 +- files/materials/shadowcaster.mat | 35 ++++++++++++ files/materials/shadowcaster.shader | 56 ++++++++++++++++++ files/materials/shadowcaster.shaderset | 15 +++++ files/shadows/depthshadowcaster.cg | 51 ----------------- files/shadows/depthshadowcaster.material | 73 ------------------------ 10 files changed, 112 insertions(+), 130 deletions(-) create mode 100644 files/materials/shadowcaster.mat create mode 100644 files/materials/shadowcaster.shader create mode 100644 files/materials/shadowcaster.shaderset delete mode 100644 files/shadows/depthshadowcaster.cg delete mode 100644 files/shadows/depthshadowcaster.material diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 1a4002b12..663388e94 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -229,7 +229,7 @@ void LocalMap::render(const float x, const float y, vp->setVisibilityMask(RV_Map); // use fallback techniques without shadows and without mrt - //vp->setMaterialScheme("local_map"); + vp->setMaterialScheme("local_map"); //rtt->update(); diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 1bea14530..93152244b 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -53,7 +53,7 @@ void Shadows::recreate() mSceneMgr->setShadowTextureSelfShadow(true); mSceneMgr->setShadowCasterRenderBackFaces(true); - // mSceneMgr->setShadowTextureCasterMaterial("openmw_shadowcaster_default"); + mSceneMgr->setShadowTextureCasterMaterial("openmw_shadowcaster_default"); mSceneMgr->setShadowTexturePixelFormat(PF_FLOAT32_R); mSceneMgr->setShadowDirectionalLightExtrusionDistance(1000000); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 311df9169..b8642a754 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -336,7 +336,7 @@ void Water::applyRTT() vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f)); vp->setShadowsEnabled(false); // use fallback techniques without shadows and without mrt (currently not implemented for sky and terrain) - //vp->setMaterialScheme("water_reflection"); + vp->setMaterialScheme("water_reflection"); rtt->addListener(this); rtt->setActive(true); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index f33ac2364..e5b196804 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -276,8 +276,8 @@ void NIFLoader::createMaterial(const String &name, else warn("Unhandled alpha setting for texture " + texName); } - //else - //instance->getMaterial ()->setShadowCasterMaterial ("openmw_shadowcaster_noalpha"); + else + instance->getMaterial ()->setShadowCasterMaterial ("openmw_shadowcaster_noalpha"); // As of yet UNTESTED code from Chris: /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); diff --git a/extern/shiny b/extern/shiny index d25605ee3..a0b3020bd 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit d25605ee3a2d67dc0b6367cd150cc6d0c6a3b661 +Subproject commit a0b3020bdba4433c1e3f45a1d5188eab0172bd88 diff --git a/files/materials/shadowcaster.mat b/files/materials/shadowcaster.mat new file mode 100644 index 000000000..25b791f1f --- /dev/null +++ b/files/materials/shadowcaster.mat @@ -0,0 +1,35 @@ + + +material openmw_shadowcaster_default +{ + create_configuration Default + pass + { + fog_override true + + vertex_program openmw_shadowcaster_vertex + fragment_program openmw_shadowcaster_fragment + + shader_properties + { + shadow_transparency true + } + } +} + +material openmw_shadowcaster_noalpha +{ + create_configuration Default + pass + { + fog_override true + + vertex_program openmw_shadowcaster_vertex + fragment_program openmw_shadowcaster_fragment + + shader_properties + { + shadow_transparency false + } + } +} diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader new file mode 100644 index 000000000..f772066a6 --- /dev/null +++ b/files/materials/shadowcaster.shader @@ -0,0 +1,56 @@ +#include "core.h" + +#define ALPHA @shPropertyBool(shadow_transparency) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM +#if ALPHA + shInput(float2, uv0) + shOutput(float2, UV) +#endif + shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shOutput(float2, depth) + SH_START_PROGRAM + { + // this is the view space position + shOutputPosition = shMatrixMult(wvp, shInputPosition); + + // depth info for the fragment. + depth.x = shOutputPosition.z; + depth.y = shOutputPosition.w; + + // clamp z to zero. seem to do the trick. :-/ + shOutputPosition.z = max(shOutputPosition.z, 0); + +#if ALPHA + UV = uv0; +#endif + } + +#else + + SH_BEGIN_PROGRAM +#if ALPHA + shInput(float2, UV) + shSampler2D(texture1) +#endif + shInput(float2, depth) + SH_START_PROGRAM + { + float finalDepth = depth.x / depth.y; + + +#if ALPHA + // use alpha channel of the first texture + float alpha = shSample(texture1, UV).a; + + // discard if alpha is less than 0.5 + if (alpha < 1.0) + discard; +#endif + + shOutputColor(0) = float4(finalDepth, finalDepth, finalDepth, 1); + } + +#endif diff --git a/files/materials/shadowcaster.shaderset b/files/materials/shadowcaster.shaderset new file mode 100644 index 000000000..5f4990ed1 --- /dev/null +++ b/files/materials/shadowcaster.shaderset @@ -0,0 +1,15 @@ +shader_set openmw_shadowcaster_vertex +{ + source shadowcaster.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set openmw_shadowcaster_fragment +{ + source shadowcaster.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/shadows/depthshadowcaster.cg b/files/shadows/depthshadowcaster.cg deleted file mode 100644 index 3457a4f8d..000000000 --- a/files/shadows/depthshadowcaster.cg +++ /dev/null @@ -1,51 +0,0 @@ -void main_vp( - float4 position : POSITION, - float2 uv : TEXCOORD0, - - out float4 oPosition : POSITION, - out float2 oDepth : TEXCOORD0, - out float2 oUv : TEXCOORD1, - - uniform float4x4 wvpMat) -{ - // this is the view space position - oPosition = mul(wvpMat, position); - - // depth info for the fragment. - oDepth.x = oPosition.z; - oDepth.y = oPosition.w; - - // clamp z to zero. seem to do the trick. :-/ - oPosition.z = max(oPosition.z, 0); - - oUv = uv; -} - -void main_fp( - float2 depth : TEXCOORD0, - float2 uv : TEXCOORD1, - uniform sampler2D texture1 : register(s0), - - out float4 oColour : COLOR) -{ - float finalDepth = depth.x / depth.y; - - // use alpha channel of the first texture - float alpha = tex2D(texture1, uv).a; - - // discard if alpha is less than 0.5 - clip((alpha >= 0.5) ? 1 : -1); - - oColour = float4(finalDepth, finalDepth, finalDepth, 1); -} - -void main_fp_noalpha( - float2 depth : TEXCOORD0, - float2 uv : TEXCOORD1, - - out float4 oColour : COLOR) -{ - float finalDepth = depth.x / depth.y; - - oColour = float4(finalDepth, finalDepth, finalDepth, 1); -} diff --git a/files/shadows/depthshadowcaster.material b/files/shadows/depthshadowcaster.material deleted file mode 100644 index f645cad01..000000000 --- a/files/shadows/depthshadowcaster.material +++ /dev/null @@ -1,73 +0,0 @@ -vertex_program depth_shadow_caster_vs cg -{ - source depthshadowcaster.cg - profiles vs_1_1 arbvp1 - entry_point main_vp - - default_params - { - param_named_auto wvpMat worldviewproj_matrix - } -} - -fragment_program depth_shadow_caster_ps cg -{ - source depthshadowcaster.cg - profiles ps_2_0 arbfp1 - entry_point main_fp - - default_params - { - } -} - -fragment_program depth_shadow_caster_ps_noalpha cg -{ - source depthshadowcaster.cg - profiles ps_2_0 arbfp1 - entry_point main_fp_noalpha - - default_params - { - } -} - -material depth_shadow_caster -{ - technique - { - pass - { - // force-disable fog (relevant for DirectX profiles below SM3 that always apply fixed function fog) - fog_override true - - vertex_program_ref depth_shadow_caster_vs - { - } - - fragment_program_ref depth_shadow_caster_ps - { - } - } - } -} - -material depth_shadow_caster_noalpha -{ - technique - { - pass - { - // force-disable fog (relevant for DirectX profiles below SM3 that always apply fixed function fog) - fog_override true - - vertex_program_ref depth_shadow_caster_vs - { - } - - fragment_program_ref depth_shadow_caster_ps_noalpha - { - } - } - } -} From 07fd9986ef995e7f21fac54aefa612f4613a4bb4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 9 Jul 2012 20:14:07 +0200 Subject: [PATCH 070/688] accidently removed the map. --- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/shadows.cpp | 4 +- apps/openmw/mwrender/sky.cpp | 63 +++---------------------------- apps/openmw/mwrender/sky.hpp | 2 + extern/shiny | 2 +- files/materials/atmosphere.shader | 15 ++++---- files/materials/shadowcaster.mat | 2 - files/materials/sky.mat | 5 +++ 8 files changed, 23 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 663388e94..962f19a57 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -231,7 +231,7 @@ void LocalMap::render(const float x, const float y, // use fallback techniques without shadows and without mrt vp->setMaterialScheme("local_map"); - //rtt->update(); + rtt->update(); // create "fog of war" texture TexturePtr tex2 = TextureManager::getSingleton().createManual( diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 93152244b..2e09f4c31 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -111,7 +111,7 @@ void Shadows::recreate() // -------------------------------------------------------------------------------------------------------------------- // --------------------------- Debug overlays to display the content of shadow maps ----------------------------------- // -------------------------------------------------------------------------------------------------------------------- - +/* OverlayManager& mgr = OverlayManager::getSingleton(); Overlay* overlay; @@ -157,7 +157,7 @@ void Shadows::recreate() overlay->add2D(debugPanel); overlay->show(); } - +*/ } PSSMShadowCameraSetup* Shadows::getPSSMSetup() diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 0a866ac7c..f32883fdd 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -453,7 +453,6 @@ void SkyManager::create() HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); // Stars - /// \todo sky_night_02.nif (available in Bloodmoon) MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); Entity* night1_ent = mSceneMgr->createEntity("meshes\\sky_night_01.nif"); night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); @@ -548,60 +547,9 @@ void SkyManager::create() atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere"); //mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); //mAtmosphereMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); - // Atmosphere shader - HighLevelGpuProgramPtr vshader = mgr.createProgram("Atmosphere_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_VERTEX_PROGRAM); - - vshader->setParameter("profiles", "vs_2_x arbvp1"); - vshader->setParameter("entry_point", "main_vp"); - - StringUtil::StrStreamType outStream; - outStream << - "void main_vp( \n" - " float4 position : POSITION, \n" - " in float4 color : COLOR, \n" - " out float4 oPosition : POSITION, \n" - " out float4 oVertexColor : TEXCOORD0, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" - " oPosition = mul( worldViewProj, position ); \n" - " oVertexColor = color; \n" - "}"; - vshader->setSource(outStream.str()); - vshader->load(); - - vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - //mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); - - HighLevelGpuProgramPtr fshader = mgr.createProgram("Atmosphere_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_FRAGMENT_PROGRAM); - - fshader->setParameter("profiles", "ps_2_x arbfp1"); - fshader->setParameter("entry_point", "main_fp"); - - StringUtil::StrStreamType _outStream; - _outStream << - "void main_fp( \n" - " in float4 iVertexColor : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n"; - if (RenderingManager::useMRT()) _outStream << - " out float4 oColor1 : COLOR1, \n"; - _outStream << - " uniform float4 emissive \n" - ") \n" - "{ \n" - " oColor = iVertexColor * emissive; \n"; - if (RenderingManager::useMRT()) _outStream << - " oColor1 = float4(1, 0, 0, 1); \n"; - _outStream << - "}"; - fshader->setSource(_outStream.str()); - fshader->load(); - - fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); // mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); + atmosphere_ent->getSubEntity (0)->setMaterialName("openmw_atmosphere"); // Clouds NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); @@ -838,15 +786,14 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(clr); mCloudColour = weather.mSunColor; } - +*/ if (mSkyColour != weather.mSkyColor) { - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(weather.mSkyColor); - mMasser->setSkyColour(weather.mSkyColor); - mSecunda->setSkyColour(weather.mSkyColor); mSkyColour = weather.mSkyColor; + sh::Factory::getInstance().setSharedParameter ("atmosphereColour", sh::makeProperty(new sh::Vector4( + weather.mSkyColor.r, weather.mSkyColor.g, weather.mSkyColor.b, 1.0))); } - +/* if (mCloudSpeed != weather.mCloudSpeed) { mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("speed", Real(weather.mCloudSpeed)); diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index e11745e82..6bb055824 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -7,6 +7,8 @@ #include #include +#include + #include "sky.hpp" #include "../mwworld/weather.hpp" diff --git a/extern/shiny b/extern/shiny index a0b3020bd..3e7e02a84 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit a0b3020bdba4433c1e3f45a1d5188eab0172bd88 +Subproject commit 3e7e02a846ce6c3de7e2344a82d346293115eb7d diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index a772e3c5e..484381a1f 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -6,29 +6,28 @@ SH_BEGIN_PROGRAM shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) - shOutput(float2, UV) + + shColourInput(float4) + shOutput(float4, colourPassthrough) SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); - UV = uv0; + colourPassthrough = colour; } #else SH_BEGIN_PROGRAM - shSampler2D(diffuseMap) - shInput(float2, UV) + shInput(float4, colourPassthrough) #if MRT shDeclareMrtOutput(1) #endif - shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) - shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + shUniform(float4 atmosphereColour) @shSharedParameter(atmosphereColour) SH_START_PROGRAM { - shOutputColor(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + shOutputColor(0) = colourPassthrough * atmosphereColour; #if MRT shOutputColor(1) = float4(1,1,1,1); diff --git a/files/materials/shadowcaster.mat b/files/materials/shadowcaster.mat index 25b791f1f..7c11fddf6 100644 --- a/files/materials/shadowcaster.mat +++ b/files/materials/shadowcaster.mat @@ -1,5 +1,3 @@ - - material openmw_shadowcaster_default { create_configuration Default diff --git a/files/materials/sky.mat b/files/materials/sky.mat index 25c9185b7..c78570f7f 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -1,5 +1,6 @@ material openmw_moon { + allow_fixed_function false mrt_output true pass { @@ -23,6 +24,7 @@ material openmw_moon material openmw_clouds { + allow_fixed_function false mrt_output true pass { @@ -55,6 +57,7 @@ material openmw_clouds material openmw_atmosphere { + allow_fixed_function false mrt_output true pass { @@ -75,6 +78,7 @@ material openmw_atmosphere material openmw_stars { + allow_fixed_function false mrt_output true pass { @@ -98,6 +102,7 @@ material openmw_stars // used for both sun and sun glare material openmw_sun { + allow_fixed_function false mrt_output true pass { From 25a5657d80b09c6a878a8cd7f92c57181ce15ebb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 9 Jul 2012 18:26:00 +0200 Subject: [PATCH 071/688] Issue #324: Modified the interface for modified stats --- apps/openmw/mwmechanics/stat.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 2449075a8..996036fc1 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -31,6 +31,11 @@ namespace MWMechanics return mModified; } + T getModifier() const + { + return mModified-mBase; + } + /// Set base and modified to \a value. void set (const T& value) { @@ -65,10 +70,9 @@ namespace MWMechanics mBase += diff; } - /// Change modified relatively. - void modify (const T& diff) + void setModifier (const T& modifier) { - mModified += diff; + mModified = mBase + modifier; } }; From 67c1c5ce1811271a15d93b8e0be906432cfe8a06 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 9 Jul 2012 20:42:45 +0200 Subject: [PATCH 072/688] Issue #324: adjusted setSkill script instructions according to the recent research --- apps/openmw/mwscript/statsextensions.cpp | 30 ++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 42781d766..c720e2c6a 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1,8 +1,12 @@ #include "statsextensions.hpp" +#include + #include +#include + #include #include @@ -260,8 +264,30 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - setModified (value, 0); + MWMechanics::NpcStats& stats = MWWorld::Class::get (ptr).getNpcStats (ptr); + + MWWorld::LiveCellRef *ref = ptr.get(); + + assert (ref); + + const ESM::Class& class_ = + *MWBase::Environment::get().getWorld()->getStore().classes.find (ref->base->cls); + + float level = 0; + float progress = std::modf (stats.getSkill (mIndex).getBase(), &level); + + float modifier = stats.getSkill (mIndex).getModifier(); + + int newLevel = static_cast (value-modifier); + + progress = (progress / stats.getSkillGain (mIndex, class_, -1, level)) + * stats.getSkillGain (mIndex, class_, -1, newLevel); + + if (progress>=1) + progress = 0.999999999; + + stats.getSkill (mIndex).set (newLevel + progress); + stats.getSkill (mIndex).setModifier (modifier); } }; From a207c86fd14a69fd2ee8d7ba17278aa7127df96c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 9 Jul 2012 21:14:11 +0200 Subject: [PATCH 073/688] Issue #324: added some range checks --- apps/openmw/mwscript/statsextensions.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index c720e2c6a..33f086c6f 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -280,6 +280,11 @@ namespace MWScript int newLevel = static_cast (value-modifier); + if (newLevel<0) + newLevel = 0; + else if (newLevel>100) + newLevel = 100; + progress = (progress / stats.getSkillGain (mIndex, class_, -1, level)) * stats.getSkillGain (mIndex, class_, -1, newLevel); From 0d8150f4bd8782304c1ca297dd14633a4fe8627a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 9 Jul 2012 21:15:52 +0200 Subject: [PATCH 074/688] Issue #324: added useSkill function to NpcStats (adjust skill level according to use) --- apps/openmw/mwmechanics/npcstats.cpp | 15 +++++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 3 +++ 2 files changed, 18 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 08ac12374..d2908e26e 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -1,6 +1,7 @@ #include "npcstats.hpp" +#include #include #include @@ -123,3 +124,17 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla return 1.0 / (level +1) * (1.0 / skillFactor) * typeFactor * specialisationFactor; } + +void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType) +{ + float base = getSkill (skillIndex).getBase(); + + int level = static_cast (base); + + base += getSkillGain (skillIndex, class_, usageType); + + if (static_cast (base)!=level) + base = level+1; + + getSkill (skillIndex).setBase (base); +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 1dccdd0d6..364dab03f 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -70,6 +70,9 @@ namespace MWMechanics ///< \param usageType: Usage specific factor, specified in the respective skill record; /// -1: use a factor of 1.0 instead. /// \param level Level to base calculation on; -1: use current level. + + void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1); + ///< Increase skill by usage. }; } From 7375035fef34fee75085dcab850f9467c25fe945 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 9 Jul 2012 17:54:15 -0400 Subject: [PATCH 075/688] Counter changed back to 10 --- libs/openengine/bullet/pmove.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index e13e9e6c0..924812c29 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -527,7 +527,7 @@ int PM_StepSlideMove( bool gravity ) delta = pm->ps.origin.z - start_o.z; if ( delta > 2 ) { - pm->ps.counter = 5; + pm->ps.counter = 10; /* if (gravity) From 1a9f59d5d466c7888f14bb82f7fc4ad58e4b14f3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jul 2012 00:07:33 +0200 Subject: [PATCH 076/688] shadows --- apps/openmw/mwrender/renderingmanager.cpp | 2 + apps/openmw/mwrender/shadows.cpp | 15 +++- extern/shiny | 2 +- files/materials/objects.mat | 20 +++++ files/materials/objects.shader | 90 +++++++++++++++++++++-- files/materials/objects.shaderset | 4 +- files/materials/shadows.h | 47 ++++++++++++ 7 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 files/materials/shadows.h diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ca26697b7..8ecb69b9e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -54,6 +54,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::OgrePlatform* platform = new sh::OgrePlatform("General", (resDir / "materials").string()); platform->setCacheFolder ("./"); mFactory = new sh::Factory(platform); + mFactory->setSharedParameter ("pssmSplitPoints", sh::makeProperty(new sh::Vector4(0,0,0,0))); + mFactory->setSharedParameter ("shadowFar_fadeStart", sh::makeProperty(new sh::Vector4(0,0,0,0))); //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 2e09f4c31..b4982e22d 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include "renderconst.hpp" using namespace Ogre; @@ -84,6 +86,12 @@ void Shadows::recreate() mSceneMgr->setShadowTextureConfig(i, texsize/4, texsize/4, Ogre::PF_FLOAT32_R);*/ } + // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) + const PSSMShadowCameraSetup::SplitPointList& splitPointList = getPSSMSetup()->getSplitPoints(); + sh::Vector4* splitPoints = new sh::Vector4(splitPointList[1], splitPointList[2], splitPointList[3], 1.0); + + sh::Factory::getInstance ().setSharedParameter ("pssmSplitPoints", sh::makeProperty(splitPoints)); + shadowCameraSetup = ShadowCameraSetupPtr(mPSSMSetup); } else @@ -96,6 +104,9 @@ void Shadows::recreate() } mSceneMgr->setShadowCameraSetup(shadowCameraSetup); + sh::Vector4* shadowFar_fadeStart = new sh::Vector4(mShadowFar, mFadeStart * mShadowFar, 0, 0); + sh::Factory::getInstance ().setSharedParameter ("shadowFar_fadeStart", sh::makeProperty(shadowFar_fadeStart)); + // Set visibility mask for the shadow render textures int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows") + (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows") @@ -111,7 +122,7 @@ void Shadows::recreate() // -------------------------------------------------------------------------------------------------------------------- // --------------------------- Debug overlays to display the content of shadow maps ----------------------------------- // -------------------------------------------------------------------------------------------------------------------- -/* + OverlayManager& mgr = OverlayManager::getSingleton(); Overlay* overlay; @@ -157,7 +168,7 @@ void Shadows::recreate() overlay->add2D(debugPanel); overlay->show(); } -*/ + } PSSMShadowCameraSetup* Shadows::getPSSMSetup() diff --git a/extern/shiny b/extern/shiny index 3e7e02a84..b3cfd41df 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 3e7e02a846ce6c3de7e2344a82d346293115eb7d +Subproject commit b3cfd41dff2758e268ce16f366b7e7857eee80ea diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 324fe1b2a..76e64112b 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -42,10 +42,30 @@ material openmw_objects_base ffp_vertex_colour_ambient $has_vertex_colour + texture_unit diffuseMap { direct_texture $diffuseMap create_in_ffp true } + + texture_unit shadowMap0 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap1 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap2 + { + content_type shadow + tex_address_mode clamp + filtering none + } } } diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 831699364..dd1b489d5 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -1,20 +1,28 @@ #include "core.h" +#include "shadows.h" + #define FOG @shPropertyBool(fog) #define MRT @shPropertyNotBool(is_transparent) && @shPropertyBool(mrt_output) #define LIGHTING @shPropertyBool(lighting) -#if FOG || MRT +#define SHADOWS LIGHTING && 0 +#define SHADOWS_PSSM LIGHTING + +#define SHADOWS 1 && LIGHTING +#define SHADOWS_PSSM 0 && LIGHTING + +#if FOG || MRT || SHADOWS_PSSM #define NEED_DEPTH #endif -#define NUM_LIGHTS 8 - #define HAS_VERTEXCOLOR @shPropertyBool(has_vertex_colour) #ifdef SH_VERTEX_SHADER + // ------------------------------------- VERTEX --------------------------------------- + SH_BEGIN_PROGRAM shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) shInput(float2, uv0) @@ -33,11 +41,28 @@ shColourInput(float4) shOutput(float4, colorPassthrough) #endif + +#if SHADOWS + shOutput(float4, lightSpacePos0) + shUniform(float4x4 texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) + shUniform(float4x4 worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#endif + +#if SHADOWS_PSSM + @shForeach(3) + shOutput(float4, lightSpacePos@shIterator) + shUniform(float4x4 texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) + @shEndForeach + shUniform(float4x4 worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#endif SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); UV = uv0; +#if LIGHTING normalPassthrough = normal.xyz; +#endif + #ifdef NEED_DEPTH depthPassthrough = shOutputPosition.z; #endif @@ -49,10 +74,22 @@ #if HAS_VERTEXCOLOR colorPassthrough = colour; #endif + +#if SHADOWS + lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); +#endif +#if SHADOWS_PSSM + float4 wPos = shMatrixMult(worldMatrix, shInputPosition); + @shForeach(3) + lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos); + @shEndForeach +#endif } #else + // ----------------------------------- FRAGMENT ------------------------------------------ + SH_BEGIN_PROGRAM shSampler2D(diffuseMap) shInput(float2, UV) @@ -76,7 +113,7 @@ shUniform(float4 materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) - @shForeach(NUM_LIGHTS) + @shForeach(8) shUniform(float4 lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) shUniform(float4 lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) shUniform(float4 lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) @@ -91,6 +128,24 @@ #ifdef HAS_VERTEXCOLOR shInput(float4, colorPassthrough) #endif + +#if SHADOWS + shInput(float4, lightSpacePos0) + shSampler2D(shadowMap0) + shUniform(float2 invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 0) +#endif +#if SHADOWS_PSSM + @shForeach(3) + shInput(float4, lightSpacePos@shIterator) + shSampler2D(shadowMap@shIterator) + shUniform(float2 invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator) + @shEndForeach + shUniform(float4 pssmSplitPoints) @shSharedParameter(pssmSplitPoints) +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4 shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) +#endif SH_START_PROGRAM { shOutputColor(0) = shSample(diffuseMap, UV); @@ -101,15 +156,38 @@ float d; float3 ambient = materialAmbient.xyz * lightAmbient.xyz; - @shForeach(NUM_LIGHTS) + @shForeach(8) + + // shadows only for the first (directional) light +#if @shIterator == 0 + #if SHADOWS + float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); + #endif + #if SHADOWS_PSSM + float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints); + #endif + + #if SHADOWS || SHADOWS_PSSM + float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; + float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); + shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); + #endif + + #if !SHADOWS && !SHADOWS_PSSM + float shadow = 1.0; + #endif +#endif lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePositionPassthrough.xyz * lightPosObjSpace@shIterator.w); d = length(lightDir); lightDir = normalize(lightDir); +#if @shIterator == 0 + diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow; +#else diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); - +#endif @shEndForeach #if HAS_VERTEXCOLOR diff --git a/files/materials/objects.shaderset b/files/materials/objects.shaderset index 7d01973e3..e84368a5b 100644 --- a/files/materials/objects.shaderset +++ b/files/materials/objects.shaderset @@ -2,7 +2,7 @@ shader_set openmw_objects_vertex { source objects.shader type vertex - profiles_cg vs_2_0 arbvp1 + profiles_cg vs_2_0 vp40 arbvp1 profiles_hlsl vs_2_0 } @@ -10,6 +10,6 @@ shader_set openmw_objects_fragment { source objects.shader type fragment - profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 profiles_hlsl ps_2_0 } diff --git a/files/materials/shadows.h b/files/materials/shadows.h new file mode 100644 index 000000000..a3518b976 --- /dev/null +++ b/files/materials/shadows.h @@ -0,0 +1,47 @@ + + + +float depthShadowPCF (shTexture2D shadowMap, float4 shadowMapPos, float2 offset) +{ + shadowMapPos /= shadowMapPos.w; + float3 o = float3(offset.xy, -offset.x) * 0.3; + //float3 o = float3(0,0,0); + float c = (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left + c += (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right + c += (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left + c += (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right + return c / 4; +} + + + +float pssmDepthShadow ( + + + float4 lightSpacePos0, + float2 invShadowmapSize0, + shTexture2D shadowMap0, + + float4 lightSpacePos1, + float2 invShadowmapSize1, + shTexture2D shadowMap1, + + float4 lightSpacePos2, + float2 invShadowmapSize2, + shTexture2D shadowMap2, + + float depth, + float4 pssmSplitPoints) + +{ + float shadow; + + if (depth < pssmSplitPoints.x) + shadow = depthShadowPCF(shadowMap0, lightSpacePos0, invShadowmapSize0); + else if (depth < pssmSplitPoints.y) + shadow = depthShadowPCF(shadowMap1, lightSpacePos1, invShadowmapSize1); + else + shadow = depthShadowPCF(shadowMap2, lightSpacePos2, invShadowmapSize2); + + return shadow; +} From 5d1e6b6bf6d8c22bf603a2aaffd53dff6b3fae35 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jul 2012 00:09:47 +0200 Subject: [PATCH 077/688] remove cg profile check --- apps/openmw/mwrender/renderingmanager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8ecb69b9e..cd12cfad0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -85,8 +85,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities(); if (!waterShaderSupported()) Settings::Manager::setBool("shader", "Water", false); - if ( !(caps->isShaderProfileSupported("fp40") || caps->isShaderProfileSupported("ps_4_0")) - || !Settings::Manager::getBool("shaders", "Objects")) + if (!Settings::Manager::getBool("shaders", "Objects")) Settings::Manager::setBool("enabled", "Shadows", false); applyCompositors(); From 3537af051d0bd308db2c0466dfe4de683d5acbb7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 10 Jul 2012 00:17:52 +0200 Subject: [PATCH 078/688] removed the old shader helper --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 4 - apps/openmw/mwrender/renderingmanager.hpp | 3 - apps/openmw/mwrender/shaderhelper.cpp | 309 ---------------------- apps/openmw/mwrender/shaderhelper.hpp | 29 -- 5 files changed, 1 insertion(+), 346 deletions(-) delete mode 100644 apps/openmw/mwrender/shaderhelper.cpp delete mode 100644 apps/openmw/mwrender/shaderhelper.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 911bd70d5..382adde46 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 occlusionquery terrain terrainmaterial water shadows shaderhelper + renderinginterface localmap occlusionquery terrain terrainmaterial water shadows compositors ) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index cd12cfad0..5d9395460 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -30,7 +30,6 @@ #include "../mwinput/inputmanager.hpp" // FIXME #include "shadows.hpp" -#include "shaderhelper.hpp" #include "localmap.hpp" #include "water.hpp" #include "compositors.hpp" @@ -108,7 +107,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const cameraPitchNode->attachObject(mRendering.getCamera()); mShadows = new Shadows(&mRendering); - mShaderHelper = new ShaderHelper(this); mTerrainManager = new TerrainManager(mRendering.getScene(), this); @@ -132,7 +130,6 @@ RenderingManager::~RenderingManager () delete mPlayer; delete mSkyManager; delete mDebugging; - delete mShaderHelper; delete mShadows; delete mTerrainManager; delete mLocalMap; @@ -617,7 +614,6 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec else if (it->second == "shader" && it->first == "Water") { applyCompositors(); - mShaderHelper->applyShaders(); } } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index db5a731b4..73739cfae 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -52,7 +52,6 @@ namespace MWRender { class Shadows; - class ShaderHelper; class LocalMap; class Water; class Compositors; @@ -223,8 +222,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Shadows* mShadows; - MWRender::ShaderHelper* mShaderHelper; - MWRender::Compositors* mCompositors; }; diff --git a/apps/openmw/mwrender/shaderhelper.cpp b/apps/openmw/mwrender/shaderhelper.cpp deleted file mode 100644 index 1f9086db4..000000000 --- a/apps/openmw/mwrender/shaderhelper.cpp +++ /dev/null @@ -1,309 +0,0 @@ -#include "shaderhelper.hpp" -#include "renderingmanager.hpp" -#include "shadows.hpp" - -#include -#include -#include -#include - -#include - -using namespace Ogre; -using namespace MWRender; - -ShaderHelper::ShaderHelper(RenderingManager* rend) -{ - mRendering = rend; - applyShaders(); -} - -void ShaderHelper::applyShaders() -{ - if (!Settings::Manager::getBool("shaders", "Objects")) return; - - bool mrt = RenderingManager::useMRT(); - bool shadows = Settings::Manager::getBool("enabled", "Shadows"); - bool split = Settings::Manager::getBool("split", "Shadows"); - - // shader for normal rendering - createShader(mrt, shadows, split, "main"); - - // fallback shader without mrt and without shadows - // (useful for reflection and for minimap) - createShader(false, false, false, "main_fallback"); -} - -void ShaderHelper::createShader(const bool mrt, const bool shadows, const bool split, const std::string& name) -{ - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - - const int numsplits = 3; - - // the number of lights to support. - // when rendering an object, OGRE automatically picks the lights that are - // closest to the object being rendered. unfortunately this mechanism does - // not work perfectly for objects batched together (they will all use the same - // lights). to work around this, we are simply pushing the maximum number - // of lights here in order to minimize disappearing lights. - int num_lights = Settings::Manager::getInt("num lights", "Objects"); - - { - // vertex - HighLevelGpuProgramPtr vertex; - if (!mgr.getByName(name+"_vp").isNull()) - mgr.remove(name+"_vp"); - - vertex = mgr.createProgram(name+"_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_VERTEX_PROGRAM); - vertex->setParameter("profiles", "vs_4_0 vs_2_x vp40 arbvp1"); - vertex->setParameter("entry_point", "main_vp"); - StringUtil::StrStreamType outStream; - outStream << - "void main_vp( \n" - " float4 position : POSITION, \n" - " float4 normal : NORMAL, \n" - " float4 colour : COLOR, \n" - " in float2 uv : TEXCOORD0, \n" - " out float2 oUV : TEXCOORD0, \n" - " out float4 oPosition : POSITION, \n" - " out float4 oPositionObjSpace : TEXCOORD1, \n" - " out float4 oNormal : TEXCOORD2, \n" - " out float oDepth : TEXCOORD3, \n" - " out float4 oVertexColour : TEXCOORD4, \n"; - if (shadows && !split) outStream << - " out float4 oLightSpacePos0 : TEXCOORD5, \n" - " uniform float4x4 worldMatrix, \n" - " uniform float4x4 texViewProjMatrix0, \n"; - else - { - for (int i=0; isetSource(outStream.str()); - vertex->load(); - vertex->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - if (shadows) - { - vertex->getDefaultParameters()->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX); - if (!split) - vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix0", GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, 0); - else - { - for (int i=0; igetDefaultParameters()->setNamedAutoConstant("texViewProjMatrix"+StringConverter::toString(i), GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i); - } - } - } - } - - { - // fragment - HighLevelGpuProgramPtr fragment; - if (!mgr.getByName(name+"_fp").isNull()) - mgr.remove(name+"_fp"); - - fragment = mgr.createProgram(name+"_fp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_FRAGMENT_PROGRAM); - fragment->setParameter("profiles", "ps_4_0 ps_2_x fp40 arbfp1"); - fragment->setParameter("entry_point", "main_fp"); - StringUtil::StrStreamType outStream; - - if (shadows) outStream << - "float depthShadow(sampler2D shadowMap, float4 shadowMapPos, float2 offset) \n" - "{ \n" - " shadowMapPos /= shadowMapPos.w; \n" - " float3 o = float3(offset.xy, -offset.x) * 0.3f; \n" - " float c = (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left \n" - " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right \n" - " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left \n" - " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right \n" - " return c / 4; \n" - "} \n"; - - outStream << - "void main_fp( \n" - " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n" - " uniform sampler2D texture : register(s0), \n" - " float4 positionObjSpace : TEXCOORD1, \n" - " float4 normal : TEXCOORD2, \n" - " float iDepth : TEXCOORD3, \n" - " float4 vertexColour : TEXCOORD4, \n" - " uniform float4 fogColour, \n" - " uniform float4 fogParams, \n"; - - if (shadows) outStream << - " uniform float4 shadowFar_fadeStart, \n"; - - if (shadows && !split) outStream << - " uniform sampler2D shadowMap : register(s1), \n" - " float4 lightSpacePos0 : TEXCOORD5, \n" - " uniform float4 invShadowmapSize0, \n"; - else - { - outStream << - " uniform float4 pssmSplitPoints, \n"; - for (int i=0; i shadowFar_fadeStart.x) ? 1 : ((iDepth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); \n" - " lightColour.xyz += shadow * lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<setSource(outStream.str()); - fragment->load(); - - for (int i=0; igetDefaultParameters()->setNamedAutoConstant("lightPositionObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i); - fragment->getDefaultParameters()->setNamedAutoConstant("lightDiffuse"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i); - fragment->getDefaultParameters()->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); - } - fragment->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - fragment->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - fragment->getDefaultParameters()->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_SURFACE_AMBIENT_COLOUR); - fragment->getDefaultParameters()->setNamedAutoConstant("lightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); - fragment->getDefaultParameters()->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); - fragment->getDefaultParameters()->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS); - - if (shadows) - { - fragment->getDefaultParameters()->setNamedConstant("shadowFar_fadeStart", Vector4(mRendering->getShadows()->getShadowFar(), mRendering->getShadows()->getFadeStart()*mRendering->getShadows()->getShadowFar(), 0, 0)); - for (int i=0; i < (split ? numsplits : 1); ++i) - { - fragment->getDefaultParameters()->setNamedAutoConstant("invShadowmapSize" + StringConverter::toString(i), GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i+1); - } - if (split) - { - Vector4 splitPoints; - const PSSMShadowCameraSetup::SplitPointList& splitPointList = mRendering->getShadows()->getPSSMSetup()->getSplitPoints(); - // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) - for (int i = 1; i < numsplits; ++i) - { - splitPoints[i-1] = splitPointList[i]; - } - fragment->getDefaultParameters()->setNamedConstant("pssmSplitPoints", splitPoints); - } - } - - if (mrt) - fragment->getDefaultParameters()->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE); - } -} diff --git a/apps/openmw/mwrender/shaderhelper.hpp b/apps/openmw/mwrender/shaderhelper.hpp deleted file mode 100644 index 356d345de..000000000 --- a/apps/openmw/mwrender/shaderhelper.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef GAME_SHADERHELPER_H -#define GAME_SHADERHELPER_H - -#include - -namespace MWRender -{ - class RenderingManager; - - /// - /// \brief manages the main shader - /// - class ShaderHelper - { - public: - ShaderHelper(RenderingManager* rend); - - void applyShaders(); - ///< apply new settings - - private: - RenderingManager* mRendering; - - void createShader(const bool mrt, const bool shadows, const bool split, const std::string& name); - }; - -} - -#endif From d3a31a24ce860cb907defe2288bf5bfd7a9c871c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 9 Jul 2012 20:53:54 -0700 Subject: [PATCH 079/688] Use proper strings and vectors instead of slice arrays for NIF files Slice arrays use pre-allocated pointers whose memory is managed externally. This is unnecessary and ultimately detrimental since it prevents any kind of data fixup (e.g. little endian to big endian, p[adding handling), and it also makes it difficult to use Ogre data streams. --- components/nif/controlled.hpp | 2 +- components/nif/data.hpp | 10 +-- components/nif/extra.hpp | 4 +- components/nif/nif_file.cpp | 8 +- components/nif/nif_file.hpp | 28 +++++-- components/nif/node.hpp | 8 +- components/nif/record.hpp | 2 +- components/nifbullet/bullet_nif_loader.cpp | 8 +- components/nifogre/ogre_nif_loader.cpp | 93 +++++++++++----------- 9 files changed, 86 insertions(+), 77 deletions(-) diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index 24281146f..b9d84b58a 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -53,7 +53,7 @@ public: class Named : public Controlled { public: - Misc::SString name; + std::string name; void read(NIFFile *nif) { diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 0684470ec..9cdbec537 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -39,8 +39,8 @@ public: // internal (data is inside the nif itself) texture? bool external; - Misc::SString filename; // In case of external textures - NiPixelDataPtr data; // In case of internal textures + std::string filename; // In case of external textures + NiPixelDataPtr data; // In case of internal textures /* Pixel layout 0 - Palettised @@ -96,7 +96,7 @@ public: class ShapeData : public Record { public: - Misc::FloatArray vertices, normals, colors, uvlist; + std::vector vertices, normals, colors, uvlist; const Vector *center; float radius; @@ -131,7 +131,7 @@ class NiTriShapeData : public ShapeData { public: // Triangles, three vertex indices per triangle - Misc::SliceArray triangles; + std::vector triangles; void read(NIFFile *nif) { @@ -390,7 +390,7 @@ public: { const BoneTrafo *trafo; const Vector4 *unknown; - Misc::SliceArray weights; + std::vector weights; }; struct BoneInfoCopy { diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index ad788661a..e5829fdfc 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -66,7 +66,7 @@ public: struct TextKey { float time; - Misc::SString text; + std::string text; }; std::vector list; @@ -93,7 +93,7 @@ public: "MRK" - marker, only visible in the editor, not rendered in-game "NCO" - no collision */ - Misc::SString string; + std::string string; void read(NIFFile *nif) { diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 42aa43f8b..35714bfbe 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -46,8 +46,8 @@ using namespace Misc; void NIFFile::parse() { // Check the header string - const char* head = getString(40); - if(!begins(head, "NetImmerse File Format")) + std::string head = getString(40); + if(head.compare(0, 22, "NetImmerse File Format") != 0) fail("Invalid NIF header"); // Get BCD version @@ -70,7 +70,7 @@ void NIFFile::parse() for(int i=0;irecType != RC_MISSING); diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index bb6f73259..d3f5f7c06 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -122,29 +122,41 @@ public: char getByte() { return getType(); } template - Misc::SliceArray getArrayLen(int num) - { return Misc::SliceArray((const X*)inp->getPtr(num*sizeof(X)),num); } + std::vector getArrayLen(int num) + { + std::vector v(num); + memcpy(&v[0], inp->getPtr(num*sizeof(X)), num*sizeof(X)); + return v; + } template - Misc::SliceArray getArray() + std::vector getArray() { int len = getInt(); return getArrayLen(len); } - Misc::SString getString() { return getArray(); } - const Vector *getVector() { return getPtr(); } const Matrix *getMatrix() { return getPtr(); } const Transformation *getTrafo() { return getPtr(); } const Vector4 *getVector4() { return getPtr(); } - Misc::FloatArray getFloatLen(int num) + std::vector getFloatLen(int num) { return getArrayLen(num); } // For fixed-size strings where you already know the size - const char *getString(int size) - { return (const char*)inp->getPtr(size); } + std::string getString(size_t size) + { + std::string str; + str.resize(size); + memcpy(&str[0], inp->getPtr(size), size); + return str.substr(0, str.find('\0')); + } + std::string getString() + { + size_t size = getInt(); + return getString(size); + } }; } // Namespace diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 5a2847b6c..9d99dbd1d 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -186,10 +186,10 @@ struct NiTriShape : Node NiTriShapeCopy clone() { NiTriShapeCopy copy; - copy.sname = name.toString(); - float *ptr = (float*)data->vertices.ptr; - float *ptrNormals = (float*)data->normals.ptr; - int numVerts = data->vertices.length / 3; + copy.sname = name; + float *ptr = (float*)&data->vertices[0]; + float *ptrNormals = (float*)&data->normals[0]; + int numVerts = data->vertices.size() / 3; for(int i = 0; i < numVerts; i++) { float *current = (float*) (ptr + i * 3); diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 06fdce55e..84f253eb8 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -90,7 +90,7 @@ struct Record { // Record type and type name int recType; - Misc::SString recName; + std::string recName; Record() : recType(RC_MISSING) {} diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 30cb4562d..9f31a3eed 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -124,7 +124,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) if (node == NULL) { warn("First record in file was not a node, but a " + - r->recName.toString() + ". Skipping file."); + r->recName + ". Skipping file."); return; } @@ -292,10 +292,10 @@ void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape *shape, int flags Nif::NiTriShapeData *data = shape->data.getPtr(); - float* vertices = (float*)data->vertices.ptr; - unsigned short* triangles = (unsigned short*)data->triangles.ptr; + float* vertices = (float*)&data->vertices[0]; + unsigned short* triangles = (unsigned short*)&data->triangles[0]; - for(unsigned int i=0; i < data->triangles.length; i = i+3) + for(unsigned int i=0; i < data->triangles.size(); i = i+3) { Ogre::Vector3 b1(vertices[triangles[i+0]*3]*parentScale,vertices[triangles[i+0]*3+1]*parentScale,vertices[triangles[i+0]*3+2]*parentScale); Ogre::Vector3 b2(vertices[triangles[i+1]*3]*parentScale,vertices[triangles[i+1]*3+1]*parentScale,vertices[triangles[i+1]*3+2]*parentScale); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 895c51a0d..55dbba518 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -399,7 +399,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std { // cout << "s:" << shape << "\n"; NiTriShapeData *data = shape->data.getPtr(); - SubMesh *sub = mesh->createSubMesh(shape->name.toString()); + SubMesh *sub = mesh->createSubMesh(shape->name); int nextBuf = 0; @@ -407,7 +407,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std // great. // Add vertices - int numVerts = data->vertices.length / 3; + int numVerts = data->vertices.size() / 3; sub->vertexData = new VertexData(); sub->vertexData->vertexCount = numVerts; sub->useSharedVertices = false; @@ -422,12 +422,12 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std if(flip) { - float *datamod = new float[data->vertices.length]; + float *datamod = new float[data->vertices.size()]; //std::cout << "Shape" << shape->name.toString() << "\n"; for(int i = 0; i < numVerts; i++) { int index = i * 3; - const float *pos = data->vertices.ptr + index; + const float *pos = &data->vertices[index]; Ogre::Vector3 original = Ogre::Vector3(*pos ,*(pos+1), *(pos+2)); original = mTransform * original; mBoundingBox.merge(original); @@ -440,14 +440,14 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std } else { - vbuf->writeData(0, vbuf->getSizeInBytes(), data->vertices.ptr, false); + vbuf->writeData(0, vbuf->getSizeInBytes(), &data->vertices[0], false); } VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding; bind->setBinding(nextBuf++, vbuf); - if (data->normals.length) + if (data->normals.size()) { decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL); vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( @@ -459,11 +459,11 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std Quaternion rotation = mTransform.extractQuaternion(); rotation.normalise(); - float *datamod = new float[data->normals.length]; + float *datamod = new float[data->normals.size()]; for(int i = 0; i < numVerts; i++) { int index = i * 3; - const float *pos = data->normals.ptr + index; + const float *pos = &data->normals[index]; Ogre::Vector3 original = Ogre::Vector3(*pos ,*(pos+1), *(pos+2)); original = rotation * original; if (mNormaliseNormals) @@ -481,16 +481,16 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std } else { - vbuf->writeData(0, vbuf->getSizeInBytes(), data->normals.ptr, false); + vbuf->writeData(0, vbuf->getSizeInBytes(), &data->normals[0], false); } bind->setBinding(nextBuf++, vbuf); } // Vertex colors - if (data->colors.length) + if (data->colors.size()) { - const float *colors = data->colors.ptr; + const float *colors = &data->colors[0]; RenderSystem* rs = Root::getSingleton().getRenderSystem(); std::vector colorsRGB(numVerts); RGBA *pColour = &colorsRGB.front(); @@ -508,7 +508,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std bind->setBinding(nextBuf++, vbuf); } - if (data->uvlist.length) + if (data->uvlist.size()) { decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES); @@ -518,12 +518,12 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std if(flip) { - float *datamod = new float[data->uvlist.length]; + float *datamod = new float[data->uvlist.size()]; - for(unsigned int i = 0; i < data->uvlist.length; i+=2){ - float x = *(data->uvlist.ptr + i); + for(unsigned int i = 0; i < data->uvlist.size(); i+=2){ + float x = data->uvlist[i]; - float y = *(data->uvlist.ptr + i + 1); + float y = data->uvlist[i + 1]; datamod[i] =x; datamod[i + 1] =y; @@ -532,13 +532,12 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std delete [] datamod; } else - vbuf->writeData(0, vbuf->getSizeInBytes(), data->uvlist.ptr, false); + vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[0], false); bind->setBinding(nextBuf++, vbuf); } // Triangle faces - The total number of triangle points - int numFaces = data->triangles.length; - + int numFaces = data->triangles.size(); if (numFaces) { @@ -558,7 +557,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std for (size_t i = 0; i < sub->indexData->indexCount; i+=3) { - const short *pos = data->triangles.ptr + index; + const short *pos = &data->triangles[index]; uint16 i0 = (uint16) *(pos+0); uint16 i1 = (uint16) *(pos+1); uint16 i2 = (uint16) *(pos+2); @@ -578,7 +577,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std } else - ibuf->writeData(0, ibuf->getSizeInBytes(), data->triangles.ptr, false); + ibuf->writeData(0, ibuf->getSizeInBytes(), &data->triangles[0], false); sub->indexData->indexBuffer = ibuf; } @@ -706,8 +705,6 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou NiSourceTexture *st = t->textures[0].texture.getPtr(); if (st->external) { - SString tname = st->filename; - /* findRealTexture checks if the file actually exists. If it doesn't, and the name ends in .tga, it will try replacing the extension with .dds instead @@ -721,7 +718,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou problem since all the nif data is stored in a local throwaway buffer. */ - texName = "textures\\" + tname.toString(); + texName = "textures\\" + st->filename; findRealTexture(texName); } else warn("Found internal texture, ignoring."); @@ -795,9 +792,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou level. */ NiTriShapeData *data = shape->data.getPtr(); - int numVerts = data->vertices.length / 3; + int numVerts = data->vertices.size() / 3; - float *ptr = (float*)data->vertices.ptr; + float *ptr = (float*)&data->vertices[0]; float *optr = ptr; std::list vertexBoneAssignments; @@ -828,7 +825,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou std::vector vertexPosOriginal(numVerts, Ogre::Vector3::ZERO); std::vector vertexNormalOriginal(numVerts, Ogre::Vector3::ZERO); - float *ptrNormals = (float*)data->normals.ptr; + float *ptrNormals = (float*)&data->normals[0]; //the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex] //the first one contains a link to the bone, the second vertex transformation //relative to the bone @@ -849,13 +846,13 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou { if(mSkel.isNull()) { - std::cout << "No skeleton for :" << shape->skin->bones[boneIndex].name.toString() << std::endl; + std::cout << "No skeleton for :" << shape->skin->bones[boneIndex].name << std::endl; break; } //get the bone from bones array of skindata - if(!mSkel->hasBone(shape->skin->bones[boneIndex].name.toString())) + if(!mSkel->hasBone(shape->skin->bones[boneIndex].name)) std::cout << "We don't have this bone"; - bonePtr = mSkel->getBone(shape->skin->bones[boneIndex].name.toString()); + bonePtr = mSkel->getBone(shape->skin->bones[boneIndex].name); // final_vector = old_vector + old_rotation*new_vector*old_scale @@ -863,19 +860,19 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou Nif::NiSkinData::BoneInfoCopy boneinfocopy; boneinfocopy.trafo.rotation = convertRotation(it->trafo->rotation); boneinfocopy.trafo.trans = convertVector3(it->trafo->trans); - boneinfocopy.bonename = shape->skin->bones[boneIndex].name.toString(); + boneinfocopy.bonename = shape->skin->bones[boneIndex].name; boneinfocopy.bonehandle = bonePtr->getHandle(); copy.boneinfo.push_back(boneinfocopy); - for (unsigned int i=0; iweights.length; i++) + for (unsigned int i=0; iweights.size(); i++) { vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * convertVector3(it->trafo->trans); vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo->rotation); - unsigned int verIndex = (it->weights.ptr + i)->vertex; + unsigned int verIndex = it->weights[i].vertex; //boneinfo.weights.push_back(*(it->weights.ptr + i)); Nif::NiSkinData::IndividualWeight ind; - ind.weight = (it->weights.ptr + i)->weight; + ind.weight = it->weights[i].weight; ind.boneinfocopyindex = copy.boneinfo.size() - 1; if(copy.vertsToWeights.find(verIndex) == copy.vertsToWeights.end()) { @@ -893,7 +890,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou { //apply transformation to the vertices Vector3 absVertPos = vecPos + vecRot * Vector3(ptr + verIndex *3); - absVertPos = absVertPos * (it->weights.ptr + i)->weight; + absVertPos = absVertPos * it->weights[i].weight; vertexPosOriginal[verIndex] = Vector3(ptr + verIndex *3); mBoundingBox.merge(absVertPos); @@ -903,10 +900,10 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou //apply rotation to the normals (not every vertex has a normal) //FIXME: I guessed that vertex[i] = normal[i], is that true? - if (verIndex < data->normals.length) + if (verIndex < data->normals.size()) { Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3); - absNormalsPos = absNormalsPos * (it->weights.ptr + i)->weight; + absNormalsPos = absNormalsPos * it->weights[i].weight; vertexNormalOriginal[verIndex] = Vector3(ptrNormals + verIndex *3); for (int j=0; j<3; j++) @@ -918,7 +915,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou else { Vector3 absVertPos = vecPos + vecRot * vertexPosOriginal[verIndex]; - absVertPos = absVertPos * (it->weights.ptr + i)->weight; + absVertPos = absVertPos * it->weights[i].weight; Vector3 old = Vector3(ptr + verIndex *3); absVertPos = absVertPos + old; @@ -929,10 +926,10 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou //apply rotation to the normals (not every vertex has a normal) //FIXME: I guessed that vertex[i] = normal[i], is that true? - if (verIndex < data->normals.length) + if (verIndex < data->normals.size()) { Vector3 absNormalsPos = vecRot * vertexNormalOriginal[verIndex]; - absNormalsPos = absNormalsPos * (it->weights.ptr + i)->weight; + absNormalsPos = absNormalsPos * it->weights[i].weight; Vector3 oldNormal = Vector3(ptrNormals + verIndex *3); absNormalsPos = absNormalsPos + oldNormal; @@ -945,7 +942,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou VertexBoneAssignment vba; vba.boneIndex = bonePtr->getHandle(); vba.vertexIndex = verIndex; - vba.weight = (it->weights.ptr + i)->weight; + vba.weight = it->weights[i].weight; vertexBoneAssignments.push_back(vba); @@ -981,9 +978,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou } // Remember to rotate all the vertex normals as well - if (data->normals.length) + if (data->normals.size()) { - ptr = (float*)data->normals.ptr; + ptr = (float*)&data->normals[0]; for (int i=0; i::iterator textiter = extra->list.begin(); textiter != extra->list.end(); textiter++) { - std::string text = textiter->text.toString(); + std::string text = textiter->text; replace(text.begin(), text.end(), '\n', '/'); @@ -1138,7 +1135,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, if (!mSkel.isNull()) //if there is a skeleton { - std::string name = node->name.toString(); + std::string name = node->name; // Quick-n-dirty workaround for the fact that several // bones may have the same name. @@ -1192,7 +1189,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, } else if (node->recType == RC_NiTriShape && bNiTri) { - std::string nodename = node->name.toString(); + std::string nodename = node->name; if (triname == "") { @@ -1334,7 +1331,7 @@ void NIFLoader::loadResource(Resource *resource) if (node == NULL) { warn("First record in file was not a node, but a " + - r->recName.toString() + ". Skipping file."); + r->recName + ". Skipping file."); return; } @@ -1358,7 +1355,7 @@ void NIFLoader::loadResource(Resource *resource) if (f->timeStart >= 10000000000000000.0f) continue; - data->setBonename(o->name.toString()); + data->setBonename(o->name); data->setStartTime(f->timeStart); data->setStopTime(f->timeStop); From 0143cacd2bebdf7a13b5e2539e54b2f3da8959bd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 9 Jul 2012 21:35:36 -0700 Subject: [PATCH 080/688] Avoid returning pointers from NIFFile --- components/nif/data.hpp | 12 +++++----- components/nif/effect.hpp | 4 ++-- components/nif/nif_file.cpp | 2 +- components/nif/nif_file.hpp | 14 ++++++----- components/nif/nif_types.hpp | 4 ++-- components/nif/node.hpp | 14 +++++------ components/nif/property.hpp | 4 ++-- components/nifbullet/bullet_nif_loader.cpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 28 +++++++++++----------- 9 files changed, 43 insertions(+), 41 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 9cdbec537..a9a5895f4 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -97,7 +97,7 @@ class ShapeData : public Record { public: std::vector vertices, normals, colors, uvlist; - const Vector *center; + Vector center; float radius; void read(NIFFile *nif) @@ -388,8 +388,8 @@ public: struct BoneInfo { - const BoneTrafo *trafo; - const Vector4 *unknown; + BoneTrafo trafo; + Vector4 unknown; std::vector weights; }; struct BoneInfoCopy @@ -406,7 +406,7 @@ public: unsigned int boneinfocopyindex; }; - const BoneTrafo *trafo; + BoneTrafo trafo; std::vector bones; void read(NIFFile *nif) @@ -414,7 +414,7 @@ public: assert(sizeof(BoneTrafo) == 4*(9+3+1)); assert(sizeof(VertWeight) == 6); - trafo = nif->getPtr(); + trafo = nif->getType(); int boneNum = nif->getInt(); nif->getInt(); // -1 @@ -424,7 +424,7 @@ public: { BoneInfo &bi = bones[i]; - bi.trafo = nif->getPtr(); + bi.trafo = nif->getType(); bi.unknown = nif->getVector4(); // Number of vertex weights diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index bac412c76..6ecc7c61a 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -42,7 +42,7 @@ struct NiLight : Effect Vector diffuse; Vector specular; }; - const SLight *light; + SLight light; void read(NIFFile *nif) { @@ -50,7 +50,7 @@ struct NiLight : Effect nif->getInt(); // 1 nif->getInt(); // 1? - light = nif->getPtr(); + light = nif->getType(); } }; diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 35714bfbe..36badbf0d 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -201,7 +201,7 @@ void NiSkinInstance::post(NIFFile *nif) if(bnum != data->bones.size()) nif->fail("Mismatch in NiSkinData bone count"); - root->makeRootBone(data->trafo); + root->makeRootBone(&data->trafo); for(size_t i=0; igetPtr(size); } - template const X* getPtr() { return (const X*)inp->getPtr(sizeof(X)); } - template X getType() { return *getPtr(); } + template X getType() + { + return *(const X*)inp->getPtr(sizeof(X)); + } unsigned short getShort() { return getType(); } int getInt() { return getType(); } float getFloat() { return getType(); } @@ -136,10 +138,10 @@ public: return getArrayLen(len); } - const Vector *getVector() { return getPtr(); } - const Matrix *getMatrix() { return getPtr(); } - const Transformation *getTrafo() { return getPtr(); } - const Vector4 *getVector4() { return getPtr(); } + Vector getVector() { return getType(); } + Matrix getMatrix() { return getType(); } + Transformation getTrafo() { return getType(); } + Vector4 getVector4() { return getType(); } std::vector getFloatLen(int num) { return getArrayLen(num); } diff --git a/components/nif/nif_types.hpp b/components/nif/nif_types.hpp index ee796cc99..41900a14e 100644 --- a/components/nif/nif_types.hpp +++ b/components/nif/nif_types.hpp @@ -60,7 +60,7 @@ struct Transformation float scale; Vector velocity; - static const Transformation* getIdentity() + static const Transformation& getIdentity() { static Transformation identity; static bool iset = false; @@ -73,7 +73,7 @@ struct Transformation iset = true; } - return &identity; + return identity; } }; #pragma pack(pop) diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 9d99dbd1d..8a6af5777 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -42,14 +42,14 @@ class Node : public Named public: // Node flags. Interpretation depends somewhat on the type of node. int flags; - const Transformation *trafo; + Transformation trafo; PropertyList props; // Bounding box info bool hasBounds; - const Vector *boundPos; - const Matrix *boundRot; - const Vector *boundXYZ; // Box size + Vector boundPos; + Matrix boundRot; + Vector boundXYZ; // Box size void read(NIFFile *nif) { @@ -103,7 +103,7 @@ public: void makeBone(short ind, const NiSkinData::BoneInfo &bi) { boneInfo = &bi; - boneTrafo = bi.trafo; + boneTrafo = &bi.trafo; boneIndex = ind; } }; @@ -219,13 +219,13 @@ struct NiCamera : Node // Level of detail modifier float LOD; }; - const Camera *cam; + Camera cam; void read(NIFFile *nif) { Node::read(nif); - nif->getPtr(); + cam = nif->getType(); nif->getInt(); // -1 nif->getInt(); // 0 diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 619e3db0e..8485d978f 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -155,12 +155,12 @@ typedef Property NiWireframeProperty; template struct StructPropT : Property { - const T* data; + T data; void read(NIFFile *nif) { Property::read(nif); - data = nif->getPtr(); + data = nif->getType(); } }; diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 9f31a3eed..f32b5e896 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -226,7 +226,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, Ogre::Vector3 finalPos; float finalScale; - Nif::Transformation &final = *((Nif::Transformation*)node->trafo); + Nif::Transformation &final = node->trafo; Ogre::Vector3 nodePos = getVector(&final); Ogre::Matrix3 nodeRot = getMatrix(&final); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 55dbba518..616e462fd 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -730,7 +730,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou if (a) { alphaFlags = a->flags; - alphaTest = a->data->threshold; + alphaTest = a->data.threshold; } // Material @@ -745,7 +745,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou if (m) { // Use NiMaterialProperty data to create the data - const S_MaterialProperty *d = m->data; + const S_MaterialProperty *d = &m->data; std::multimap::iterator itr = MaterialMap.find(texName); std::multimap::iterator lastElement; @@ -858,17 +858,17 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou Nif::NiSkinData::BoneInfoCopy boneinfocopy; - boneinfocopy.trafo.rotation = convertRotation(it->trafo->rotation); - boneinfocopy.trafo.trans = convertVector3(it->trafo->trans); + boneinfocopy.trafo.rotation = convertRotation(it->trafo.rotation); + boneinfocopy.trafo.trans = convertVector3(it->trafo.trans); boneinfocopy.bonename = shape->skin->bones[boneIndex].name; boneinfocopy.bonehandle = bonePtr->getHandle(); copy.boneinfo.push_back(boneinfocopy); for (unsigned int i=0; iweights.size(); i++) { vecPos = bonePtr->_getDerivedPosition() + - bonePtr->_getDerivedOrientation() * convertVector3(it->trafo->trans); + bonePtr->_getDerivedOrientation() * convertVector3(it->trafo.trans); - vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo->rotation); + vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo.rotation); unsigned int verIndex = it->weights[i].vertex; //boneinfo.weights.push_back(*(it->weights.ptr + i)); Nif::NiSkinData::IndividualWeight ind; @@ -959,9 +959,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou copy.boneSequence = boneSequence; // Rotate, scale and translate all the vertices, - const Matrix &rot = shape->trafo->rotation; - const Vector &pos = shape->trafo->pos; - float scale = shape->trafo->scale; + const Matrix &rot = shape->trafo.rotation; + const Vector &pos = shape->trafo.pos; + float scale = shape->trafo.scale; copy.trafo.trans = convertVector3(original.pos); copy.trafo.rotation = convertRotation(original.rotation); @@ -1148,19 +1148,19 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, parentBone->addChild(bone); bone->setInheritOrientation(true); - bone->setPosition(convertVector3(node->trafo->pos)); - bone->setOrientation(convertRotation(node->trafo->rotation)); + bone->setPosition(convertVector3(node->trafo.pos)); + bone->setOrientation(convertRotation(node->trafo.rotation)); } } } - Transformation original = *(node->trafo); + Transformation original = node->trafo; // Apply the parent transformation to this node. We overwrite the // existing data with the final transformation. if (trafo) { // Get a non-const reference to the node's data, since we're // overwriting it. TODO: Is this necessary? - Transformation &final = *((Transformation*)node->trafo); + Transformation &final = node->trafo; // For both position and rotation we have that: // final_vector = old_vector + old_rotation*new_vector*old_scale @@ -1184,7 +1184,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, { if (list.has(i)) - handleNode(&list[i], flags, node->trafo, bounds, bone, boneSequence); + handleNode(&list[i], flags, &node->trafo, bounds, bone, boneSequence); } } else if (node->recType == RC_NiTriShape && bNiTri) From b3aa453f9a2e005fb6ce203b56dffa612135a23c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 9 Jul 2012 22:02:12 -0700 Subject: [PATCH 081/688] Use Ogre data streams for loading NIFs --- components/nif/nif_file.hpp | 38 ++++++++-------------- components/nifbullet/bullet_nif_loader.cpp | 14 +------- components/nifbullet/bullet_nif_loader.hpp | 12 +------ components/nifogre/ogre_nif_loader.cpp | 24 +++----------- components/nifogre/ogre_nif_loader.hpp | 7 ---- 5 files changed, 21 insertions(+), 74 deletions(-) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 59ba3a5b9..a2246e00e 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -24,9 +24,8 @@ #ifndef _NIF_FILE_H_ #define _NIF_FILE_H_ -#include -#include -#include +#include +#include #include #include @@ -36,8 +35,6 @@ #include "record.hpp" #include "nif_types.hpp" -using namespace Mangle::Stream; - namespace Nif { @@ -51,7 +48,7 @@ class NIFFile int ver; /// Input stream - StreamPtr inp; + Ogre::DataStreamPtr inp; /// File name, used for error messages std::string filename; @@ -72,22 +69,10 @@ public: } /// Open a NIF stream. The name is used for error messages. - NIFFile(StreamPtr nif, const std::string &name) + NIFFile(const std::string &name) : filename(name) { - /* Load the entire file into memory. This allows us to use - direct pointers to the data arrays in the NIF, instead of - individually allocating and copying each one. - - The NIF data is only stored temporarily in memory, since once - the mesh data is loaded it is siphoned into OGRE and - deleted. For that reason, we might improve this further and - use a shared region/pool based allocation scheme in the - future, especially since only one NIFFile will ever be loaded - at any given time. - */ - inp = StreamPtr(new BufferStream(nif)); - + inp = Ogre::ResourceGroupManager::getSingleton().openResource(name); parse(); } @@ -112,11 +97,14 @@ public: Parser functions ****************************************************/ - void skip(size_t size) { inp->getPtr(size); } + void skip(size_t size) { inp->skip(size); } template X getType() { - return *(const X*)inp->getPtr(sizeof(X)); + X obj; + if(inp->read(&obj, sizeof(X)) != sizeof(X)) + fail("Failed to read from NIF"); + return obj; } unsigned short getShort() { return getType(); } int getInt() { return getType(); } @@ -127,7 +115,8 @@ public: std::vector getArrayLen(int num) { std::vector v(num); - memcpy(&v[0], inp->getPtr(num*sizeof(X)), num*sizeof(X)); + if(inp->read(&v[0], num*sizeof(X)) != num*sizeof(X)) + fail("Failed to read from NIF"); return v; } @@ -151,7 +140,8 @@ public: { std::string str; str.resize(size); - memcpy(&str[0], inp->getPtr(size), size); + if(inp->read(&str[0], size) != size) + fail("Failed to read from NIF"); return str.substr(0, str.find('\0')); } std::string getString() diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index f32b5e896..ae9ac94c2 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -25,7 +25,6 @@ http://www.gnu.org/licenses/ . #include #include -#include #include "../nif/nif_file.hpp" #include "../nif/node.hpp" #include "../nif/data.hpp" @@ -47,13 +46,11 @@ typedef unsigned char ubyte; using namespace std; using namespace Ogre; using namespace Nif; -using namespace Mangle::VFS; using namespace NifBullet; ManualBulletShapeLoader::~ManualBulletShapeLoader() { - delete vfs; } Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(Nif::Transformation* tr) @@ -94,20 +91,11 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mTriMesh = new btTriangleMesh(); - if (!vfs) vfs = new OgreVFS(resourceGroup); - - if (!vfs->isFile(resourceName)) - { - warn("File not found."); - return; - } - // Load the NIF. TODO: Wrap this in a try-catch block once we're out // of the early stages of development. Right now we WANT to catch // every error as early and intrusively as possible, as it's most // likely a sign of incomplete code rather than faulty input. - Nif::NIFFile nif(vfs->open(resourceName), resourceName); - + Nif::NIFFile nif(resourceName); if (nif.numRecords() < 1) { warn("Found no records in NIF."); diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index ed3aceac4..5a33074c9 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -50,14 +50,6 @@ namespace Nif class Matrix; } -namespace Mangle -{ - namespace VFS - { - class OgreVFS; - } -} - namespace NifBullet { @@ -68,7 +60,7 @@ class ManualBulletShapeLoader : public BulletShapeLoader { public: - ManualBulletShapeLoader():resourceGroup("General"){vfs = 0;} + ManualBulletShapeLoader():resourceGroup("General"){} virtual ~ManualBulletShapeLoader(); void warn(std::string msg) @@ -119,8 +111,6 @@ private: */ void handleNiTriShape(Nif::NiTriShape *shape, int flags,Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScales,bool raycastingOnly); - Mangle::VFS::OgreVFS *vfs; - std::string resourceName; std::string resourceGroup; diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 616e462fd..64a1e0871 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -379,16 +379,12 @@ String NIFLoader::getUniqueName(const String &input) // is lost in that case. void NIFLoader::findRealTexture(String &texName) { - assert(vfs); - if (vfs->isFile(texName)) return; - - int len = texName.size(); - if (len < 4) return; + if(Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + return; // Change texture extension to .dds - texName[len-3] = 'd'; - texName[len-2] = 'd'; - texName[len-1] = 's'; + String::size_type pos = texName.rfind('.'); + texName.replace(pos, texName.length(), ".dds"); } //Handle node at top @@ -1290,9 +1286,6 @@ void NIFLoader::loadResource(Resource *resource) { calculateTransform(); } - // Set up the VFS if it hasn't been done already - if (!vfs) vfs = new OgreVFS(resourceGroup); - // Get the mesh mesh = dynamic_cast(resource); assert(mesh); @@ -1301,12 +1294,6 @@ void NIFLoader::loadResource(Resource *resource) resourceName = mesh->getName(); //std::cout << resourceName << "\n"; - if (!vfs->isFile(resourceName)) - { - warn("File "+resourceName+" not found."); - return; - } - // Helper that computes bounding boxes for us. BoundsFinder bounds; @@ -1314,8 +1301,7 @@ void NIFLoader::loadResource(Resource *resource) // of the early stages of development. Right now we WANT to catch // every error as early and intrusively as possible, as it's most // likely a sign of incomplete code rather than faulty input. - NIFFile nif(vfs->open(resourceName), resourceName); - + NIFFile nif(resourceName); if (nif.numRecords() < 1) { warn("Found no records in NIF."); diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index d73948fa8..55915b310 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -154,13 +154,6 @@ class NIFLoader : Ogre::ManualResourceLoader return resourceName + ".skel"; } - // This is the interface to the Ogre resource system. It allows us to - // load NIFs from BSAs, in the file system and in any other place we - // tell Ogre to look (eg. in zip or rar files.) It's also used to - // check for the existence of texture files, so we can exchange the - // extension from .tga to .dds if the texture is missing. - Mangle::VFS::OgreVFS *vfs; - std::string verbosePath; std::string resourceName; std::string resourceGroup; From 98ae7168b1fe10ec86f2e8810d06448dc0362669 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 10 Jul 2012 00:24:18 -0700 Subject: [PATCH 082/688] Fix double-incrementing a pointer --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 64a1e0871..ec54319c1 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -968,7 +968,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou for (int i=0; i Date: Tue, 10 Jul 2012 00:27:13 -0700 Subject: [PATCH 083/688] Remove NIFFile::getType --- components/nif/data.hpp | 18 ++++-- components/nif/effect.hpp | 10 ++- components/nif/extra.hpp | 2 +- components/nif/nif_file.hpp | 124 ++++++++++++++++++++++++++++++------ components/nif/node.hpp | 19 +++++- components/nif/property.hpp | 23 ++++++- 6 files changed, 166 insertions(+), 30 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index a9a5895f4..1b5fa029b 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -105,16 +105,16 @@ public: int verts = nif->getShort(); if(nif->getInt()) - vertices = nif->getFloatLen(verts*3); + vertices = nif->getArrayLen(verts*3); if(nif->getInt()) - normals = nif->getFloatLen(verts*3); + normals = nif->getArrayLen(verts*3); center = nif->getVector(); radius = nif->getFloat(); if(nif->getInt()) - colors = nif->getFloatLen(verts*4); + colors = nif->getArrayLen(verts*4); int uvs = nif->getShort(); @@ -123,7 +123,7 @@ public: uvs &= 0x3f; if(nif->getInt()) - uvlist = nif->getFloatLen(uvs*verts*2); + uvlist = nif->getArrayLen(uvs*verts*2); } }; @@ -181,7 +181,7 @@ public: if(nif->getInt()) { // Particle sizes - nif->getFloatLen(activeCount); + nif->getArrayLen(activeCount); } } }; @@ -414,7 +414,9 @@ public: assert(sizeof(BoneTrafo) == 4*(9+3+1)); assert(sizeof(VertWeight) == 6); - trafo = nif->getType(); + trafo.rotation = nif->getMatrix(); + trafo.trans = nif->getVector(); + trafo.scale = nif->getFloat(); int boneNum = nif->getInt(); nif->getInt(); // -1 @@ -424,7 +426,9 @@ public: { BoneInfo &bi = bones[i]; - bi.trafo = nif->getType(); + bi.trafo.rotation = nif->getMatrix(); + bi.trafo.trans = nif->getVector(); + bi.trafo.scale = nif->getFloat(); bi.unknown = nif->getVector4(); // Number of vertex weights diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index 6ecc7c61a..f48049ec4 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -41,6 +41,14 @@ struct NiLight : Effect Vector ambient; Vector diffuse; Vector specular; + + void read(NIFFile *nif) + { + nif->load(dimmer); + ambient = nif->getVector(); + diffuse = nif->getVector(); + specular = nif->getVector(); + } }; SLight light; @@ -50,7 +58,7 @@ struct NiLight : Effect nif->getInt(); // 1 nif->getInt(); // 1? - light = nif->getType(); + light.read(nif); } }; diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index e5829fdfc..5615d833e 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -56,7 +56,7 @@ public: /*int i =*/ nif->getInt(); int s = nif->getShort(); // number of vertices - nif->getFloatLen(s); // vertex weights I guess + nif->getArrayLen(s); // vertex weights I guess } }; diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index a2246e00e..c4b138232 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -99,20 +99,70 @@ public: void skip(size_t size) { inp->skip(size); } - template X getType() + uint32_t read_le32() { - X obj; - if(inp->read(&obj, sizeof(X)) != sizeof(X)) - fail("Failed to read from NIF"); - return obj; + uint8_t buffer[4]; + if(inp->read(buffer, 4) != 4) return 0; + return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); + } + uint16_t read_le16() + { + uint8_t buffer[2]; + if(inp->read(buffer, 2) != 2) return 0; + return buffer[0] | (buffer[1]<<8); + } + uint8_t read_byte() + { + uint8_t byte; + if(inp->read(&byte, 1) != 1) return 0; + return byte; + } + std::string read_string(size_t length) + { + std::string str; + str.resize(length); + if(inp->read(&str[0], length) != length) + return std::string(); + return str.substr(0, str.find('\0')); } - unsigned short getShort() { return getType(); } - int getInt() { return getType(); } - float getFloat() { return getType(); } - char getByte() { return getType(); } - template - std::vector getArrayLen(int num) + + char& load(char &c) { c = read_byte(); return c; } + unsigned char& load(unsigned char &c) { c = read_byte(); return c; } + short& load(short &s) { s = read_le16(); return s; } + unsigned short& load(unsigned short &s) { s = read_le16(); return s; } + int& load(int &i) { i = read_le32(); return i; } + unsigned int& load(unsigned int &i) { i = read_le32(); return i; } + float& load(float &f) + { + union { + int i; + float f; + } u = { read_le32() }; + f = u.f; + return f; + } + + template + T* load(T (&a)[N]) + { + for(size_t i = 0;i < N;i++) + load(a[i]); + return a; + } + + template + std::vector& load(std::vector &v, size_t size) + { + v.resize(size); + for(size_t i = 0;i < size;i++) + load(v[i]); + return v; + } + + + template + std::vector getArrayLen(size_t num) { std::vector v(num); if(inp->read(&v[0], num*sizeof(X)) != num*sizeof(X)) @@ -120,20 +170,47 @@ public: return v; } - template + template std::vector getArray() { - int len = getInt(); + size_t len = read_le32(); return getArrayLen(len); } - Vector getVector() { return getType(); } - Matrix getMatrix() { return getType(); } - Transformation getTrafo() { return getType(); } - Vector4 getVector4() { return getType(); } + char getByte() { char c; return load(c); } + unsigned short getShort() { unsigned short s; return load(s); } + int getInt() { int i; return load(i); } + float getFloat() { float f; return load(f); } + Vector getVector() + { + Vector v; + load(v.array); + return v; + } + Vector4 getVector4() + { + Vector4 v; + load(v.array); + return v; + } + Matrix getMatrix() + { + Matrix m; + m.v[0] = getVector(); + m.v[1] = getVector(); + m.v[2] = getVector(); + return m; + } + Transformation getTrafo() + { + Transformation t; + t.pos = getVector(); + t.rotation = getMatrix(); + load(t.scale); + t.velocity = getVector(); + return t; + } - std::vector getFloatLen(int num) - { return getArrayLen(num); } // For fixed-size strings where you already know the size std::string getString(size_t size) @@ -151,5 +228,14 @@ public: } }; +template<> +inline std::vector NIFFile::getArrayLen(size_t num) +{ + std::vector v(num); + for(size_t i = 0;i < num;i++) + load(v[i]); + return v; +} + } // Namespace #endif diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 8a6af5777..d5cd8fe82 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -218,6 +218,23 @@ struct NiCamera : Node // Level of detail modifier float LOD; + + void read(NIFFile *nif) + { + nif->load(left); + nif->load(right); + nif->load(top); + nif->load(bottom); + nif->load(nearDist); + nif->load(farDist); + + nif->load(vleft); + nif->load(vright); + nif->load(vtop); + nif->load(vbottom); + + nif->load(LOD); + } }; Camera cam; @@ -225,7 +242,7 @@ struct NiCamera : Node { Node::read(nif); - cam = nif->getType(); + cam.read(nif); nif->getInt(); // -1 nif->getInt(); // 0 diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 8485d978f..87e3ae5f2 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -160,7 +160,7 @@ struct StructPropT : Property void read(NIFFile *nif) { Property::read(nif); - data = nif->getType(); + data.read(nif); } }; @@ -169,6 +169,16 @@ struct S_MaterialProperty // The vector components are R,G,B Vector ambient, diffuse, specular, emissive; float glossiness, alpha; + + void read(NIFFile *nif) + { + ambient = nif->getVector(); + diffuse = nif->getVector(); + specular = nif->getVector(); + emissive = nif->getVector(); + nif->load(glossiness); + nif->load(alpha); + } }; struct S_VertexColorProperty @@ -183,6 +193,12 @@ struct S_VertexColorProperty 1 - lighting emmisive ambient/diffuse */ int vertmode, lightmode; + + void read(NIFFile *nif) + { + nif->load(vertmode); + nif->load(lightmode); + } }; struct S_AlphaProperty @@ -234,6 +250,11 @@ struct S_AlphaProperty // Tested against when certain flags are set (see above.) unsigned char threshold; + + void read(NIFFile *nif) + { + nif->load(threshold); + } }; typedef StructPropT NiAlphaProperty; From 410b69355539f9ad5d328e0fdd0b4b21b5962d98 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 10 Jul 2012 11:15:46 +0200 Subject: [PATCH 084/688] setAngle improvement --- apps/openmw/mwclass/npc.cpp | 7 +++++++ apps/openmw/mwclass/npc.hpp | 2 ++ .../openmw/mwscript/transformationextensions.cpp | 16 ++++++++++------ apps/openmw/mwworld/worldimp.cpp | 4 ++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ab4e2d5e6..0d2efcd9e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -347,4 +347,11 @@ namespace MWClass return weight; } + + void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const + { + y = 0; + x = 0; + std::cout << "dfdfdfdfnzdofnmqsldgnmqskdhblqkdbv lqksdf"; + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 4cb733977..f50ed2159 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -76,6 +76,8 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; + static void registerSelf(); }; } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 41eba4a99..2ea80c0d8 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -62,17 +62,21 @@ namespace MWScript Interpreter::Type_Float angle = runtime[0].mFloat; runtime.pop(); + float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); + if(axis == "X") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,0,0); + MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); } if(axis == "Y") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,0,angle,0); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); } if(axis == "Z") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,0,0,angle); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); } } }; @@ -91,15 +95,15 @@ namespace MWScript if(axis == "X") { - runtime.push(ptr.getRefData().getPosition().rot[0]); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); } if(axis == "Y") { - runtime.push(ptr.getRefData().getPosition().rot[1]); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); } if(axis == "Z") { - runtime.push(ptr.getRefData().getPosition().rot[0]); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); } } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5309fbe40..1e1fae154 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -602,7 +602,7 @@ namespace MWWorld MWWorld::Class::get(ptr).adjustScale(ptr,scale); ptr.getCellRef().scale = scale; - scale = scale/ptr.getRefData().getBaseNode()->getScale().x; + //scale = scale/ptr.getRefData().getBaseNode()->getScale().x; ptr.getRefData().getBaseNode()->setScale(scale,scale,scale); mPhysics->scaleObject( ptr.getRefData().getHandle(), scale ); } @@ -618,7 +618,7 @@ namespace MWWorld Ogre::Quaternion rotx(Ogre::Degree(x),Ogre::Vector3::UNIT_X); Ogre::Quaternion roty(Ogre::Degree(y),Ogre::Vector3::UNIT_Y); Ogre::Quaternion rotz(Ogre::Degree(z),Ogre::Vector3::UNIT_Z); - ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); + ptr.getRefData().getBaseNode()->setOrientation(rotz*rotx*roty); mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); } From ca37706b3406c1b88ee1c754097b4a917353679b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 10 Jul 2012 02:38:35 -0700 Subject: [PATCH 085/688] Use Ogre types for Matrix and Vector objects --- components/nif/data.hpp | 54 ++--- components/nif/effect.hpp | 6 +- components/nif/nif_file.hpp | 55 +++-- components/nif/nif_types.hpp | 40 +-- components/nif/node.hpp | 6 +- components/nif/property.hpp | 2 +- components/nifbullet/bullet_nif_loader.cpp | 20 +- components/nifbullet/bullet_nif_loader.hpp | 6 +- components/nifogre/ogre_nif_loader.cpp | 269 +++++++++------------ components/nifogre/ogre_nif_loader.hpp | 20 +- 10 files changed, 192 insertions(+), 286 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 1b5fa029b..667e77ffd 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -97,7 +97,7 @@ class ShapeData : public Record { public: std::vector vertices, normals, colors, uvlist; - Vector center; + Ogre::Vector3 center; float radius; void read(NIFFile *nif) @@ -198,7 +198,7 @@ public: // Rotation quaternions. I THINK activeCount is correct here, // but verts (vertex number) might also be correct, if there is // any case where the two don't match. - nif->getArrayLen(activeCount); + nif->skip(activeCount * 4*sizeof(float)); } } }; @@ -244,7 +244,7 @@ public: if(count) { nif->getInt(); // always 2 - nif->getArrayLen(count); // Really one time float + one vector + nif->skip(count * (sizeof(float) + 3*sizeof(float))); // Really one time float + one vector } } // Always 0 @@ -260,7 +260,7 @@ public: { int count = nif->getInt(); nif->getInt(); // always 2 - nif->getArrayLen(count); // Really one time float + one vector + nif->skip(count * (sizeof(float) + 3*sizeof(float))); // Really one time float + one vector } }; @@ -309,7 +309,7 @@ public: struct ColorData { float time; - Vector4 rgba; + Ogre::Vector4 rgba; }; void read(NIFFile *nif) @@ -318,25 +318,23 @@ public: nif->getInt(); // always 1 // Skip the data - assert(sizeof(ColorData) == 4*5); - nif->skip(sizeof(ColorData) * count); + nif->skip(count * 5*sizeof(float)); } }; class NiVisData : public Record { public: + struct VisData { + float time; + char isSet; + }; + void read(NIFFile *nif) { int count = nif->getInt(); - /* - Each VisData consists of: - float time; - byte isSet; - If you implement this, make sure you use a packed struct - (sizeof==5), or read each element individually. - */ + /* Skip VisData */ nif->skip(count*5); } }; @@ -361,16 +359,11 @@ public: class NiSkinData : public Record { public: - // This is to make sure the structs are packed, ie. that the - // compiler doesn't mess them up with extra alignment bytes. -#pragma pack(push) -#pragma pack(1) - struct BoneTrafo { - Matrix rotation; // Rotation offset from bone? - Vector trans; // Translation - float scale; // Probably scale (always 1) + Ogre::Matrix3 rotation; // Rotation offset from bone? + Ogre::Vector3 trans; // Translation + float scale; // Probably scale (always 1) }; struct BoneTrafoCopy { @@ -384,12 +377,12 @@ public: short vertex; float weight; }; -#pragma pack(pop) + struct BoneInfo { BoneTrafo trafo; - Vector4 unknown; + Ogre::Vector4 unknown; std::vector weights; }; struct BoneInfoCopy @@ -397,7 +390,7 @@ public: std::string bonename; unsigned short bonehandle; BoneTrafoCopy trafo; - Vector4 unknown; + Ogre::Vector4 unknown; //std::vector weights; }; struct IndividualWeight @@ -411,9 +404,6 @@ public: void read(NIFFile *nif) { - assert(sizeof(BoneTrafo) == 4*(9+3+1)); - assert(sizeof(VertWeight) == 6); - trafo.rotation = nif->getMatrix(); trafo.trans = nif->getVector(); trafo.scale = nif->getFloat(); @@ -432,8 +422,12 @@ public: bi.unknown = nif->getVector4(); // Number of vertex weights - int count = nif->getShort(); - bi.weights = nif->getArrayLen(count); + bi.weights.resize(nif->getShort()); + for(size_t j = 0;j < bi.weights.size();j++) + { + nif->load(bi.weights[j].vertex); + nif->load(bi.weights[j].weight); + } } } }; diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index f48049ec4..30877b48c 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -38,9 +38,9 @@ struct NiLight : Effect struct SLight { float dimmer; - Vector ambient; - Vector diffuse; - Vector specular; + Ogre::Vector3 ambient; + Ogre::Vector3 diffuse; + Ogre::Vector3 specular; void read(NIFFile *nif) { diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index c4b138232..d9fdd97ae 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -26,6 +26,9 @@ #include #include +#include +#include +#include #include #include @@ -162,44 +165,31 @@ public: template - std::vector getArrayLen(size_t num) - { - std::vector v(num); - if(inp->read(&v[0], num*sizeof(X)) != num*sizeof(X)) - fail("Failed to read from NIF"); - return v; - } - - template - std::vector getArray() - { - size_t len = read_le32(); - return getArrayLen(len); - } + std::vector getArrayLen(size_t num); char getByte() { char c; return load(c); } unsigned short getShort() { unsigned short s; return load(s); } int getInt() { int i; return load(i); } float getFloat() { float f; return load(f); } - Vector getVector() + Ogre::Vector3 getVector() { - Vector v; - load(v.array); - return v; + float a[3]; + load(a); + return Ogre::Vector3(a); } - Vector4 getVector4() + Ogre::Vector4 getVector4() { - Vector4 v; - load(v.array); - return v; + float a[4]; + load(a); + return Ogre::Vector4(a); } - Matrix getMatrix() + Ogre::Matrix3 getMatrix() { - Matrix m; - m.v[0] = getVector(); - m.v[1] = getVector(); - m.v[2] = getVector(); - return m; + float a[3*3]; + load(a); + return Ogre::Matrix3(Ogre::Real(a[0]), Ogre::Real(a[1]), Ogre::Real(a[2]), + Ogre::Real(a[3]), Ogre::Real(a[4]), Ogre::Real(a[5]), + Ogre::Real(a[6]), Ogre::Real(a[7]), Ogre::Real(a[8])); } Transformation getTrafo() { @@ -228,6 +218,15 @@ public: } }; +template<> +inline std::vector NIFFile::getArrayLen(size_t num) +{ + std::vector v(num); + for(size_t i = 0;i < num;i++) + load(v[i]); + return v; +} + template<> inline std::vector NIFFile::getArrayLen(size_t num) { diff --git a/components/nif/nif_types.hpp b/components/nif/nif_types.hpp index 41900a14e..705ed5994 100644 --- a/components/nif/nif_types.hpp +++ b/components/nif/nif_types.hpp @@ -24,41 +24,20 @@ #ifndef _NIF_TYPES_H_ #define _NIF_TYPES_H_ +#include +#include + // Common types used in NIF files namespace Nif { -/* These packing #pragmas aren't really necessary on 32 bit - machines. I haven't tested on 64 bit yet. In any case it doesn't - hurt to include them. We can't allow any compiler-generated padding - in any of these structs, since they are used to interface directly - with raw data from the NIF files. -*/ -#pragma pack(push) -#pragma pack(1) - -struct Vector -{ - float array[3]; -}; - -struct Vector4 -{ - float array[4]; -}; - -struct Matrix -{ - Vector v[3]; -}; - struct Transformation { - Vector pos; - Matrix rotation; + Ogre::Vector3 pos; + Ogre::Matrix3 rotation; float scale; - Vector velocity; + Ogre::Vector3 velocity; static const Transformation& getIdentity() { @@ -67,16 +46,15 @@ struct Transformation if (!iset) { identity.scale = 1.0f; - identity.rotation.v[0].array[0] = 1.0f; - identity.rotation.v[1].array[1] = 1.0f; - identity.rotation.v[2].array[2] = 1.0f; + identity.rotation[0][0] = 1.0f; + identity.rotation[1][1] = 1.0f; + identity.rotation[2][2] = 1.0f; iset = true; } return identity; } }; -#pragma pack(pop) } // Namespace #endif diff --git a/components/nif/node.hpp b/components/nif/node.hpp index d5cd8fe82..6ba3ce61d 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -47,9 +47,9 @@ public: // Bounding box info bool hasBounds; - Vector boundPos; - Matrix boundRot; - Vector boundXYZ; // Box size + Ogre::Vector3 boundPos; + Ogre::Matrix3 boundRot; + Ogre::Vector3 boundXYZ; // Box size void read(NIFFile *nif) { diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 87e3ae5f2..6ec277a62 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -167,7 +167,7 @@ struct StructPropT : Property struct S_MaterialProperty { // The vector components are R,G,B - Vector ambient, diffuse, specular, emissive; + Ogre::Vector3 ambient, diffuse, specular, emissive; float glossiness, alpha; void read(NIFFile *nif) diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index ae9ac94c2..41bc3b0a0 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -43,10 +43,6 @@ http://www.gnu.org/licenses/ . typedef unsigned char ubyte; -using namespace std; -using namespace Ogre; -using namespace Nif; - using namespace NifBullet; ManualBulletShapeLoader::~ManualBulletShapeLoader() @@ -55,18 +51,14 @@ ManualBulletShapeLoader::~ManualBulletShapeLoader() Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(Nif::Transformation* tr) { - Ogre::Matrix3 rot(tr->rotation.v[0].array[0],tr->rotation.v[0].array[1],tr->rotation.v[0].array[2], - tr->rotation.v[1].array[0],tr->rotation.v[1].array[1],tr->rotation.v[1].array[2], - tr->rotation.v[2].array[0],tr->rotation.v[2].array[1],tr->rotation.v[2].array[2]); - return rot; + return tr->rotation; } Ogre::Vector3 ManualBulletShapeLoader::getVector(Nif::Transformation* tr) { - Ogre::Vector3 vect3(tr->pos.array[0],tr->pos.array[1],tr->pos.array[2]); - return vect3; + return tr->pos; } -btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 m) +btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 &m) { Ogre::Quaternion oquat(m); btQuaternion quat; @@ -77,10 +69,9 @@ btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 m) return quat; } -btVector3 ManualBulletShapeLoader::getbtVector(Nif::Vector v) +btVector3 ManualBulletShapeLoader::getbtVector(Ogre::Vector3 &v) { - btVector3 a(v.array[0],v.array[1],v.array[2]); - return a; + return btVector3(v[0], v[1], v[2]); } void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) @@ -108,7 +99,6 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) assert(r != NULL); Nif::Node *node = dynamic_cast(r); - if (node == NULL) { warn("First record in file was not a node, but a " + diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index 5a33074c9..bf288e081 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -46,8 +46,6 @@ namespace Nif class Node; class Transformation; class NiTriShape; - class Vector; - class Matrix; } namespace NifBullet @@ -91,9 +89,9 @@ private: Ogre::Vector3 getVector(Nif::Transformation* tr); - btQuaternion getbtQuat(Ogre::Matrix3 m); + btQuaternion getbtQuat(Ogre::Matrix3 &m); - btVector3 getbtVector(Nif::Vector v); + btVector3 getbtVector(Ogre::Vector3 &v); /** *Parse a node. diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index ec54319c1..ad604c8d4 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -39,9 +39,7 @@ typedef unsigned char ubyte; using namespace std; -using namespace Ogre; using namespace Nif; -using namespace Mangle::VFS; using namespace Misc; using namespace NifOgre; @@ -67,21 +65,6 @@ void NIFLoader::fail(string msg) assert(1); } -Vector3 NIFLoader::convertVector3(const Nif::Vector& vec) -{ - return Ogre::Vector3(vec.array); -} - -Quaternion NIFLoader::convertRotation(const Nif::Matrix& rot) -{ - Real matrix[3][3]; - - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - matrix[i][j] = rot.v[i].array[j]; - - return Quaternion(Matrix3(matrix)); -} // Helper class that computes the bounding box and of a mesh class BoundsFinder @@ -217,16 +200,16 @@ void NIFLoader::setOutputAnimFiles(bool output){ void NIFLoader::setVerbosePath(std::string path){ verbosePath = path; } -void NIFLoader::createMaterial(const String &name, - const Vector &ambient, - const Vector &diffuse, - const Vector &specular, - const Vector &emissive, +void NIFLoader::createMaterial(const Ogre::String &name, + const Ogre::Vector3 &ambient, + const Ogre::Vector3 &diffuse, + const Ogre::Vector3 &specular, + const Ogre::Vector3 &emissive, float glossiness, float alpha, int alphaFlags, float alphaTest, - const String &texName) + const Ogre::String &texName) { - MaterialPtr material = MaterialManager::getSingleton().create(name, resourceGroup); + Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(name, resourceGroup); //Hardware Skinning code, textures may be the wrong color if enabled @@ -249,11 +232,11 @@ void NIFLoader::createMaterial(const String &name, if (!texName.empty()) { - Pass *pass = material->getTechnique(0)->getPass(0); + Ogre::Pass *pass = material->getTechnique(0)->getPass(0); /*TextureUnitState *txt =*/ pass->createTextureUnitState(texName); - pass->setVertexColourTracking(TVC_DIFFUSE); + pass->setVertexColourTracking(Ogre::TVC_DIFFUSE); // As of yet UNTESTED code from Chris: /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); @@ -294,13 +277,13 @@ void NIFLoader::createMaterial(const String &name, NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); if (result.first) { - pass->setAlphaRejectFunction(CMPF_GREATER_EQUAL); + pass->setAlphaRejectFunction(Ogre::CMPF_GREATER_EQUAL); pass->setAlphaRejectValue(result.second); } else { // Enable transparency - pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); + pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); //pass->setDepthCheckEnabled(false); pass->setDepthWriteEnabled(false); @@ -322,11 +305,11 @@ void NIFLoader::createMaterial(const String &name, const int numsplits = 3; for (int i = 0; i < (split ? numsplits : 1); ++i) { - TextureUnitState* tu = material->getTechnique(0)->getPass(0)->createTextureUnitState(); - tu->setName("shadowMap" + StringConverter::toString(i)); - tu->setContentType(TextureUnitState::CONTENT_SHADOW); - tu->setTextureAddressingMode(TextureUnitState::TAM_BORDER); - tu->setTextureBorderColour(ColourValue::White); + Ogre::TextureUnitState* tu = material->getTechnique(0)->getPass(0)->createTextureUnitState(); + tu->setName("shadowMap" + Ogre::StringConverter::toString(i)); + tu->setContentType(Ogre::TextureUnitState::CONTENT_SHADOW); + tu->setTextureAddressingMode(Ogre::TextureUnitState::TAM_BORDER); + tu->setTextureBorderColour(Ogre::ColourValue::White); } } @@ -339,11 +322,11 @@ void NIFLoader::createMaterial(const String &name, } // Create a fallback technique without shadows and without mrt - Technique* tech2 = material->createTechnique(); + Ogre::Technique* tech2 = material->createTechnique(); tech2->setSchemeName("Fallback"); - Pass* pass2 = tech2->createPass(); + Ogre::Pass* pass2 = tech2->createPass(); pass2->createTextureUnitState(texName); - pass2->setVertexColourTracking(TVC_DIFFUSE); + pass2->setVertexColourTracking(Ogre::TVC_DIFFUSE); if (Settings::Manager::getBool("shaders", "Objects")) { pass2->setVertexProgram("main_fallback_vp"); @@ -352,16 +335,16 @@ void NIFLoader::createMaterial(const String &name, } // Add material bells and whistles - material->setAmbient(ambient.array[0], ambient.array[1], ambient.array[2]); - material->setDiffuse(diffuse.array[0], diffuse.array[1], diffuse.array[2], alpha); - material->setSpecular(specular.array[0], specular.array[1], specular.array[2], alpha); - material->setSelfIllumination(emissive.array[0], emissive.array[1], emissive.array[2]); + material->setAmbient(ambient[0], ambient[1], ambient[2]); + material->setDiffuse(diffuse[0], diffuse[1], diffuse[2], alpha); + material->setSpecular(specular[0], specular[1], specular[2], alpha); + material->setSelfIllumination(emissive[0], emissive[1], emissive[2]); material->setShininess(glossiness); } // Takes a name and adds a unique part to it. This is just used to // make sure that all materials are given unique names. -String NIFLoader::getUniqueName(const String &input) +Ogre::String NIFLoader::getUniqueName(const Ogre::String &input) { static int addon = 0; static char buf[8]; @@ -377,13 +360,13 @@ String NIFLoader::getUniqueName(const String &input) // does not, change the string IN PLACE to say .dds instead and try // that. The texture may still not exist, but no information of value // is lost in that case. -void NIFLoader::findRealTexture(String &texName) +void NIFLoader::findRealTexture(Ogre::String &texName) { if(Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) return; // Change texture extension to .dds - String::size_type pos = texName.rfind('.'); + Ogre::String::size_type pos = texName.rfind('.'); texName.replace(pos, texName.length(), ".dds"); } @@ -391,11 +374,11 @@ void NIFLoader::findRealTexture(String &texName) // Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given // mesh. -void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std::list &vertexBoneAssignments) +void NIFLoader::createOgreSubMesh(NiTriShape *shape, const Ogre::String &material, std::list &vertexBoneAssignments) { // cout << "s:" << shape << "\n"; NiTriShapeData *data = shape->data.getPtr(); - SubMesh *sub = mesh->createSubMesh(shape->name); + Ogre::SubMesh *sub = mesh->createSubMesh(shape->name); int nextBuf = 0; @@ -404,17 +387,17 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std // Add vertices int numVerts = data->vertices.size() / 3; - sub->vertexData = new VertexData(); + sub->vertexData = new Ogre::VertexData(); sub->vertexData->vertexCount = numVerts; sub->useSharedVertices = false; - VertexDeclaration *decl = sub->vertexData->vertexDeclaration; - decl->addElement(nextBuf, 0, VET_FLOAT3, VES_POSITION); + Ogre::VertexDeclaration *decl = sub->vertexData->vertexDeclaration; + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - HardwareVertexBufferSharedPtr vbuf = - HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_FLOAT3), - numVerts, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, false); + Ogre::HardwareVertexBufferSharedPtr vbuf = + Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + numVerts, Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, false); if(flip) { @@ -440,19 +423,19 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std } - VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding; + Ogre::VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding; bind->setBinding(nextBuf++, vbuf); if (data->normals.size()) { - decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL); - vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_FLOAT3), - numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); + vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); if(flip) { - Quaternion rotation = mTransform.extractQuaternion(); + Ogre::Quaternion rotation = mTransform.extractQuaternion(); rotation.normalise(); float *datamod = new float[data->normals.size()]; @@ -487,19 +470,19 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std if (data->colors.size()) { const float *colors = &data->colors[0]; - RenderSystem* rs = Root::getSingleton().getRenderSystem(); - std::vector colorsRGB(numVerts); - RGBA *pColour = &colorsRGB.front(); + Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); + std::vector colorsRGB(numVerts); + Ogre::RGBA *pColour = &colorsRGB.front(); for (int i=0; iconvertColourValue(ColourValue(colors[0],colors[1],colors[2], - colors[3]),pColour++); + rs->convertColourValue(Ogre::ColourValue(colors[0],colors[1],colors[2], + colors[3]),pColour++); colors += 4; } - decl->addElement(nextBuf, 0, VET_COLOUR, VES_DIFFUSE); - vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_COLOUR), - numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); + decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); + vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB.front(), true); bind->setBinding(nextBuf++, vbuf); } @@ -507,10 +490,10 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std if (data->uvlist.size()) { - decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES); - vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( - VertexElement::getTypeSize(VET_FLOAT2), - numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY,false); + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES); + vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), + numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,false); if(flip) { @@ -539,24 +522,23 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std sub->indexData->indexCount = numFaces; sub->indexData->indexStart = 0; - HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton(). - createIndexBuffer(HardwareIndexBuffer::IT_16BIT, - numFaces, - HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); + Ogre::HardwareIndexBufferSharedPtr ibuf = Ogre::HardwareBufferManager::getSingleton(). + createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, numFaces, + Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); if(flip && mFlipVertexWinding && sub->indexData->indexCount % 3 == 0){ sub->indexData->indexBuffer = ibuf; - uint16 *datamod = new uint16[numFaces]; + uint16_t *datamod = new uint16_t[numFaces]; int index = 0; for (size_t i = 0; i < sub->indexData->indexCount; i+=3) { const short *pos = &data->triangles[index]; - uint16 i0 = (uint16) *(pos+0); - uint16 i1 = (uint16) *(pos+1); - uint16 i2 = (uint16) *(pos+2); + uint16_t i0 = (uint16_t) *(pos+0); + uint16_t i1 = (uint16_t) *(pos+1); + uint16_t i2 = (uint16_t) *(pos+2); //std::cout << "i0: " << i0 << "i1: " << i1 << "i2: " << i2 << "\n"; @@ -582,7 +564,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std //add vertex bone assignments - for (std::list::iterator it = vertexBoneAssignments.begin(); + for (std::list::iterator it = vertexBoneAssignments.begin(); it != vertexBoneAssignments.end(); it++) { sub->addBoneAssignment(*it); @@ -593,23 +575,8 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std // Helper math functions. Reinventing linear algebra for the win! -// Computes B = AxB (matrix*matrix) -static void matrixMul(const Matrix &A, Matrix &B) -{ - for (int i=0;i<3;i++) - { - float a = B.v[0].array[i]; - float b = B.v[1].array[i]; - float c = B.v[2].array[i]; - - B.v[0].array[i] = a*A.v[0].array[0] + b*A.v[0].array[1] + c*A.v[0].array[2]; - B.v[1].array[i] = a*A.v[1].array[0] + b*A.v[1].array[1] + c*A.v[1].array[2]; - B.v[2].array[i] = a*A.v[2].array[0] + b*A.v[2].array[1] + c*A.v[2].array[2]; - } -} - // Computes C = B + AxC*scale -static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale) +static void vectorMulAdd(const Ogre::Matrix3 &A, const Ogre::Vector3 &B, float *C, float scale) { // Keep the original values float a = C[0]; @@ -618,11 +585,11 @@ static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale // Perform matrix multiplication, scaling and addition for (int i=0;i<3;i++) - C[i] = B.array[i] + (a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2])*scale; + C[i] = B[i] + (a*A[i][0] + b*A[i][1] + c*A[i][2])*scale; } // Computes B = AxB (matrix*vector) -static void vectorMul(const Matrix &A, float *C) +static void vectorMul(const Ogre::Matrix3 &A, float *C) { // Keep the original values float a = C[0]; @@ -631,7 +598,7 @@ static void vectorMul(const Matrix &A, float *C) // Perform matrix multiplication, scaling and addition for (int i=0;i<3;i++) - C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2]; + C[i] = a*A[i][0] + b*A[i][1] + c*A[i][2]; } @@ -666,7 +633,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou return; // Material name for this submesh, if any - String material; + Ogre::String material; // Skip the entire material phase for hidden nodes if (!hidden) @@ -695,7 +662,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou } // Texture - String texName; + Ogre::String texName; if (t && t->textures[0].inUse) { NiSourceTexture *st = t->textures[0].texture.getPtr(); @@ -768,14 +735,8 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou { // We only have a texture name. Create a default // material for it. - Vector zero, one; - for (int i=0; i<3;i++) - { - zero.array[i] = 0.0; - one.array[i] = 1.0; - } - - createMaterial(material, one, one, zero, zero, 0.0, 1.0, + const Ogre::Vector3 zero(0.0f), one(1.0f); + createMaterial(material, one, one, zero, zero, 0.0f, 1.0f, alphaFlags, alphaTest, texName); } } @@ -793,7 +754,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou float *ptr = (float*)&data->vertices[0]; float *optr = ptr; - std::list vertexBoneAssignments; + std::list vertexBoneAssignments; Nif::NiTriShapeCopy copy = shape->clone(); @@ -826,9 +787,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou //the first one contains a link to the bone, the second vertex transformation //relative to the bone int boneIndex = 0; - Bone *bonePtr; - Vector3 vecPos; - Quaternion vecRot; + Ogre::Bone *bonePtr; + Ogre::Vector3 vecPos; + Ogre::Quaternion vecRot; std::vector boneList = shape->skin->data->bones; @@ -854,17 +815,17 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou Nif::NiSkinData::BoneInfoCopy boneinfocopy; - boneinfocopy.trafo.rotation = convertRotation(it->trafo.rotation); - boneinfocopy.trafo.trans = convertVector3(it->trafo.trans); + boneinfocopy.trafo.rotation = it->trafo.rotation; + boneinfocopy.trafo.trans = it->trafo.trans; boneinfocopy.bonename = shape->skin->bones[boneIndex].name; boneinfocopy.bonehandle = bonePtr->getHandle(); copy.boneinfo.push_back(boneinfocopy); for (unsigned int i=0; iweights.size(); i++) { vecPos = bonePtr->_getDerivedPosition() + - bonePtr->_getDerivedOrientation() * convertVector3(it->trafo.trans); + bonePtr->_getDerivedOrientation() * it->trafo.trans; - vecRot = bonePtr->_getDerivedOrientation() * convertRotation(it->trafo.rotation); + vecRot = bonePtr->_getDerivedOrientation() * it->trafo.rotation; unsigned int verIndex = it->weights[i].vertex; //boneinfo.weights.push_back(*(it->weights.ptr + i)); Nif::NiSkinData::IndividualWeight ind; @@ -885,9 +846,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou if (vertexPosAbsolut[verIndex] == false) { //apply transformation to the vertices - Vector3 absVertPos = vecPos + vecRot * Vector3(ptr + verIndex *3); + Ogre::Vector3 absVertPos = vecPos + vecRot * Ogre::Vector3(ptr + verIndex *3); absVertPos = absVertPos * it->weights[i].weight; - vertexPosOriginal[verIndex] = Vector3(ptr + verIndex *3); + vertexPosOriginal[verIndex] = Ogre::Vector3(ptr + verIndex *3); mBoundingBox.merge(absVertPos); //convert it back to float * @@ -898,9 +859,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou //FIXME: I guessed that vertex[i] = normal[i], is that true? if (verIndex < data->normals.size()) { - Vector3 absNormalsPos = vecRot * Vector3(ptrNormals + verIndex *3); + Ogre::Vector3 absNormalsPos = vecRot * Ogre::Vector3(ptrNormals + verIndex *3); absNormalsPos = absNormalsPos * it->weights[i].weight; - vertexNormalOriginal[verIndex] = Vector3(ptrNormals + verIndex *3); + vertexNormalOriginal[verIndex] = Ogre::Vector3(ptrNormals + verIndex *3); for (int j=0; j<3; j++) (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; @@ -910,9 +871,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou } else { - Vector3 absVertPos = vecPos + vecRot * vertexPosOriginal[verIndex]; + Ogre::Vector3 absVertPos = vecPos + vecRot * vertexPosOriginal[verIndex]; absVertPos = absVertPos * it->weights[i].weight; - Vector3 old = Vector3(ptr + verIndex *3); + Ogre::Vector3 old = Ogre::Vector3(ptr + verIndex *3); absVertPos = absVertPos + old; mBoundingBox.merge(absVertPos); @@ -924,9 +885,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou //FIXME: I guessed that vertex[i] = normal[i], is that true? if (verIndex < data->normals.size()) { - Vector3 absNormalsPos = vecRot * vertexNormalOriginal[verIndex]; + Ogre::Vector3 absNormalsPos = vecRot * vertexNormalOriginal[verIndex]; absNormalsPos = absNormalsPos * it->weights[i].weight; - Vector3 oldNormal = Vector3(ptrNormals + verIndex *3); + Ogre::Vector3 oldNormal = Ogre::Vector3(ptrNormals + verIndex *3); absNormalsPos = absNormalsPos + oldNormal; for (int j=0; j<3; j++) @@ -935,7 +896,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou } - VertexBoneAssignment vba; + Ogre::VertexBoneAssignment vba; vba.boneIndex = bonePtr->getHandle(); vba.vertexIndex = verIndex; vba.weight = it->weights[i].weight; @@ -955,12 +916,12 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou copy.boneSequence = boneSequence; // Rotate, scale and translate all the vertices, - const Matrix &rot = shape->trafo.rotation; - const Vector &pos = shape->trafo.pos; + const Ogre::Matrix3 &rot = shape->trafo.rotation; + const Ogre::Vector3 &pos = shape->trafo.pos; float scale = shape->trafo.scale; - copy.trafo.trans = convertVector3(original.pos); - copy.trafo.rotation = convertRotation(original.rotation); + copy.trafo.trans = original.pos; + copy.trafo.rotation = original.rotation; copy.trafo.scale = original.scale; //We don't use velocity for anything yet, so it does not need to be saved @@ -988,7 +949,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou boneIndex = mSkel->getNumBones() - 1; for(int i = 0; i < numVerts; i++){ - VertexBoneAssignment vba; + Ogre::VertexBoneAssignment vba; vba.boneIndex = boneIndex; vba.vertexIndex = i; vba.weight = 1; @@ -1012,15 +973,15 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou void NIFLoader::calculateTransform() { // Calculate transform - Matrix4 transform = Matrix4::IDENTITY; - transform = Matrix4::getScale(vector) * transform; + Ogre::Matrix4 transform = Ogre::Matrix4::IDENTITY; + transform = Ogre::Matrix4::getScale(vector) * transform; // Check whether we have to flip vertex winding. // We do have to, if we changed our right hand base. // We can test it by using the cross product from X and Y and see, if it is a non-negative // projection on Z. Actually it should be exactly Z, as we don't do non-uniform scaling yet, // but the test is cheap either way. - Matrix3 m3; + Ogre::Matrix3 m3; transform.extract3x3Matrix(m3); if (m3.GetColumn(0).crossProduct(m3.GetColumn(1)).dotProduct(m3.GetColumn(2)) < 0) @@ -1114,7 +1075,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, } } - Bone *bone = 0; + Ogre::Bone *bone = 0; // create skeleton or add bones if (node->recType == RC_NiNode) @@ -1124,7 +1085,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, { inTheSkeletonTree = true; - mSkel = SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true); + mSkel = Ogre::SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true); } else if (!mSkel.isNull() && !parentBone) inTheSkeletonTree = false; @@ -1144,8 +1105,8 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, parentBone->addChild(bone); bone->setInheritOrientation(true); - bone->setPosition(convertVector3(node->trafo.pos)); - bone->setOrientation(convertRotation(node->trafo.rotation)); + bone->setPosition(node->trafo.pos); + bone->setOrientation(node->trafo.rotation); } } } @@ -1160,14 +1121,13 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, // For both position and rotation we have that: // final_vector = old_vector + old_rotation*new_vector*old_scale - vectorMulAdd(trafo->rotation, trafo->pos, final.pos.array, trafo->scale); - vectorMulAdd(trafo->rotation, trafo->velocity, final.velocity.array, trafo->scale); + final.pos = trafo->pos + trafo->rotation*final.pos*trafo->scale; + final.velocity = trafo->velocity + trafo->rotation*final.velocity*trafo->scale; // Merge the rotations together - matrixMul(trafo->rotation, final.rotation); + final.rotation = trafo->rotation * final.rotation; - // Scalar values are so nice to deal with. Why can't everything - // just be scalar? + // Scale final.scale *= trafo->scale; } @@ -1200,7 +1160,7 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, } } -void NIFLoader::loadResource(Resource *resource) +void NIFLoader::loadResource(Ogre::Resource *resource) { inTheSkeletonTree = false; allanim.clear(); @@ -1287,7 +1247,7 @@ void NIFLoader::loadResource(Resource *resource) calculateTransform(); } // Get the mesh - mesh = dynamic_cast(resource); + mesh = dynamic_cast(resource); assert(mesh); // Look it up @@ -1352,8 +1312,8 @@ void NIFLoader::loadResource(Resource *resource) // set the bounding value. if (bounds.isValid()) { - mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ())); + mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), + bounds.maxX(), bounds.maxY(), bounds.maxZ())); mesh->_setBoundingSphereRadius(bounds.getRadius()); } if(hasAnim && addAnim){ @@ -1375,7 +1335,7 @@ void NIFLoader::loadResource(Resource *resource) for(std::vector::iterator iter = needBoneAssignments.begin(); iter != needBoneAssignments.end(); iter++) { int boneIndex = mSkel->getNumBones() - 1; - VertexBoneAssignment vba; + Ogre::VertexBoneAssignment vba; vba.boneIndex = boneIndex; vba.vertexIndex = 0; vba.weight = 1; @@ -1394,20 +1354,19 @@ void NIFLoader::loadResource(Resource *resource) -MeshPtr NIFLoader::load(const std::string &name, - const std::string &group) +Ogre::MeshPtr NIFLoader::load(const std::string &name, const std::string &group) { - MeshManager *m = MeshManager::getSingletonPtr(); + Ogre::MeshManager *m = Ogre::MeshManager::getSingletonPtr(); // Check if the resource already exists - ResourcePtr ptr = m->getByName(name, group); - MeshPtr themesh; + Ogre::ResourcePtr ptr = m->getByName(name, group); + Ogre::MeshPtr themesh; if (!ptr.isNull()){ - themesh = MeshPtr(ptr); + themesh = Ogre::MeshPtr(ptr); } else // Nope, create a new one. { - themesh = MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr()); + themesh = Ogre::MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr()); } return themesh; } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 55915b310..64efc70c7 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -62,17 +62,8 @@ namespace Nif class Node; class Transformation; class NiTriShape; - class Vector; - class Matrix; } -namespace Mangle -{ - namespace VFS - { - class OgreVFS; - } -} namespace NifOgre { @@ -110,9 +101,6 @@ class NIFLoader : Ogre::ManualResourceLoader std::map* getTextIndices(std::string name); - Ogre::Vector3 convertVector3(const Nif::Vector& vec); - Ogre::Quaternion convertRotation(const Nif::Matrix& rot); - void setOutputAnimFiles(bool output); void setVerbosePath(std::string path); @@ -136,10 +124,10 @@ class NIFLoader : Ogre::ManualResourceLoader void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material, std::list &vertexBoneAssignments); void createMaterial(const Ogre::String &name, - const Nif::Vector &ambient, - const Nif::Vector &diffuse, - const Nif::Vector &specular, - const Nif::Vector &emissive, + const Ogre::Vector3 &ambient, + const Ogre::Vector3 &diffuse, + const Ogre::Vector3 &specular, + const Ogre::Vector3 &emissive, float glossiness, float alpha, int alphaFlags, float alphaTest, const Ogre::String &texName); From 70c74ede055b2bbfcb727dcc9c8e92f082cd9554 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 10 Jul 2012 11:53:12 +0200 Subject: [PATCH 086/688] changed rotation order --- 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 1e1fae154..24baac144 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -618,7 +618,7 @@ namespace MWWorld Ogre::Quaternion rotx(Ogre::Degree(x),Ogre::Vector3::UNIT_X); Ogre::Quaternion roty(Ogre::Degree(y),Ogre::Vector3::UNIT_Y); Ogre::Quaternion rotz(Ogre::Degree(z),Ogre::Vector3::UNIT_Z); - ptr.getRefData().getBaseNode()->setOrientation(rotz*rotx*roty); + ptr.getRefData().getBaseNode()->setOrientation(rotz*roty*rotx); mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); } From 95b804a104ef4c187a53f3b8228932278ef4f752 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 10 Jul 2012 03:02:37 -0700 Subject: [PATCH 087/688] Remove NIFFile::getArrayLen --- components/nif/data.hpp | 17 ++++++++--------- components/nif/extra.hpp | 11 ++++++++--- components/nif/nif_file.cpp | 2 +- components/nif/nif_file.hpp | 34 ++-------------------------------- 4 files changed, 19 insertions(+), 45 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 667e77ffd..babc07545 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -105,25 +105,24 @@ public: int verts = nif->getShort(); if(nif->getInt()) - vertices = nif->getArrayLen(verts*3); + nif->load(vertices, verts*3); if(nif->getInt()) - normals = nif->getArrayLen(verts*3); + nif->load(normals, verts*3); center = nif->getVector(); radius = nif->getFloat(); if(nif->getInt()) - colors = nif->getArrayLen(verts*4); - - int uvs = nif->getShort(); + nif->load(colors, verts*4); // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. + int uvs = nif->getShort(); uvs &= 0x3f; if(nif->getInt()) - uvlist = nif->getArrayLen(uvs*verts*2); + nif->load(uvlist, uvs*verts*2); } }; @@ -143,7 +142,7 @@ public: // We have three times as many vertices as triangles, so this // is always equal to tris*3. int cnt = nif->getInt(); - triangles = nif->getArrayLen(cnt); + nif->load(triangles, cnt); } // Read the match list, which lists the vertices that are equal to @@ -175,13 +174,13 @@ public: activeCount = nif->getShort(); // Skip all the info, we don't support particles yet - nif->getFloat(); // Active radius ? + nif->getFloat(); // Active radius ? nif->getShort(); // Number of valid entries in the following arrays ? if(nif->getInt()) { // Particle sizes - nif->getArrayLen(activeCount); + nif->skip(activeCount * sizeof(float)); } } }; diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index 5615d833e..7659bb3d2 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -47,16 +47,21 @@ public: class NiVertWeightsExtraData : public Extra { public: + std::vector weights; + void read(NIFFile *nif) { Extra::read(nif); + int i; + unsigned short s; + // We should have s*4+2 == i, for some reason. Might simply be the // size of the rest of the record, unhelpful as that may be. - /*int i =*/ nif->getInt(); - int s = nif->getShort(); // number of vertices + nif->load(i); - nif->getArrayLen(s); // vertex weights I guess + nif->load(s); // number of vertices + nif->load(weights, s); // vertex weights I guess } }; diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 36badbf0d..5b88b45fe 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -46,7 +46,7 @@ using namespace Misc; void NIFFile::parse() { // Check the header string - std::string head = getString(40); + std::string head = read_string(40); if(head.compare(0, 22, "NetImmerse File Format") != 0) fail("Invalid NIF header"); diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index d9fdd97ae..6165f5811 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -164,9 +164,6 @@ public: } - template - std::vector getArrayLen(size_t num); - char getByte() { char c; return load(c); } unsigned short getShort() { unsigned short s; return load(s); } int getInt() { int i; return load(i); } @@ -202,39 +199,12 @@ public: } - // For fixed-size strings where you already know the size - std::string getString(size_t size) - { - std::string str; - str.resize(size); - if(inp->read(&str[0], size) != size) - fail("Failed to read from NIF"); - return str.substr(0, str.find('\0')); - } std::string getString() { - size_t size = getInt(); - return getString(size); + size_t size = read_le32(); + return read_string(size); } }; -template<> -inline std::vector NIFFile::getArrayLen(size_t num) -{ - std::vector v(num); - for(size_t i = 0;i < num;i++) - load(v[i]); - return v; -} - -template<> -inline std::vector NIFFile::getArrayLen(size_t num) -{ - std::vector v(num); - for(size_t i = 0;i < num;i++) - load(v[i]); - return v; -} - } // Namespace #endif From 164a5c8fe4d6ab0abda8116f872f90eaac6ce960 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 10 Jul 2012 12:10:50 +0200 Subject: [PATCH 088/688] rotation now also work with the physic representation --- apps/openmw/mwworld/physicssystem.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 9848efe6e..7d7b237ae 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -291,12 +291,14 @@ namespace MWWorld void PhysicsSystem::rotateObject (const std::string& handle, const Ogre::Quaternion& rotation) { - if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow - // start positions others than 0, 0, 0 act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); } + if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) + { + body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); + } } void PhysicsSystem::scaleObject (const std::string& handle, float scale) From 930459365b1ca8842acd1d36299bf8820e43242f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 10 Jul 2012 03:52:01 -0700 Subject: [PATCH 089/688] Rename getShort->getUShort and getByte->getChar --- components/nif/controller.hpp | 8 +++---- components/nif/data.hpp | 39 ++++++++++++++++------------------- components/nif/nif_file.hpp | 6 +++--- components/nif/node.hpp | 2 +- components/nif/property.hpp | 12 +++++------ 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index d00c1bc0e..cbc19cd8f 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -44,7 +44,7 @@ public: { next.read(nif); - flags = nif->getShort(); + flags = nif->getUShort(); frequency = nif->getFloat(); phase = nif->getFloat(); @@ -71,7 +71,7 @@ public: // At the moment, just skip it all nif->skip(111); - int s = nif->getShort(); + int s = nif->getUShort(); nif->skip(15 + s*40); } }; @@ -133,7 +133,7 @@ public: { Controller::read(nif); - nif->getShort(); // always 0 + nif->getUShort(); // always 0 data.read(nif); } @@ -189,7 +189,7 @@ public: { Controller::read(nif); data.read(nif); - nif->getByte(); // always 0 + nif->getChar(); // always 0 } void post(NIFFile *nif) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index babc07545..e77fd6239 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -69,12 +69,12 @@ public: { Named::read(nif); - external = !!nif->getByte(); + external = !!nif->getChar(); if(external) filename = nif->getString(); else { - nif->getByte(); // always 1 + nif->getChar(); // always 1 data.read(nif); } @@ -82,7 +82,7 @@ public: mipmap = nif->getInt(); alpha = nif->getInt(); - nif->getByte(); // always 1 + nif->getChar(); // always 1 } void post(NIFFile *nif) @@ -102,7 +102,7 @@ public: void read(NIFFile *nif) { - int verts = nif->getShort(); + int verts = nif->getUShort(); if(nif->getInt()) nif->load(vertices, verts*3); @@ -118,7 +118,7 @@ public: // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. - int uvs = nif->getShort(); + int uvs = nif->getUShort(); uvs &= 0x3f; if(nif->getInt()) @@ -136,7 +136,7 @@ public: { ShapeData::read(nif); - int tris = nif->getShort(); + int tris = nif->getUShort(); if(tris) { // We have three times as many vertices as triangles, so this @@ -148,15 +148,12 @@ public: // Read the match list, which lists the vertices that are equal to // vertices. We don't actually need need this for anything, so // just skip it. - int verts = nif->getShort(); - if(verts) + int verts = nif->getUShort(); + for(int i=0;igetShort(); - nif->skip(num*sizeof(short)); - } + // Number of vertices matching vertex 'i' + int num = nif->getUShort(); + nif->skip(num*sizeof(short)); } } }; @@ -171,11 +168,11 @@ public: ShapeData::read(nif); // Should always match the number of vertices - activeCount = nif->getShort(); + activeCount = nif->getUShort(); // Skip all the info, we don't support particles yet - nif->getFloat(); // Active radius ? - nif->getShort(); // Number of valid entries in the following arrays ? + nif->getFloat(); // Active radius ? + nif->getUShort(); // Number of valid entries in the following arrays ? if(nif->getInt()) { @@ -421,11 +418,11 @@ public: bi.unknown = nif->getVector4(); // Number of vertex weights - bi.weights.resize(nif->getShort()); + bi.weights.resize(nif->getUShort()); for(size_t j = 0;j < bi.weights.size();j++) { - nif->load(bi.weights[j].vertex); - nif->load(bi.weights[j].weight); + bi.weights[j].vertex = nif->getUShort(); + bi.weights[j].weight = nif->getFloat(); } } } @@ -464,7 +461,7 @@ public: { int morphCount = nif->getInt(); int vertCount = nif->getInt(); - nif->getByte(); + nif->getChar(); int magic = nif->getInt(); /*int type =*/ nif->getInt(); diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 6165f5811..2b46f84f3 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -164,8 +164,8 @@ public: } - char getByte() { char c; return load(c); } - unsigned short getShort() { unsigned short s; return load(s); } + char getChar() { char c; return load(c); } + unsigned short getUShort() { unsigned short s; return load(s); } int getInt() { int i; return load(i); } float getFloat() { float f; return load(f); } Ogre::Vector3 getVector() @@ -193,7 +193,7 @@ public: Transformation t; t.pos = getVector(); t.rotation = getMatrix(); - load(t.scale); + t.scale = getFloat(); t.velocity = getVector(); return t; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 6ba3ce61d..f86ea5af9 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -55,7 +55,7 @@ public: { Named::read(nif); - flags = nif->getShort(); + flags = nif->getUShort(); trafo = nif->getTrafo(); props.read(nif); diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 6ec277a62..1b455b14f 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -38,7 +38,7 @@ public: void read(NIFFile *nif) { Named::read(nif); - flags = nif->getShort(); + flags = nif->getUShort(); } }; @@ -176,8 +176,8 @@ struct S_MaterialProperty diffuse = nif->getVector(); specular = nif->getVector(); emissive = nif->getVector(); - nif->load(glossiness); - nif->load(alpha); + glossiness = nif->getFloat(); + alpha = nif->getFloat(); } }; @@ -196,8 +196,8 @@ struct S_VertexColorProperty void read(NIFFile *nif) { - nif->load(vertmode); - nif->load(lightmode); + vertmode = nif->getInt(); + lightmode = nif->getInt(); } }; @@ -253,7 +253,7 @@ struct S_AlphaProperty void read(NIFFile *nif) { - nif->load(threshold); + threshold = nif->getChar(); } }; From d30f64650a323f84d7ea554b7e0d3e4728cc1b47 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 10 Jul 2012 04:21:47 -0700 Subject: [PATCH 090/688] Make the read_* methods private and remove the generic load() methods --- components/nif/data.hpp | 12 ++-- components/nif/effect.hpp | 2 +- components/nif/extra.hpp | 11 +-- components/nif/nif_file.cpp | 2 +- components/nif/nif_file.hpp | 140 +++++++++++++++++------------------- components/nif/node.hpp | 22 +++--- 6 files changed, 87 insertions(+), 102 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index e77fd6239..118e21e54 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -105,16 +105,16 @@ public: int verts = nif->getUShort(); if(nif->getInt()) - nif->load(vertices, verts*3); + nif->getFloats(vertices, verts*3); if(nif->getInt()) - nif->load(normals, verts*3); + nif->getFloats(normals, verts*3); center = nif->getVector(); radius = nif->getFloat(); if(nif->getInt()) - nif->load(colors, verts*4); + nif->getFloats(colors, verts*4); // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. @@ -122,7 +122,7 @@ public: uvs &= 0x3f; if(nif->getInt()) - nif->load(uvlist, uvs*verts*2); + nif->getFloats(uvlist, uvs*verts*2); } }; @@ -142,7 +142,7 @@ public: // We have three times as many vertices as triangles, so this // is always equal to tris*3. int cnt = nif->getInt(); - nif->load(triangles, cnt); + nif->getShorts(triangles, cnt); } // Read the match list, which lists the vertices that are equal to @@ -153,7 +153,7 @@ public: { // Number of vertices matching vertex 'i' int num = nif->getUShort(); - nif->skip(num*sizeof(short)); + nif->skip(num * sizeof(short)); } } }; diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index 30877b48c..1a2ecace8 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -44,7 +44,7 @@ struct NiLight : Effect void read(NIFFile *nif) { - nif->load(dimmer); + dimmer = nif->getFloat(); ambient = nif->getVector(); diffuse = nif->getVector(); specular = nif->getVector(); diff --git a/components/nif/extra.hpp b/components/nif/extra.hpp index 7659bb3d2..35781dbf5 100644 --- a/components/nif/extra.hpp +++ b/components/nif/extra.hpp @@ -47,21 +47,16 @@ public: class NiVertWeightsExtraData : public Extra { public: - std::vector weights; - void read(NIFFile *nif) { Extra::read(nif); - int i; - unsigned short s; - // We should have s*4+2 == i, for some reason. Might simply be the // size of the rest of the record, unhelpful as that may be. - nif->load(i); + /*int i =*/ nif->getInt(); + int s = nif->getUShort(); - nif->load(s); // number of vertices - nif->load(weights, s); // vertex weights I guess + nif->skip(s * sizeof(float)); // vertex weights I guess } }; diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 5b88b45fe..36badbf0d 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -46,7 +46,7 @@ using namespace Misc; void NIFFile::parse() { // Check the header string - std::string head = read_string(40); + std::string head = getString(40); if(head.compare(0, 22, "NetImmerse File Format") != 0) fail("Invalid NIF header"); diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 2b46f84f3..0218795e0 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -62,6 +62,33 @@ class NIFFile /// Parse the file void parse(); + uint8_t read_byte() + { + uint8_t byte; + if(inp->read(&byte, 1) != 1) return 0; + return byte; + } + uint16_t read_le16() + { + uint8_t buffer[2]; + if(inp->read(buffer, 2) != 2) return 0; + return buffer[0] | (buffer[1]<<8); + } + uint32_t read_le32() + { + uint8_t buffer[4]; + if(inp->read(buffer, 4) != 4) return 0; + return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); + } + float read_le32f() + { + union { + int i; + float f; + } u = { read_le32() }; + return u.f; + } + public: /// Used for error handling void fail(const std::string &msg) @@ -102,91 +129,34 @@ public: void skip(size_t size) { inp->skip(size); } - uint32_t read_le32() - { - uint8_t buffer[4]; - if(inp->read(buffer, 4) != 4) return 0; - return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); - } - uint16_t read_le16() - { - uint8_t buffer[2]; - if(inp->read(buffer, 2) != 2) return 0; - return buffer[0] | (buffer[1]<<8); - } - uint8_t read_byte() - { - uint8_t byte; - if(inp->read(&byte, 1) != 1) return 0; - return byte; - } - std::string read_string(size_t length) - { - std::string str; - str.resize(length); - if(inp->read(&str[0], length) != length) - return std::string(); - return str.substr(0, str.find('\0')); - } - - - char& load(char &c) { c = read_byte(); return c; } - unsigned char& load(unsigned char &c) { c = read_byte(); return c; } - short& load(short &s) { s = read_le16(); return s; } - unsigned short& load(unsigned short &s) { s = read_le16(); return s; } - int& load(int &i) { i = read_le32(); return i; } - unsigned int& load(unsigned int &i) { i = read_le32(); return i; } - float& load(float &f) - { - union { - int i; - float f; - } u = { read_le32() }; - f = u.f; - return f; - } - - template - T* load(T (&a)[N]) - { - for(size_t i = 0;i < N;i++) - load(a[i]); - return a; - } - - template - std::vector& load(std::vector &v, size_t size) - { - v.resize(size); - for(size_t i = 0;i < size;i++) - load(v[i]); - return v; - } - - - char getChar() { char c; return load(c); } - unsigned short getUShort() { unsigned short s; return load(s); } - int getInt() { int i; return load(i); } - float getFloat() { float f; return load(f); } + char getChar() { return read_byte(); } + short getShort() { return read_le16(); } + unsigned short getUShort() { return read_le16(); } + int getInt() { return read_le32(); } + float getFloat() { return read_le32f(); } Ogre::Vector3 getVector() { float a[3]; - load(a); + for(size_t i = 0;i < 3;i++) + a[i] = getFloat(); return Ogre::Vector3(a); } Ogre::Vector4 getVector4() { float a[4]; - load(a); + for(size_t i = 0;i < 4;i++) + a[i] = getFloat(); return Ogre::Vector4(a); } Ogre::Matrix3 getMatrix() { - float a[3*3]; - load(a); - return Ogre::Matrix3(Ogre::Real(a[0]), Ogre::Real(a[1]), Ogre::Real(a[2]), - Ogre::Real(a[3]), Ogre::Real(a[4]), Ogre::Real(a[5]), - Ogre::Real(a[6]), Ogre::Real(a[7]), Ogre::Real(a[8])); + Ogre::Real a[3][3]; + for(size_t i = 0;i < 3;i++) + { + for(size_t j = 0;j < 3;j++) + a[i][j] = Ogre::Real(getFloat()); + } + return Ogre::Matrix3(a); } Transformation getTrafo() { @@ -198,11 +168,31 @@ public: return t; } - + std::string getString(size_t length) + { + std::string str; + str.resize(length); + if(inp->read(&str[0], length) != length) + return std::string(); + return str.substr(0, str.find('\0')); + } std::string getString() { size_t size = read_le32(); - return read_string(size); + return getString(size); + } + + void getShorts(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getShort(); + } + void getFloats(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getFloat(); } }; diff --git a/components/nif/node.hpp b/components/nif/node.hpp index f86ea5af9..240dbe540 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -221,19 +221,19 @@ struct NiCamera : Node void read(NIFFile *nif) { - nif->load(left); - nif->load(right); - nif->load(top); - nif->load(bottom); - nif->load(nearDist); - nif->load(farDist); + left = nif->getFloat(); + right = nif->getFloat(); + top = nif->getFloat(); + bottom = nif->getFloat(); + nearDist = nif->getFloat(); + farDist = nif->getFloat(); - nif->load(vleft); - nif->load(vright); - nif->load(vtop); - nif->load(vbottom); + vleft = nif->getFloat(); + vright = nif->getFloat(); + vtop = nif->getFloat(); + vbottom = nif->getFloat(); - nif->load(LOD); + LOD = nif->getFloat(); } }; Camera cam; From f11bf49a9023cff3ceebdeab6326a5379f878995 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Jul 2012 13:23:41 +0200 Subject: [PATCH 091/688] cmake fix; silenced some warnings --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwworld/physicssystem.cpp | 13 ++++----- components/nifbullet/bullet_nif_loader.cpp | 34 +++++++++++----------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 9534ecc90..b12c58f0d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -39,7 +39,7 @@ add_openmw_dir (mwscript locals scriptmanager compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions - animationextensions + animationextensions transformationextensions ) add_openmw_dir (mwsound diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7d7b237ae..105995aca 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -165,7 +165,6 @@ namespace MWWorld for (std::vector >::const_iterator iter (actors.begin()); iter!=actors.end(); ++iter) { - OEngine::Physic::PhysicActor* act = mEngine->getCharacter(iter->first); //dirty stuff to get the camera orientation. Must be changed! Ogre::SceneNode *sceneNode = mRender.getScene()->getSceneNode (iter->first); @@ -175,10 +174,10 @@ namespace MWWorld Ogre::Quaternion yawQuat = yawNode->getOrientation(); Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); - + playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); - + playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() *-1 + 90; @@ -194,9 +193,9 @@ namespace MWWorld } - - - + + + mEngine->stepSimulation(dt); } @@ -307,7 +306,7 @@ namespace MWWorld { btTransform transform = mEngine->getRigidBody(handle)->getWorldTransform(); removeObject(handle); - + Ogre::Quaternion quat = Ogre::Quaternion(transform.getRotation().getW(), transform.getRotation().getX(), transform.getRotation().getY(), transform.getRotation().getZ()); Ogre::Vector3 vec = Ogre::Vector3(transform.getOrigin().getX(), transform.getOrigin().getY(), transform.getOrigin().getZ()); addObject(handle, handleToMesh[handle], quat, scale, vec); diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 17c5f18ac..f66239e7b 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -82,17 +82,17 @@ static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale } // Computes B = AxB (matrix*vector) -static void vectorMul(const Matrix &A, float *C) -{ - // Keep the original values - float a = C[0]; - float b = C[1]; - float c = C[2]; +//static void vectorMul(const Matrix &A, float *C) +//{ +// // Keep the original values +// float a = C[0]; +// float b = C[1]; +// float c = C[2]; - // Perform matrix multiplication, scaling and addition - for (int i=0;i<3;i++) - C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2]; -} +// // Perform matrix multiplication, scaling and addition +// for (int i=0;i<3;i++) +// C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2]; +//} ManualBulletShapeLoader::~ManualBulletShapeLoader() @@ -233,7 +233,7 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, const Nif::Transformation *trafo,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) { - + // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; @@ -267,11 +267,11 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, } } - - + + if (trafo) { - + // Get a non-const reference to the node's data, since we're // overwriting it. TODO: Is this necessary? Transformation &final = *((Transformation*)node->trafo); @@ -287,9 +287,9 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // Scalar values are so nice to deal with. Why can't everything // just be scalar? final.scale *= trafo->scale; - + } - + // For NiNodes, loop through children if (node->recType == Nif::RC_NiNode) @@ -304,7 +304,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, } } } - else if (node->recType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) + else if (node->recType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) { cShape->collide = true; handleNiTriShape(dynamic_cast(node), flags,getMatrix(node->trafo),getVector(node->trafo),node->trafo->scale,raycastingOnly); From dddf1b4ee57328f47226825a694a0e04d70149f8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 10 Jul 2012 04:45:14 -0700 Subject: [PATCH 092/688] Rename getMatrix->getMatrix3 and getVector->getVector3 --- components/nif/data.hpp | 18 +++++++++--------- components/nif/effect.hpp | 6 +++--- components/nif/nif_file.hpp | 10 +++++----- components/nif/node.hpp | 6 +++--- components/nif/property.hpp | 8 ++++---- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 118e21e54..ad670bc5e 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -110,7 +110,7 @@ public: if(nif->getInt()) nif->getFloats(normals, verts*3); - center = nif->getVector(); + center = nif->getVector3(); radius = nif->getFloat(); if(nif->getInt()) @@ -214,12 +214,12 @@ public: for(int i=0; igetFloat(); - nif->getVector(); // This isn't really shared between type 1 - // and type 2, most likely + nif->getVector3(); // This isn't really shared between type 1 + // and type 2, most likely if(type == 2) { - nif->getVector(); - nif->getVector(); + nif->getVector3(); + nif->getVector3(); } } } @@ -400,8 +400,8 @@ public: void read(NIFFile *nif) { - trafo.rotation = nif->getMatrix(); - trafo.trans = nif->getVector(); + trafo.rotation = nif->getMatrix3(); + trafo.trans = nif->getVector3(); trafo.scale = nif->getFloat(); int boneNum = nif->getInt(); @@ -412,8 +412,8 @@ public: { BoneInfo &bi = bones[i]; - bi.trafo.rotation = nif->getMatrix(); - bi.trafo.trans = nif->getVector(); + bi.trafo.rotation = nif->getMatrix3(); + bi.trafo.trans = nif->getVector3(); bi.trafo.scale = nif->getFloat(); bi.unknown = nif->getVector4(); diff --git a/components/nif/effect.hpp b/components/nif/effect.hpp index 1a2ecace8..850415dad 100644 --- a/components/nif/effect.hpp +++ b/components/nif/effect.hpp @@ -45,9 +45,9 @@ struct NiLight : Effect void read(NIFFile *nif) { dimmer = nif->getFloat(); - ambient = nif->getVector(); - diffuse = nif->getVector(); - specular = nif->getVector(); + ambient = nif->getVector3(); + diffuse = nif->getVector3(); + specular = nif->getVector3(); } }; SLight light; diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 0218795e0..a21882c6d 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -134,7 +134,7 @@ public: unsigned short getUShort() { return read_le16(); } int getInt() { return read_le32(); } float getFloat() { return read_le32f(); } - Ogre::Vector3 getVector() + Ogre::Vector3 getVector3() { float a[3]; for(size_t i = 0;i < 3;i++) @@ -148,7 +148,7 @@ public: a[i] = getFloat(); return Ogre::Vector4(a); } - Ogre::Matrix3 getMatrix() + Ogre::Matrix3 getMatrix3() { Ogre::Real a[3][3]; for(size_t i = 0;i < 3;i++) @@ -161,10 +161,10 @@ public: Transformation getTrafo() { Transformation t; - t.pos = getVector(); - t.rotation = getMatrix(); + t.pos = getVector3(); + t.rotation = getMatrix3(); t.scale = getFloat(); - t.velocity = getVector(); + t.velocity = getVector3(); return t; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 240dbe540..64ef1e3e9 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -63,9 +63,9 @@ public: if(hasBounds) { nif->getInt(); // always 1 - boundPos = nif->getVector(); - boundRot = nif->getMatrix(); - boundXYZ = nif->getVector(); + boundPos = nif->getVector3(); + boundRot = nif->getMatrix3(); + boundXYZ = nif->getVector3(); } parent = NULL; diff --git a/components/nif/property.hpp b/components/nif/property.hpp index 1b455b14f..b24e49b47 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -172,10 +172,10 @@ struct S_MaterialProperty void read(NIFFile *nif) { - ambient = nif->getVector(); - diffuse = nif->getVector(); - specular = nif->getVector(); - emissive = nif->getVector(); + ambient = nif->getVector3(); + diffuse = nif->getVector3(); + specular = nif->getVector3(); + emissive = nif->getVector3(); glossiness = nif->getFloat(); alpha = nif->getFloat(); } From 61b690eb4bd86e02db076abb9858c676d4b4eef5 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 10 Jul 2012 14:25:39 +0200 Subject: [PATCH 093/688] ncp scale factor --- apps/openmw/mwclass/npc.cpp | 8 ++++++++ apps/openmw/mwclass/npc.hpp | 2 ++ 2 files changed, 10 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0d2efcd9e..34575aa97 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -348,6 +348,14 @@ namespace MWClass return weight; } + void Npc::adjustScale(const MWWorld::Ptr& ptr,float& scale) const + { + //ptr. + //MWWorld::LiveCellRef* npc = ptr.get(); + //npc->base->race + //ESM::Race + } + void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const { y = 0; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index f50ed2159..2c4e6cb88 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -76,6 +76,8 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; + virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; static void registerSelf(); From 1fef4f2bc217929992771cae66449fcf2365003f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Jul 2012 16:59:26 +0200 Subject: [PATCH 094/688] splitting off credits from readme file --- credits.txt | 39 +++++++++++++++++++++++++++++++++++++++ readme.txt | 41 ----------------------------------------- 2 files changed, 39 insertions(+), 41 deletions(-) create mode 100644 credits.txt diff --git a/credits.txt b/credits.txt new file mode 100644 index 000000000..8d7cbe7d5 --- /dev/null +++ b/credits.txt @@ -0,0 +1,39 @@ +CREDITS + +Current Developers: +Aleksandar Jovanov +Alexander “Ace†Olofsson +athile +BrotherBrick +Cris “Mirceam†Mihalache +gugus / gus +Jacob “Yacoby†Essex +Jannik “scrawl†Heller +Jason “jhooks†Hooks +Karl-Felix “k1ll†Glatzer +Lukasz “lgro†Gromanowski +Marc “Zini†Zinnschlag +Michael “werdanith†Papageorgiou +Nikolay “corristo†Kasyanov +Pieter “pvdk†van der Kloet +Roman "Kromgart" Melnik +Sebastian “swick†Wick +Sylvain "Garvek" T. + +Retired Developers: +Ardekantur +Armin Preiml +Diggory Hardy +Jan Borsodi +Jan-Peter “peppe†Nilsson +Josua Grawitter +Nicolay Korslund +sergoz +Star-Demon +Yuri Krupenin + +OpenMW: +Thanks to DokterDume for kindly providing us with the Moon and Star logo used as the application icon and project logo. + +Launcher: +Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Files tab. diff --git a/readme.txt b/readme.txt index aa981dba3..327d36586 100644 --- a/readme.txt +++ b/readme.txt @@ -90,47 +90,6 @@ Allowed options: --fallback arg fallback values -CREDITS - -Current Developers: -Aleksandar Jovanov -Alexander “Ace†Olofsson -athile -BrotherBrick -Cris “Mirceam†Mihalache -gugus / gus -Jacob “Yacoby†Essex -Jannik “scrawl†Heller -Jason “jhooks†Hooks -Karl-Felix “k1ll†Glatzer -Lukasz “lgro†Gromanowski -Marc “Zini†Zinnschlag -Michael “werdanith†Papageorgiou -Nikolay “corristo†Kasyanov -Pieter “pvdk†van der Kloet -Roman "Kromgart" Melnik -Sebastian “swick†Wick -Sylvain "Garvek" T. - -Retired Developers: -Ardekantur -Armin Preiml -Diggory Hardy -Jan Borsodi -Jan-Peter “peppe†Nilsson -Josua Grawitter -Nicolay Korslund -sergoz -Star-Demon -Yuri Krupenin - -OpenMW: -Thanks to DokterDume for kindly providing us with the Moon and Star logo used as the application icon and project logo. - -Launcher: -Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Files tab. - - CHANGELOG 0.16.0 From 089ee335884ea2da2fec1845ea50789b03ef21c7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 10 Jul 2012 17:05:52 +0200 Subject: [PATCH 095/688] some readme.txt improvements --- readme.txt | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/readme.txt b/readme.txt index 327d36586..ded9bcd7b 100644 --- a/readme.txt +++ b/readme.txt @@ -12,8 +12,6 @@ EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) VeraMono.ttf: custom (see Bitstream Vera License.txt for more information) -THIS IS A WORK IN PROGRESS - INSTALLATION @@ -174,7 +172,6 @@ Task #113: Morrowind.ini Importer Task #215: Refactor the sound code Task #216: Update MyGUI - 0.13.0 Bug #145: Fixed sound problems after cell change @@ -232,7 +229,6 @@ Task #131: NPC Activation doesn't work properly Task #144: MWRender cleanup Task #155: cmake cleanup - 0.11.1 Bug #2: Resources loading doesn't work outside of bsa files @@ -259,4 +255,95 @@ Task #14: Replace tabs with 4 spaces Task #18: Move components from global namespace into their own namespace Task #123: refactor header files in components/esm -TODO add old changelog (take pre 0.11.1 changelog from wiki) +0.10.0 + +* NPC dialogue window (not functional yet) +* Collisions with objects +* Refactor the PlayerPos class +* Adjust file locations +* CMake files and test linking for Bullet +* Replace Ogre raycasting test for activation with something more precise +* Adjust player movement according to collision results +* FPS display +* Various Portability Improvements +* Mac OS X support is back! + +0.9.0 + +* Exterior cells loading, unloading and management +* Character Creation GUI +* Character creation +* Make cell names case insensitive when doing internal lookups +* Music player +* NPCs rendering + +0.8.0 + +* GUI +* Complete and working script engine +* In game console +* Sky rendering +* Sound and music +* Tons of smaller stuff + +0.7.0 + +* This release is a complete rewrite in C++. +* All D code has been culled, and all modules have been rewritten. +* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. + +0.6.0 + +* Coded a GUI system using MyGUI +* Skinned MyGUI to look like Morrowind (work in progress) +* Integrated the Monster script engine +* Rewrote some functions into script code +* Very early MyGUI < > Monster binding +* Fixed Windows sound problems (replaced old openal32.dll) + +0.5.0 + +* Collision detection with Bullet +* Experimental walk & fall character physics +* New key bindings: + * t toggle physics mode (walking, flying, ghost), + * n night eye, brightens the scene +* Fixed incompatability with DMD 1.032 and newer compilers +* * (thanks to tomqyp) +* Various minor changes and updates + +0.4.0 + +* Switched from Audiere to OpenAL +* * (BIG thanks to Chris Robinson) +* Added complete Makefile (again) as a alternative build tool +* More realistic lighting (thanks again to Chris Robinson) +* Various localization fixes tested with Russian and French versions +* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' +* Added ns option to disable sound, for debugging +* Various bug fixes +* Cosmetic changes to placate gdc Wall + +0.3.0 + +* Built and tested on Windows XP +* Partial support for FreeBSD (exceptions do not work) +* You no longer have to download Monster separately +* Made an alternative for building without DSSS (but DSSS still works) +* Renamed main program from 'morro' to 'openmw' +* Made the config system more robust +* Added oc switch for showing Ogre config window on startup +* Removed some config files, these are auto generated when missing. +* Separated plugins.cfg into linux and windows versions. +* Updated Makefile and sources for increased portability +* confirmed to work against OIS 1.0.0 (Ubuntu repository package) + +0.2.0 + +* Compiles with gdc +* Switched to DSSS for building D code +* Includes the program esmtool + +0.1.0 + +first release From 6c73f5e5184303ea0119f12a8a250abac5e89004 Mon Sep 17 00:00:00 2001 From: jvoisin Date: Tue, 10 Jul 2012 22:25:19 +0300 Subject: [PATCH 096/688] Add some translators/reversers to credits.txt --- credits.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/credits.txt b/credits.txt index 8d7cbe7d5..16b41d56a 100644 --- a/credits.txt +++ b/credits.txt @@ -32,6 +32,25 @@ sergoz Star-Demon Yuri Krupenin +PR team and Translators: +Julien (jvoisin/ap0) Voisin +sirherrbatka +ElderTroll +spyboot +corristo +Okulo +penguinroad +Kingpix + +Reverser and Research: +natirips +Sadler +fragonard +Greendogo +Myckel +modred11 +HiPhish + OpenMW: Thanks to DokterDume for kindly providing us with the Moon and Star logo used as the application icon and project logo. From 865bfc6f4762c883713f99c5e2e9ec5800068e4e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jul 2012 02:31:03 +0200 Subject: [PATCH 097/688] sync mrt_output setting --- apps/openmw/mwrender/objects.cpp | 15 +++++++++++++++ apps/openmw/mwrender/objects.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 6 +++++- apps/openmw/mwrender/sky.cpp | 9 --------- extern/shiny | 2 +- files/materials/atmosphere.shader | 4 ++-- files/materials/clouds.shader | 7 +++++-- files/materials/core.h | 4 ++-- files/materials/objects.shader | 20 ++++++++++---------- files/materials/shadowcaster.mat | 2 ++ files/materials/shadowcaster.shader | 2 +- files/materials/sky.mat | 4 ++-- files/materials/sun.shader | 4 ++-- 13 files changed, 49 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index fb2bfb3c5..b3457a5fa 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -450,3 +450,18 @@ void Objects::update(const float dt) it = mLights.erase(it); } } + +void Objects::rebuildStaticGeometry() +{ + for (std::map::iterator it = mStaticGeometry.begin(); it != mStaticGeometry.end(); ++it) + { + it->second->destroy(); + it->second->build(); + } + + for (std::map::iterator it = mStaticGeometrySmall.begin(); it != mStaticGeometrySmall.end(); ++it) + { + it->second->destroy(); + it->second->build(); + } +} diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index e240b11c9..443f25ecf 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -93,6 +93,8 @@ public: void removeCell(MWWorld::CellStore* store); void buildStaticGeometry(MWWorld::CellStore &cell); void setMwRoot(Ogre::SceneNode* root); + + void rebuildStaticGeometry(); }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5d9395460..fa20cd48d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -81,12 +81,14 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const //mRendering.getScene()->setCameraRelativeRendering(true); // disable unsupported effects - const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities(); + //const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities(); if (!waterShaderSupported()) Settings::Manager::setBool("shader", "Water", false); if (!Settings::Manager::getBool("shaders", "Objects")) Settings::Manager::setBool("enabled", "Shadows", false); + sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); + applyCompositors(); // Turn the entire scene (represented by the 'root' node) -90 @@ -614,6 +616,8 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec else if (it->second == "shader" && it->first == "Water") { applyCompositors(); + sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); + mObjects.rebuildStaticGeometry (); } } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index f32883fdd..f79ab6116 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -631,18 +631,9 @@ void SkyManager::create() // Make a unique "modifiable" copy of the materials to be sure //mCloudMaterial = mCloudMaterial->clone("Clouds"); //clouds_ent->getSubEntity(0)->setMaterial(mCloudMaterial); - //mAtmosphereMaterial = mAtmosphereMaterial->clone("Atmosphere"); - //atmosphere_ent->getSubEntity(0)->setMaterial(mAtmosphereMaterial); - /* - - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 0.0); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); mCloudMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); mCloudMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates(); diff --git a/extern/shiny b/extern/shiny index b3cfd41df..27a128c41 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit b3cfd41dff2758e268ce16f366b7e7857eee80ea +Subproject commit 27a128c414e2f92d6bcda379b9c9df04e69b7472 diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index 484381a1f..831ba1138 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -27,10 +27,10 @@ SH_START_PROGRAM { - shOutputColor(0) = colourPassthrough * atmosphereColour; + shOutputColour(0) = colourPassthrough * atmosphereColour; #if MRT - shOutputColor(1) = float4(1,1,1,1); + shOutputColour(1) = float4(1,1,1,1); #endif } diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index a772e3c5e..1a80a27dd 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -8,9 +8,12 @@ shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) shInput(float2, uv0) shOutput(float2, UV) + shColourInput(float4) + shOutput(float4, colourPassthrough) SH_START_PROGRAM { + shOutputPosition = shMatrixMult(wvp, shInputPosition); UV = uv0; } @@ -28,10 +31,10 @@ SH_START_PROGRAM { - shOutputColor(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + shOutputColour(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); #if MRT - shOutputColor(1) = float4(1,1,1,1); + shOutputColour(1) = float4(1,1,1,1); #endif } diff --git a/files/materials/core.h b/files/materials/core.h index 0ee95057f..b5d784dae 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -36,7 +36,7 @@ #ifdef SH_FRAGMENT_SHADER - #define shOutputColor(num) oColor##num + #define shOutputColour(num) oColor##num #define shDeclareMrtOutput(num) , out float4 oColor##num : COLOR##num @@ -75,7 +75,7 @@ #define shInputPosition vertex #define shOutputPosition gl_Position - #define shOutputColor(num) oColor##num + #define shOutputColour(num) oColor##num #define float4x4 mat4 diff --git a/files/materials/objects.shader b/files/materials/objects.shader index dd1b489d5..517845cb5 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -4,13 +4,13 @@ #define FOG @shPropertyBool(fog) -#define MRT @shPropertyNotBool(is_transparent) && @shPropertyBool(mrt_output) +#define MRT @shPropertyNotBool(is_transparent) && @shPropertyBool(mrt_output) && @shGlobalSettingBool(mrt_output) #define LIGHTING @shPropertyBool(lighting) #define SHADOWS LIGHTING && 0 #define SHADOWS_PSSM LIGHTING -#define SHADOWS 1 && LIGHTING +#define SHADOWS 0 && LIGHTING #define SHADOWS_PSSM 0 && LIGHTING #if FOG || MRT || SHADOWS_PSSM @@ -132,13 +132,13 @@ #if SHADOWS shInput(float4, lightSpacePos0) shSampler2D(shadowMap0) - shUniform(float2 invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 0) + shUniform(float2 invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1) #endif #if SHADOWS_PSSM @shForeach(3) shInput(float4, lightSpacePos@shIterator) shSampler2D(shadowMap@shIterator) - shUniform(float2 invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator) + shUniform(float2 invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1)) @shEndForeach shUniform(float4 pssmSplitPoints) @shSharedParameter(pssmSplitPoints) #endif @@ -148,7 +148,7 @@ #endif SH_START_PROGRAM { - shOutputColor(0) = shSample(diffuseMap, UV); + shOutputColour(0) = shSample(diffuseMap, UV); #if LIGHTING float3 normal = normalize(normalPassthrough); @@ -194,24 +194,24 @@ ambient *= colorPassthrough.xyz; #endif - shOutputColor(0).xyz *= (ambient + diffuse + materialEmissive.xyz); + shOutputColour(0).xyz *= (ambient + diffuse + materialEmissive.xyz); #endif #if HAS_VERTEXCOLOR && !LIGHTING - shOutputColor(0).xyz *= colorPassthrough.xyz; + shOutputColour(0).xyz *= colorPassthrough.xyz; #endif #if FOG float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); - shOutputColor(0).xyz = shLerp (shOutputColor(0).xyz, fogColor, fogValue); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); #endif // prevent negative color output (for example with negative lights) - shOutputColor(0).xyz = max(shOutputColor(0).xyz, float3(0,0,0)); + shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); #if MRT - shOutputColor(1) = float4(depthPassthrough / far,1,1,1); + shOutputColour(1) = float4(depthPassthrough / far,1,1,1); #endif } diff --git a/files/materials/shadowcaster.mat b/files/materials/shadowcaster.mat index 7c11fddf6..5c5c8e088 100644 --- a/files/materials/shadowcaster.mat +++ b/files/materials/shadowcaster.mat @@ -1,6 +1,7 @@ material openmw_shadowcaster_default { create_configuration Default + allow_fixed_function false pass { fog_override true @@ -18,6 +19,7 @@ material openmw_shadowcaster_default material openmw_shadowcaster_noalpha { create_configuration Default + allow_fixed_function false pass { fog_override true diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader index f772066a6..500207778 100644 --- a/files/materials/shadowcaster.shader +++ b/files/materials/shadowcaster.shader @@ -50,7 +50,7 @@ discard; #endif - shOutputColor(0) = float4(finalDepth, finalDepth, finalDepth, 1); + shOutputColour(0) = float4(finalDepth, finalDepth, finalDepth, 1); } #endif diff --git a/files/materials/sky.mat b/files/materials/sky.mat index c78570f7f..6465476dd 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -44,13 +44,13 @@ material openmw_clouds // second diffuse map is used for weather transitions texture_unit diffuseMap1 { - texture $diffuseMap1 + texture_alias cloud_texture_1 create_in_ffp true } texture_unit diffuseMap2 { - texture $diffuseMap2 + texture_alias cloud_texture_2 } } } diff --git a/files/materials/sun.shader b/files/materials/sun.shader index 811d45031..03ec7665c 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -29,10 +29,10 @@ SH_START_PROGRAM { - shOutputColor(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + shOutputColour(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); #if MRT - shOutputColor(1) = float4(1,1,1,1); + shOutputColour(1) = float4(1,1,1,1); #endif } From 485adc8bdc0060abb071a57d3a94607608731294 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jul 2012 03:19:51 +0200 Subject: [PATCH 098/688] shader mode button in settingswindow --- apps/openmw/mwgui/settingswindow.cpp | 62 +++++++++++++++++++ apps/openmw/mwgui/settingswindow.hpp | 3 + apps/openmw/mwrender/renderingmanager.cpp | 27 +++++++++ files/mygui/openmw_settings_window.layout | 11 +++- files/mygui/openmw_windows.skin.xml | 74 +++++++++++++++++++++++ files/settings-default.cfg | 2 + 6 files changed, 178 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 5e25d5ece..122849634 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -74,6 +74,14 @@ namespace return "16 : 10"; return boost::lexical_cast(xaspect) + " : " + boost::lexical_cast(yaspect); } + + std::string hlslGlsl () + { + if (Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") == std::string::npos) + return "hlsl"; + else + return "glsl"; + } } namespace MWGui @@ -103,8 +111,10 @@ namespace MWGui getWidget(mReflectObjectsButton, "ReflectObjectsButton"); getWidget(mReflectActorsButton, "ReflectActorsButton"); getWidget(mReflectTerrainButton, "ReflectTerrainButton"); + getWidget(mShadersButton, "ShadersButton"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); + mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); mFullscreenButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mWaterShaderButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mReflectObjectsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -189,6 +199,15 @@ namespace MWGui mReflectActorsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect actors", "Water") ? "#{sOn}" : "#{sOff}"); mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); + std::string shaders; + if (!Settings::Manager::getBool("shaders", "Objects")) + shaders = "off"; + else + { + shaders = Settings::Manager::getString("shader mode", "General"); + } + mShadersButton->setCaption (shaders); + if (!MWRender::RenderingManager::waterShaderSupported()) { mWaterShaderButton->setEnabled(false); @@ -280,6 +299,10 @@ namespace MWGui Settings::Manager::setBool("fullscreen", "Video", newState); apply(); } + } + else if (_sender == mShadersButton) + { + } else if (_sender == mVSyncButton) { @@ -306,6 +329,45 @@ namespace MWGui } } + void SettingsWindow::onShadersToggled(MyGUI::Widget* _sender) + { + std::string val = static_cast(_sender)->getCaption(); + if (val == "off") + val = hlslGlsl(); + else if (val == hlslGlsl()) + val = "cg"; + else + val = "off"; + + static_cast(_sender)->setCaption(val); + + if (val == "off") + { + Settings::Manager::setBool("shaders", "Objects", false); + + // water shader not supported with object shaders off + mWaterShaderButton->setCaptionWithReplacing("#{sOff}"); + mWaterShaderButton->setEnabled(false); + mReflectObjectsButton->setEnabled(false); + mReflectActorsButton->setEnabled(false); + mReflectTerrainButton->setEnabled(false); + Settings::Manager::setBool("shader", "Water", false); + } + else + { + // re-enable + mWaterShaderButton->setEnabled(true); + mReflectObjectsButton->setEnabled(true); + mReflectActorsButton->setEnabled(true); + mReflectTerrainButton->setEnabled(true); + + Settings::Manager::setBool("shaders", "Objects", true); + Settings::Manager::setString("shader mode", "General", val); + } + + apply(); + } + void SettingsWindow::onFpsToggled(MyGUI::Widget* _sender) { int newLevel = (Settings::Manager::getInt("fps", "HUD") + 1) % 3; diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index e3827c7b0..2bcab9d21 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -42,6 +42,7 @@ namespace MWGui MyGUI::Button* mReflectObjectsButton; MyGUI::Button* mReflectActorsButton; MyGUI::Button* mReflectTerrainButton; + MyGUI::Button* mShadersButton; // audio MyGUI::ScrollBar* mMasterVolumeSlider; @@ -59,6 +60,8 @@ namespace MWGui void onResolutionAccept(); void onResolutionCancel(); + void onShadersToggled(MyGUI::Widget* _sender); + void apply(); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index fa20cd48d..b3e3e42a4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -42,6 +42,15 @@ namespace MWRender { RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine) :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0) { + // select best shader mode + if (Settings::Manager::getString("shader mode", "General") == "") + { + if (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") == std::string::npos) + Settings::Manager::setString("shader mode", "General", "hlsl"); + else + Settings::Manager::setString("shader mode", "General", "glsl"); + } + mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5); mRendering.setWindowEventListener(this); @@ -619,6 +628,24 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); mObjects.rebuildStaticGeometry (); } + else if (it->second == "shaders" && it->first == "Objects") + { + sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); + mObjects.rebuildStaticGeometry (); + } + else if (it->second == "shader mode" && it->first == "General") + { + sh::Language lang; + std::string l = Settings::Manager::getString("shader mode", "General"); + if (l == "glsl") + lang = sh::Language_GLSL; + else if (l == "hlsl") + lang = sh::Language_HLSL; + else + lang = sh::Language_CG; + sh::Factory::getInstance ().setCurrentLanguage (lang); + mObjects.rebuildStaticGeometry (); + } } if (changeRes) diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 7c508b1e9..9d25b3ee5 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -1,7 +1,10 @@ - + + + + @@ -100,6 +103,12 @@ + + + + + + diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index b379f84ce..a9eb23d68 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -322,6 +322,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 883f32ae0..bd8bb64b3 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -48,6 +48,8 @@ anisotropy = 4 # Number of texture mipmaps to generate num mipmaps = 5 +shader mode = + [Shadows] # Shadows are only supported when object shaders are on! enabled = false From e0a99f104b2b2030923d460a5a13420430e0b639 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jul 2012 04:28:45 +0200 Subject: [PATCH 099/688] settings tab "shadows" --- apps/openmw/mwgui/settingswindow.cpp | 66 ++++++++++++-- apps/openmw/mwgui/settingswindow.hpp | 9 ++ apps/openmw/mwrender/renderingmanager.cpp | 8 +- apps/openmw/mwrender/shadows.cpp | 100 ++++++++++++---------- files/materials/objects.shader | 9 +- files/mygui/openmw_settings_window.layout | 44 ++++++++++ files/settings-default.cfg | 2 + 7 files changed, 180 insertions(+), 58 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 122849634..3ef89d422 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -112,6 +112,13 @@ namespace MWGui getWidget(mReflectActorsButton, "ReflectActorsButton"); getWidget(mReflectTerrainButton, "ReflectTerrainButton"); getWidget(mShadersButton, "ShadersButton"); + getWidget(mShadowsEnabledButton, "ShadowsEnabledButton"); + getWidget(mShadowsLargeDistance, "ShadowsLargeDistance"); + getWidget(mShadowsTextureSize, "ShadowsTextureSize"); + getWidget(mActorShadows, "ActorShadows"); + getWidget(mStaticsShadows, "StaticsShadows"); + getWidget(mMiscShadows, "MiscShadows"); + getWidget(mShadowsDebug, "ShadowsDebug"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -130,6 +137,14 @@ namespace MWGui mResolutionList->eventListChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onResolutionSelected); mAnisotropySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + mShadowsEnabledButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mShadowsLargeDistance->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mShadowsTextureSize->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadowTextureSize); + mActorShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mStaticsShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mMiscShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mShadowsDebug->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mMasterVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mVoiceVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mEffectsVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); @@ -182,7 +197,6 @@ namespace MWGui std::string tf = Settings::Manager::getString("texture filtering", "General"); mTextureFilteringButton->setCaption(textureFilteringToStr(tf)); mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(Settings::Manager::getInt("anisotropy", "General")) + ")"); - mAnisotropyBox->setVisible(tf == "anisotropic"); float val = (Settings::Manager::getFloat("max viewing distance", "Viewing distance")-sViewDistMin)/(sViewDistMax-sViewDistMin); int viewdist = (mViewDistanceSlider->getScrollRange()-1) * val; @@ -199,6 +213,14 @@ namespace MWGui mReflectActorsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect actors", "Water") ? "#{sOn}" : "#{sOff}"); mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); + mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); + mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); + mShadowsEnabledButton->setCaptionWithReplacing(Settings::Manager::getBool("enabled", "Shadows") ? "#{sOn}" : "#{sOff}"); + mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); + mStaticsShadows->setCaptionWithReplacing(Settings::Manager::getBool("statics shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); + mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); + mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + std::string shaders; if (!Settings::Manager::getBool("shaders", "Objects")) shaders = "off"; @@ -256,6 +278,25 @@ namespace MWGui mResolutionList->setIndexSelected(MyGUI::ITEM_NONE); } + void SettingsWindow::onShadowTextureSize(MyGUI::Widget* _sender) + { + std::string size = mShadowsTextureSize->getCaption(); + + if (size == "512") + size = "1024"; + else if (size == "1024") + size = "2048"; + else if (size == "2048") + size = "4096"; + else + size = "512"; + + mShadowsTextureSize->setCaption(size); + + Settings::Manager::setString("texture size", "Shadows", size); + apply(); + } + void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender) { std::string on = mWindowManager.getGameSettingString("sOn", "On"); @@ -299,10 +340,6 @@ namespace MWGui Settings::Manager::setBool("fullscreen", "Video", newState); apply(); } - } - else if (_sender == mShadersButton) - { - } else if (_sender == mVSyncButton) { @@ -324,6 +361,18 @@ namespace MWGui Settings::Manager::setBool("reflect actors", "Water", newState); else if (_sender == mReflectTerrainButton) Settings::Manager::setBool("reflect terrain", "Water", newState); + else if (_sender == mShadowsEnabledButton) + Settings::Manager::setBool("enabled", "Shadows", newState); + else if (_sender == mShadowsLargeDistance) + Settings::Manager::setBool("split", "Shadows", newState); + else if (_sender == mActorShadows) + Settings::Manager::setBool("actor shadows", "Shadows", newState); + else if (_sender == mStaticsShadows) + Settings::Manager::setBool("statics shadows", "Shadows", newState); + else if (_sender == mMiscShadows) + Settings::Manager::setBool("misc shadows", "Shadows", newState); + else if (_sender == mShadowsDebug) + Settings::Manager::setBool("debug", "Shadows", newState); apply(); } @@ -352,6 +401,11 @@ namespace MWGui mReflectActorsButton->setEnabled(false); mReflectTerrainButton->setEnabled(false); Settings::Manager::setBool("shader", "Water", false); + + // shadows not supported + mShadowsEnabledButton->setEnabled(false); + mShadowsEnabledButton->setCaptionWithReplacing("#{sOff}"); + Settings::Manager::setBool("enabled", "Shadows", false); } else { @@ -360,6 +414,7 @@ namespace MWGui mReflectObjectsButton->setEnabled(true); mReflectActorsButton->setEnabled(true); mReflectTerrainButton->setEnabled(true); + mShadowsEnabledButton->setEnabled(true); Settings::Manager::setBool("shaders", "Objects", true); Settings::Manager::setString("shader mode", "General", val); @@ -390,7 +445,6 @@ namespace MWGui next = "none"; mTextureFilteringButton->setCaption(textureFilteringToStr(next)); - mAnisotropyBox->setVisible(next == "anisotropic"); Settings::Manager::setString("texture filtering", "General", next); apply(); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 2bcab9d21..840d266f1 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -44,6 +44,14 @@ namespace MWGui MyGUI::Button* mReflectTerrainButton; MyGUI::Button* mShadersButton; + MyGUI::Button* mShadowsEnabledButton; + MyGUI::Button* mShadowsLargeDistance; + MyGUI::Button* mShadowsTextureSize; + MyGUI::Button* mActorShadows; + MyGUI::Button* mStaticsShadows; + MyGUI::Button* mMiscShadows; + MyGUI::Button* mShadowsDebug; + // audio MyGUI::ScrollBar* mMasterVolumeSlider; MyGUI::ScrollBar* mVoiceVolumeSlider; @@ -61,6 +69,7 @@ namespace MWGui void onResolutionCancel(); void onShadersToggled(MyGUI::Widget* _sender); + void onShadowTextureSize(MyGUI::Widget* _sender); void apply(); }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b3e3e42a4..d0613648d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -62,8 +62,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::OgrePlatform* platform = new sh::OgrePlatform("General", (resDir / "materials").string()); platform->setCacheFolder ("./"); mFactory = new sh::Factory(platform); - mFactory->setSharedParameter ("pssmSplitPoints", sh::makeProperty(new sh::Vector4(0,0,0,0))); - mFactory->setSharedParameter ("shadowFar_fadeStart", sh::makeProperty(new sh::Vector4(0,0,0,0))); //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog @@ -646,6 +644,12 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec sh::Factory::getInstance ().setCurrentLanguage (lang); mObjects.rebuildStaticGeometry (); } + else if (it->first == "Shadows") + { + mShadows->recreate (); + + mObjects.rebuildStaticGeometry (); + } } if (changeRes) diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index b4982e22d..4c5188e37 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -36,6 +36,9 @@ void Shadows::recreate() bool split = Settings::Manager::getBool("split", "Shadows"); //const bool split = false; + sh::Factory::getInstance ().setGlobalSetting ("shadows", enabled && !split ? "true" : "false"); + sh::Factory::getInstance ().setGlobalSetting ("shadows_pssm", enabled && split ? "true" : "false"); + if (!enabled) { mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE); @@ -122,53 +125,62 @@ void Shadows::recreate() // -------------------------------------------------------------------------------------------------------------------- // --------------------------- Debug overlays to display the content of shadow maps ----------------------------------- // -------------------------------------------------------------------------------------------------------------------- + if (Settings::Manager::getBool("debug", "Shadows")) + { + OverlayManager& mgr = OverlayManager::getSingleton(); + Overlay* overlay; - OverlayManager& mgr = OverlayManager::getSingleton(); - Overlay* overlay; - - // destroy if already exists - if (overlay = mgr.getByName("DebugOverlay")) - mgr.destroy(overlay); - - overlay = mgr.create("DebugOverlay"); - for (size_t i = 0; i < (split ? 3 : 1); ++i) { - TexturePtr tex = mRendering->getScene()->getShadowTexture(i); - - // Set up a debug panel to display the shadow - - if (MaterialManager::getSingleton().resourceExists("Ogre/DebugTexture" + StringConverter::toString(i))) - MaterialManager::getSingleton().remove("Ogre/DebugTexture" + StringConverter::toString(i)); - MaterialPtr debugMat = MaterialManager::getSingleton().create( - "Ogre/DebugTexture" + StringConverter::toString(i), - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false); - TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName()); - t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + // destroy if already exists + if (overlay = mgr.getByName("DebugOverlay")) + mgr.destroy(overlay); - OverlayContainer* debugPanel; - - // destroy container if exists - try - { - if (debugPanel = - static_cast( - mgr.getOverlayElement("Ogre/DebugTexPanel" + StringConverter::toString(i) - ))) - mgr.destroyOverlayElement(debugPanel); - } - catch (Ogre::Exception&) {} - - debugPanel = (OverlayContainer*) - (OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/DebugTexPanel" + StringConverter::toString(i))); - debugPanel->_setPosition(0.8, i*0.25); - debugPanel->_setDimensions(0.2, 0.24); - debugPanel->setMaterialName(debugMat->getName()); - debugPanel->show(); - overlay->add2D(debugPanel); - overlay->show(); - } + overlay = mgr.create("DebugOverlay"); + for (size_t i = 0; i < (split ? 3 : 1); ++i) { + TexturePtr tex = mRendering->getScene()->getShadowTexture(i); + // Set up a debug panel to display the shadow + + if (MaterialManager::getSingleton().resourceExists("Ogre/DebugTexture" + StringConverter::toString(i))) + MaterialManager::getSingleton().remove("Ogre/DebugTexture" + StringConverter::toString(i)); + MaterialPtr debugMat = MaterialManager::getSingleton().create( + "Ogre/DebugTexture" + StringConverter::toString(i), + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + + debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false); + TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName()); + t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + + OverlayContainer* debugPanel; + + // destroy container if exists + try + { + if (debugPanel = + static_cast( + mgr.getOverlayElement("Ogre/DebugTexPanel" + StringConverter::toString(i) + ))) + mgr.destroyOverlayElement(debugPanel); + } + catch (Ogre::Exception&) {} + + debugPanel = (OverlayContainer*) + (OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/DebugTexPanel" + StringConverter::toString(i))); + debugPanel->_setPosition(0.8, i*0.25); + debugPanel->_setDimensions(0.2, 0.24); + debugPanel->setMaterialName(debugMat->getName()); + debugPanel->show(); + overlay->add2D(debugPanel); + overlay->show(); + } + } + else + { + OverlayManager& mgr = OverlayManager::getSingleton(); + Overlay* overlay; + + if (overlay = mgr.getByName("DebugOverlay")) + mgr.destroy(overlay); + } } PSSMShadowCameraSetup* Shadows::getPSSMSetup() diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 517845cb5..c80a592a3 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -7,11 +7,8 @@ #define MRT @shPropertyNotBool(is_transparent) && @shPropertyBool(mrt_output) && @shGlobalSettingBool(mrt_output) #define LIGHTING @shPropertyBool(lighting) -#define SHADOWS LIGHTING && 0 -#define SHADOWS_PSSM LIGHTING - -#define SHADOWS 0 && LIGHTING -#define SHADOWS_PSSM 0 && LIGHTING +#define SHADOWS_PSSM LIGHTING && @shGlobalSettingBool(shadows_pssm) +#define SHADOWS LIGHTING && @shGlobalSettingBool(shadows) #if FOG || MRT || SHADOWS_PSSM #define NEED_DEPTH @@ -183,7 +180,7 @@ lightDir = normalize(lightDir); -#if @shIterator == 0 +#if @shIterator == 0 && (SHADOWS || SHADOWS_PSSM) diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow; #else diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 9d25b3ee5..d8fbca69c 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -190,6 +190,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index bd8bb64b3..a723e307c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -76,6 +76,8 @@ statics shadows = true # Fraction of the total shadow distance after which the shadow starts to fade out fade start = 0.8 +debug = false + [HUD] # FPS counter # 0: not visible From b2313be6d755b48002cc7e51b47a7af2134f3025 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jul 2012 04:31:33 +0200 Subject: [PATCH 100/688] submodule update --- extern/shiny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny b/extern/shiny index 27a128c41..34e952bf3 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 27a128c414e2f92d6bcda379b9c9df04e69b7472 +Subproject commit 34e952bf3d5e09adfd3e5c0f6462d612e193e447 From 395a7600fd061c73a24007d902c3148a31bb5038 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 10 Jul 2012 23:13:03 -0700 Subject: [PATCH 101/688] Initialize identify transform when declaring the identity object --- components/nif/nif_types.hpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/components/nif/nif_types.hpp b/components/nif/nif_types.hpp index 705ed5994..f364cd2d4 100644 --- a/components/nif/nif_types.hpp +++ b/components/nif/nif_types.hpp @@ -41,17 +41,9 @@ struct Transformation static const Transformation& getIdentity() { - static Transformation identity; - static bool iset = false; - if (!iset) - { - identity.scale = 1.0f; - identity.rotation[0][0] = 1.0f; - identity.rotation[1][1] = 1.0f; - identity.rotation[2][2] = 1.0f; - iset = true; - } - + static const Transformation identity = { + Ogre::Vector3::ZERO, Ogre::Matrix3::IDENTITY, 1.0f, Ogre::Vector3::ZERO + }; return identity; } }; From ad7383be439f9c3260dfc97ea8dbbc859342faac Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jul 2012 09:08:55 +0200 Subject: [PATCH 102/688] sky --- apps/openmw/mwrender/renderingmanager.cpp | 10 + apps/openmw/mwrender/shadows.cpp | 14 +- apps/openmw/mwrender/sky.cpp | 254 ++++++---------------- apps/openmw/mwrender/sky.hpp | 10 +- extern/shiny | 2 +- files/materials/atmosphere.shader | 6 +- files/materials/clouds.shader | 27 ++- files/materials/core.h | 6 +- files/materials/objects.shader | 40 ++-- files/materials/shadowcaster.shader | 2 +- files/materials/shadows.h | 4 +- files/materials/sky.mat | 40 +--- files/materials/sun.shader | 10 +- 13 files changed, 145 insertions(+), 280 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d0613648d..475e906c4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -63,6 +63,16 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const platform->setCacheFolder ("./"); mFactory = new sh::Factory(platform); + sh::Language lang; + std::string l = Settings::Manager::getString("shader mode", "General"); + if (l == "glsl") + lang = sh::Language_GLSL; + else if (l == "hlsl") + lang = sh::Language_HLSL; + else + lang = sh::Language_CG; + mFactory->setCurrentLanguage (lang); + //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog configureFog(1, ColourValue(1,1,1)); diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 4c5188e37..3d9f13243 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -91,16 +91,16 @@ void Shadows::recreate() // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) const PSSMShadowCameraSetup::SplitPointList& splitPointList = getPSSMSetup()->getSplitPoints(); - sh::Vector4* splitPoints = new sh::Vector4(splitPointList[1], splitPointList[2], splitPointList[3], 1.0); + sh::Vector3* splitPoints = new sh::Vector3(splitPointList[1], splitPointList[2], splitPointList[3]); - sh::Factory::getInstance ().setSharedParameter ("pssmSplitPoints", sh::makeProperty(splitPoints)); + sh::Factory::getInstance ().setSharedParameter ("pssmSplitPoints", sh::makeProperty(splitPoints)); shadowCameraSetup = ShadowCameraSetupPtr(mPSSMSetup); } else { LiSPSMShadowCameraSetup* lispsmSetup = new LiSPSMShadowCameraSetup(); - lispsmSetup->setOptimalAdjustFactor(2); + lispsmSetup->setOptimalAdjustFactor(64); //lispsmSetup->setCameraLightDirectionThreshold(Degree(0)); //lispsmSetup->setUseAggressiveFocusRegion(false); shadowCameraSetup = ShadowCameraSetupPtr(lispsmSetup); @@ -131,7 +131,7 @@ void Shadows::recreate() Overlay* overlay; // destroy if already exists - if (overlay = mgr.getByName("DebugOverlay")) + if ((overlay = mgr.getByName("DebugOverlay"))) mgr.destroy(overlay); overlay = mgr.create("DebugOverlay"); @@ -155,10 +155,10 @@ void Shadows::recreate() // destroy container if exists try { - if (debugPanel = + if ((debugPanel = static_cast( mgr.getOverlayElement("Ogre/DebugTexPanel" + StringConverter::toString(i) - ))) + )))) mgr.destroyOverlayElement(debugPanel); } catch (Ogre::Exception&) {} @@ -178,7 +178,7 @@ void Shadows::recreate() OverlayManager& mgr = OverlayManager::getSingleton(); Overlay* overlay; - if (overlay = mgr.getByName("DebugOverlay")) + if ((overlay = mgr.getByName("DebugOverlay"))) mgr.destroy(overlay); } } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index f79ab6116..65a9e8953 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -15,6 +15,8 @@ #include #include +#include + #include #include "../mwbase/environment.hpp" @@ -50,7 +52,7 @@ void BillboardObject::setSize(const float size) void BillboardObject::setVisibility(const float visibility) { - mMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, visibility); + //mMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, visibility); } void BillboardObject::setPosition(const Vector3& pPosition) @@ -76,7 +78,7 @@ void BillboardObject::setVisibilityFlags(int flags) void BillboardObject::setColour(const ColourValue& pColour) { - mMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(pColour); + //mMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(pColour); } void BillboardObject::setRenderQueue(unsigned int id) @@ -112,6 +114,12 @@ void BillboardObject::init(const String& textureName, mBBSet->createBillboard(0,0,0); mBBSet->setCastShadows(false); + sh::MaterialInstance* m = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), "openmw_sun"); + m->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + + mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); + + /* mMaterial = MaterialManager::getSingleton().create("BillboardMaterial"+StringConverter::toString(bodyCount), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); mMaterial->removeAllTechniques(); Pass* p = mMaterial->createTechnique()->createPass(); @@ -123,7 +131,6 @@ void BillboardObject::init(const String& textureName, p->setAmbient(0.0,0.0,0.0); p->setPolygonModeOverrideable(false); p->createTextureUnitState(textureName); - mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); HighLevelGpuProgramPtr vshader; @@ -183,7 +190,7 @@ void BillboardObject::init(const String& textureName, fshader->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); mMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); - +*/ bodyCount++; } @@ -194,6 +201,8 @@ Moon::Moon( const String& textureName, { init(textureName, initialSize, position, rootNode); + + /* HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); HighLevelGpuProgramPtr vshader; if (mgr.resourceExists("Moon_VP")) @@ -261,6 +270,8 @@ Moon::Moon( const String& textureName, fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); mMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); + */ + setVisibility(1.0); mPhase = Moon::Phase_Full; @@ -273,7 +284,7 @@ void Moon::setType(const Moon::Type& type) void Moon::setSkyColour(const Ogre::ColourValue& colour) { - mMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("skyColour", colour); + //mMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("skyColour", colour); } void Moon::setPhase(const Moon::Phase& phase) @@ -295,7 +306,7 @@ void Moon::setPhase(const Moon::Phase& phase) textureName += ".dds"; - mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(textureName); + //mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(textureName); mPhase = phase; } @@ -387,8 +398,6 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mSceneMgr(NULL) , mAtmosphereDay(NULL) , mAtmosphereNight(NULL) - , mCloudMaterial() - , mAtmosphereMaterial() , mCloudFragmentShader() , mClouds() , mNextClouds() @@ -406,6 +415,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mMasserEnabled(true) , mSecundaEnabled(true) , mCreated(false) + , mCloudAnimationTimer(0.f) { mSceneMgr = pMwRoot->getCreator(); mRootNode = mCamera->getParentSceneNode()->createChildSceneNode(); @@ -415,7 +425,19 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) void SkyManager::create() { - /// \todo preload all the textures and meshes that are used for sky rendering + sh::Factory::getInstance().setSharedParameter ("cloudBlendFactor", + sh::makeProperty(new sh::FloatValue(0))); + sh::Factory::getInstance().setSharedParameter ("cloudOpacity", + sh::makeProperty(new sh::FloatValue(1))); + sh::Factory::getInstance().setSharedParameter ("cloudColour", + sh::makeProperty(new sh::Vector3(1,1,1))); + sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", + sh::makeProperty(new sh::FloatValue(0))); + sh::Factory::getInstance().setSharedParameter ("nightFade", + sh::makeProperty(new sh::FloatValue(0))); + + sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", ""); + sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", ""); // Create overlay used for thunderstorm MaterialPtr material = MaterialManager::getSingleton().create( "ThunderMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); @@ -449,9 +471,6 @@ void SkyManager::create() mSunGlare->setRenderQueue(RQG_SkiesLate); mSunGlare->setVisibilityFlags(RV_Glare); - - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - // Stars MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); Entity* night1_ent = mSceneMgr->createEntity("meshes\\sky_night_01.nif"); @@ -462,76 +481,18 @@ void SkyManager::create() mAtmosphereNight = mRootNode->createChildSceneNode(); mAtmosphereNight->attachObject(night1_ent); - // Stars vertex shader - HighLevelGpuProgramPtr stars_vp = mgr.createProgram("Stars_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_VERTEX_PROGRAM); - stars_vp->setParameter("profiles", "vs_2_x arbvp1"); - stars_vp->setParameter("entry_point", "main_vp"); - StringUtil::StrStreamType outStream4; - outStream4 << - "void main_vp( \n" - " float4 position : POSITION, \n" - " in float2 uv : TEXCOORD0, \n" - " out float2 oUV : TEXCOORD0, \n" - " out float oFade : TEXCOORD1, \n" - " out float4 oPosition : POSITION, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" - " oUV = uv; \n" - " oFade = (position.z > 50) ? 1.f : 0.f; \n" - " oPosition = mul( worldViewProj, position ); \n" - "}"; - stars_vp->setSource(outStream4.str()); - stars_vp->load(); - stars_vp->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - - // Stars fragment shader - HighLevelGpuProgramPtr stars_fp = mgr.createProgram("Stars_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_FRAGMENT_PROGRAM); - stars_fp->setParameter("profiles", "ps_2_x arbfp1"); - stars_fp->setParameter("entry_point", "main_fp"); - StringUtil::StrStreamType outStream5; - outStream5 << - "void main_fp( \n" - " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n"; - if (RenderingManager::useMRT()) outStream5 << - " out float4 oColor1 : COLOR1, \n"; - outStream5 << - " in float fade : TEXCOORD1, \n" - " uniform sampler2D texture : TEXUNIT0, \n" - " uniform float opacity, \n" - " uniform float4 diffuse, \n" - " uniform float4 emissive \n" - ") \n" - "{ \n" - " oColor = tex2D(texture, uv) * float4(emissive.xyz, 1) * float4(1,1,1,fade*diffuse.a); \n"; - if (RenderingManager::useMRT()) outStream5 << - " oColor1 = float4(1, 0, 0, 1); \n"; - outStream5 << - "}"; - stars_fp->setSource(outStream5.str()); - stars_fp->load(); - stars_fp->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - stars_fp->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - - /* for (unsigned int i=0; igetNumSubEntities(); ++i) { - MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); - mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); - mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); - mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0); - mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); - mp->getTechnique(0)->getPass(0)->setVertexProgram(stars_vp->getName()); - mp->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName()); - mp->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); - mStarsMaterials[i] = mp; + std::string matName = "openmw_stars_" + boost::lexical_cast(i); + sh::MaterialInstance* m = sh::Factory::getInstance ().createMaterialInstance (matName, "openmw_stars"); + + std::string textureName = sh::retrieveValue( + sh::Factory::getInstance().getMaterialInstance(night1_ent->getSubEntity (i)->getMaterialName ())->getProperty("diffuseMap"), NULL).get(); + + m->setProperty ("texture", sh::makeProperty(new sh::StringValue(textureName))); + + night1_ent->getSubEntity(i)->setMaterialName (matName); } - */ // Atmosphere (day) mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); @@ -545,11 +506,6 @@ void SkyManager::create() mAtmosphereDay = mRootNode->createChildSceneNode(); mAtmosphereDay->attachObject(atmosphere_ent); atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere"); - //mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); - //mAtmosphereMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); - // Atmosphere shader - // mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); - atmosphere_ent->getSubEntity (0)->setMaterialName("openmw_atmosphere"); // Clouds NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); @@ -558,89 +514,11 @@ void SkyManager::create() clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); SceneNode* clouds_node = mRootNode->createChildSceneNode(); clouds_node->attachObject(clouds_ent); - //mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds"); - //mCloudMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); clouds_ent->setCastShadows(false); - // Clouds vertex shader - HighLevelGpuProgramPtr vshader2 = mgr.createProgram("Clouds_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_VERTEX_PROGRAM); - vshader2->setParameter("profiles", "vs_2_x arbvp1"); - vshader2->setParameter("entry_point", "main_vp"); - StringUtil::StrStreamType outStream3; - outStream3 << - "void main_vp( \n" - " float4 position : POSITION, \n" - " in float4 color : COLOR, \n" - " out float4 oColor : TEXCOORD1, \n" - " in float2 uv : TEXCOORD0, \n" - " out float2 oUV : TEXCOORD0, \n" - " out float4 oPosition : POSITION, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" - " oUV = uv; \n" - " oColor = color; \n" - " oPosition = mul( worldViewProj, position ); \n" - "}"; - vshader2->setSource(outStream3.str()); - vshader2->load(); - vshader2->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - //mCloudMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader2->getName()); - - // Clouds fragment shader - mCloudFragmentShader = mgr.createProgram("Clouds_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_FRAGMENT_PROGRAM); - mCloudFragmentShader->setParameter("profiles", "ps_2_x arbfp1"); - mCloudFragmentShader->setParameter("entry_point", "main_fp"); - StringUtil::StrStreamType outStream2; - outStream2 << - "void main_fp( \n" - " in float2 uv : TEXCOORD0, \n" - " in float4 color : TEXCOORD1, \n" - " out float4 oColor : COLOR, \n"; - if (RenderingManager::useMRT()) outStream2 << - " out float4 oColor1 : COLOR1, \n"; - outStream2 << - " uniform sampler2D texture : TEXUNIT0, \n" - " uniform sampler2D secondTexture : TEXUNIT1, \n" - " uniform float transitionFactor, \n" - " uniform float time, \n" - " uniform float speed, \n" - " uniform float opacity, \n" - " uniform float4 emissive \n" - ") \n" - "{ \n" - " uv += float2(0,1) * time * speed * 0.003; \n" // Scroll in y direction - " float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n" - " oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n"; - if (RenderingManager::useMRT()) outStream2 << - " oColor1 = float4(1, 0, 0, 1); \n"; - outStream2 << - "}"; - mCloudFragmentShader->setSource(outStream2.str()); - mCloudFragmentShader->load(); - mCloudFragmentShader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - //mCloudMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(mCloudFragmentShader->getName()); - setCloudsOpacity(0.75); - ModVertexAlpha(clouds_ent, 1); - // I'm not sure if the materials are being used by any other objects - // Make a unique "modifiable" copy of the materials to be sure - //mCloudMaterial = mCloudMaterial->clone("Clouds"); - //clouds_ent->getSubEntity(0)->setMaterial(mCloudMaterial); - /* - mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); - mCloudMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); - - mCloudMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates(); - mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("textures\\tx_sky_cloudy.dds"); - mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); -*/ - mCreated = true; } @@ -669,7 +547,10 @@ void SkyManager::update(float duration) if (!mEnabled) return; // UV Scroll the clouds - //mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstantFromTime("time", MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); + mCloudAnimationTimer += duration * mCloudSpeed * (MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); + sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", + sh::makeProperty(new sh::FloatValue(mCloudAnimationTimer))); + /// \todo improve this mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); @@ -678,15 +559,15 @@ void SkyManager::update(float duration) if (mSunEnabled) { - // take 1/5 sec for fading the glare effect from invisible to full + // take 1/10 sec for fading the glare effect from invisible to full if (mGlareFade > mGlare) { - mGlareFade -= duration*5; + mGlareFade -= duration*10; if (mGlareFade < mGlare) mGlareFade = mGlare; } else if (mGlareFade < mGlare) { - mGlareFade += duration*5; + mGlareFade += duration*10; if (mGlareFade > mGlare) mGlareFade = mGlare; } @@ -733,39 +614,34 @@ void SkyManager::setMoonColour (bool red) : ColourValue(1.0, 1.0, 1.0)); } -void SkyManager::setCloudsOpacity(float opacity) -{ - if (!mCreated) return; - //mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(opacity)); -} - void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mCreated) return; - /* if (mClouds != weather.mCloudTexture) { - mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("textures\\"+weather.mCloudTexture); + sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", "textures\\"+weather.mCloudTexture); mClouds = weather.mCloudTexture; } if (mNextClouds != weather.mNextCloudTexture) { - mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName("textures\\"+weather.mNextCloudTexture); + sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", "textures\\"+weather.mNextCloudTexture); mNextClouds = weather.mNextCloudTexture; } if (mCloudBlendFactor != weather.mCloudBlendFactor) { - mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("transitionFactor", Real(weather.mCloudBlendFactor)); mCloudBlendFactor = weather.mCloudBlendFactor; + sh::Factory::getInstance().setSharedParameter ("cloudBlendFactor", + sh::makeProperty(new sh::FloatValue(weather.mCloudBlendFactor))); } if (mCloudOpacity != weather.mCloudOpacity) { - mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(weather.mCloudOpacity)); mCloudOpacity = weather.mCloudOpacity; + sh::Factory::getInstance().setSharedParameter ("cloudOpacity", + sh::makeProperty(new sh::FloatValue(weather.mCloudOpacity))); } if (mCloudColour != weather.mSunColor) @@ -774,23 +650,21 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) weather.mSunColor.g*0.7 + weather.mAmbientColor.g*0.7, weather.mSunColor.b*0.7 + weather.mAmbientColor.b*0.7); - mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(clr); + sh::Factory::getInstance().setSharedParameter ("cloudColour", + sh::makeProperty(new sh::Vector3(clr.r, clr.g, clr.b))); + mCloudColour = weather.mSunColor; } -*/ + if (mSkyColour != weather.mSkyColor) { mSkyColour = weather.mSkyColor; sh::Factory::getInstance().setSharedParameter ("atmosphereColour", sh::makeProperty(new sh::Vector4( - weather.mSkyColor.r, weather.mSkyColor.g, weather.mSkyColor.b, 1.0))); - } -/* - if (mCloudSpeed != weather.mCloudSpeed) - { - mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("speed", Real(weather.mCloudSpeed)); - mCloudSpeed = weather.mCloudSpeed; + weather.mSkyColor.r, weather.mSkyColor.g, weather.mSkyColor.b, weather.mSkyColor.a))); } + mCloudSpeed = weather.mCloudSpeed; + if (weather.mNight && mStarsOpacity != weather.mNightFade) { if (weather.mNightFade == 0) @@ -798,12 +672,14 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) else { mAtmosphereNight->setVisible(true); - for (int i=0; i<7; ++i) - mStarsMaterials[i]->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, weather.mNightFade); + + sh::Factory::getInstance().setSharedParameter ("nightFade", + sh::makeProperty(new sh::FloatValue(weather.mNightFade))); + mStarsOpacity = weather.mNightFade; } } - */ + float strength; float timeofday_angle = std::abs(mSunGlare->getPosition().z/mSunGlare->getPosition().length()); diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 6bb055824..f59583c04 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -139,9 +139,6 @@ namespace MWRender 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(); @@ -182,6 +179,8 @@ namespace MWRender int mDay; int mMonth; + float mCloudAnimationTimer; + BillboardObject* mSun; BillboardObject* mSunGlare; Moon* mMasser; @@ -194,11 +193,6 @@ namespace MWRender Ogre::SceneNode* mAtmosphereDay; Ogre::SceneNode* mAtmosphereNight; - Ogre::MaterialPtr mCloudMaterial; - Ogre::MaterialPtr mAtmosphereMaterial; - - Ogre::MaterialPtr mStarsMaterials[7]; - Ogre::HighLevelGpuProgramPtr mCloudFragmentShader; // remember some settings so we don't have to apply them again if they didnt change diff --git a/extern/shiny b/extern/shiny index 34e952bf3..7485a15c2 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 34e952bf3d5e09adfd3e5c0f6462d612e193e447 +Subproject commit 7485a15c26f129084a1c264fa1a98dc2de86f298 diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index 831ba1138..295fa9376 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -1,11 +1,11 @@ #include "core.h" -#define MRT @shPropertyBool(mrt_output) +#define MRT @shGlobalSettingBool(mrt_output) #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shColourInput(float4) shOutput(float4, colourPassthrough) @@ -23,7 +23,7 @@ #if MRT shDeclareMrtOutput(1) #endif - shUniform(float4 atmosphereColour) @shSharedParameter(atmosphereColour) + shUniform(float4, atmosphereColour) @shSharedParameter(atmosphereColour) SH_START_PROGRAM { diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index 1a80a27dd..7677ecd95 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -1,11 +1,11 @@ #include "core.h" -#define MRT @shPropertyBool(mrt_output) +#define MRT @shGlobalSettingBool(mrt_output) #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shInput(float2, uv0) shOutput(float2, UV) shColourInput(float4) @@ -13,7 +13,7 @@ SH_START_PROGRAM { - + colourPassthrough = colour; shOutputPosition = shMatrixMult(wvp, shInputPosition); UV = uv0; } @@ -21,17 +21,28 @@ #else SH_BEGIN_PROGRAM - shSampler2D(diffuseMap) shInput(float2, UV) + shInput(float4, colourPassthrough) #if MRT shDeclareMrtOutput(1) #endif - shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) - shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) - + + shSampler2D(diffuseMap1) + shSampler2D(diffuseMap2) + + shUniform(float, cloudBlendFactor) @shSharedParameter(cloudBlendFactor) + shUniform(float, cloudAnimationTimer) @shSharedParameter(cloudAnimationTimer) + shUniform(float, cloudOpacity) @shSharedParameter(cloudOpacity) + shUniform(float3, cloudColour) @shSharedParameter(cloudColour) + SH_START_PROGRAM { - shOutputColour(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + // Scroll in y direction + float2 scrolledUV = UV + float2(0,1) * cloudAnimationTimer * 0.003; + + float4 albedo = shSample(diffuseMap1, scrolledUV) * (1-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor; + + shOutputColour(0) = colourPassthrough * float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity); #if MRT shOutputColour(1) = float4(1,1,1,1); diff --git a/files/materials/core.h b/files/materials/core.h index b5d784dae..34095f93d 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -9,7 +9,7 @@ #define shMatrixMult(m, v) mul(m, v) - #define shUniform(s) , uniform s + #define shUniform(type, name) , uniform type name #define shInput(type, name) , in type name : TEXCOORD@shCounter(1) #define shOutput(type, name) , out type name : TEXCOORD@shCounter(2) @@ -59,13 +59,13 @@ #define float4 vec4 #define int2 ivec2 #define int3 ivec3 - #define int4 ivec4 + #define int4 ivec4/ #define shTexture2D sampler2D #define shSample(tex, coord) texture(tex, coord) #define shLerp(a, b, t) mix(a, b, t) #define shSaturate(a) clamp(a, 0.0, 1.0) - #define shUniform(s) uniform s; + #define shUniform(type, name) uniform type name; #define shSampler2D(name) uniform sampler2D name; @shUseSampler(name) diff --git a/files/materials/objects.shader b/files/materials/objects.shader index c80a592a3..3b4992f5b 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -21,7 +21,7 @@ // ------------------------------------- VERTEX --------------------------------------- SH_BEGIN_PROGRAM - shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shInput(float2, uv0) shOutput(float2, UV) shNormalInput(float4) @@ -41,16 +41,16 @@ #if SHADOWS shOutput(float4, lightSpacePos0) - shUniform(float4x4 texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) - shUniform(float4x4 worldMatrix) @shAutoConstant(worldMatrix, world_matrix) + shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) #endif #if SHADOWS_PSSM @shForeach(3) shOutput(float4, lightSpacePos@shIterator) - shUniform(float4x4 texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) + shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) @shEndForeach - shUniform(float4x4 worldMatrix) @shAutoConstant(worldMatrix, world_matrix) + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) #endif SH_START_PROGRAM { @@ -99,27 +99,27 @@ #endif #if MRT - shUniform(float far) @shAutoConstant(far, far_clip_distance) + shUniform(float, far) @shAutoConstant(far, far_clip_distance) #endif #if LIGHTING shInput(float3, normalPassthrough) shInput(float3, objSpacePositionPassthrough) - shUniform(float4 lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - //shUniform(float passIteration) @shAutoConstant(passIteration, pass_iteration_number) - shUniform(float4 materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) - shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) - shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) + //shUniform(float, passIteration) @shAutoConstant(passIteration, pass_iteration_number) + shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) + shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) @shForeach(8) - shUniform(float4 lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) - shUniform(float4 lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) - shUniform(float4 lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) + shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) + shUniform(float4, lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) + shUniform(float4, lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) @shEndForeach #endif #if FOG - shUniform(float3 fogColor) @shAutoConstant(fogColor, fog_colour) - shUniform(float4 fogParams) @shAutoConstant(fogParams, fog_params) + shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) + shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) #endif #ifdef HAS_VERTEXCOLOR @@ -129,19 +129,19 @@ #if SHADOWS shInput(float4, lightSpacePos0) shSampler2D(shadowMap0) - shUniform(float2 invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1) + shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1) #endif #if SHADOWS_PSSM @shForeach(3) shInput(float4, lightSpacePos@shIterator) shSampler2D(shadowMap@shIterator) - shUniform(float2 invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1)) + shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1)) @shEndForeach - shUniform(float4 pssmSplitPoints) @shSharedParameter(pssmSplitPoints) + shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints) #endif #if SHADOWS || SHADOWS_PSSM - shUniform(float4 shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) + shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) #endif SH_START_PROGRAM { diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader index 500207778..a5551509d 100644 --- a/files/materials/shadowcaster.shader +++ b/files/materials/shadowcaster.shader @@ -9,7 +9,7 @@ shInput(float2, uv0) shOutput(float2, UV) #endif - shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shOutput(float2, depth) SH_START_PROGRAM { diff --git a/files/materials/shadows.h b/files/materials/shadows.h index a3518b976..9127d28f7 100644 --- a/files/materials/shadows.h +++ b/files/materials/shadows.h @@ -1,6 +1,4 @@ - - float depthShadowPCF (shTexture2D shadowMap, float4 shadowMapPos, float2 offset) { shadowMapPos /= shadowMapPos.w; @@ -31,7 +29,7 @@ float pssmDepthShadow ( shTexture2D shadowMap2, float depth, - float4 pssmSplitPoints) + float3 pssmSplitPoints) { float shadow; diff --git a/files/materials/sky.mat b/files/materials/sky.mat index 6465476dd..3f52966e8 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -1,7 +1,6 @@ material openmw_moon { allow_fixed_function false - mrt_output true pass { vertex_program moon_vertex @@ -9,11 +8,6 @@ material openmw_moon polygon_mode_overrideable off - shader_properties - { - mrt_output $mrt_output - } - texture_unit diffuseMap { texture $diffuseMap @@ -25,7 +19,6 @@ material openmw_moon material openmw_clouds { allow_fixed_function false - mrt_output true pass { vertex_program clouds_vertex @@ -36,11 +29,6 @@ material openmw_clouds scene_blend alpha_blend depth_write off - shader_properties - { - mrt_output $mrt_output - } - // second diffuse map is used for weather transitions texture_unit diffuseMap1 { @@ -58,7 +46,6 @@ material openmw_clouds material openmw_atmosphere { allow_fixed_function false - mrt_output true pass { vertex_program atmosphere_vertex @@ -68,33 +55,26 @@ material openmw_atmosphere scene_blend alpha_blend depth_write off - - shader_properties - { - mrt_output $mrt_output - } } } material openmw_stars { allow_fixed_function false - mrt_output true pass { vertex_program stars_vertex fragment_program stars_fragment polygon_mode_overrideable off - - shader_properties - { - mrt_output $mrt_output - } + + depth_check off + depth_write off + scene_blend alpha_blend texture_unit diffuseMap { - diffuseMap $diffuseMap + direct_texture $texture } } } @@ -103,7 +83,6 @@ material openmw_stars material openmw_sun { allow_fixed_function false - mrt_output true pass { vertex_program sun_vertex @@ -111,14 +90,9 @@ material openmw_sun polygon_mode_overrideable off - shader_properties + texture_unit diffuseMap { - mrt_output $mrt_output - } - - texture unit diffuseMap - { - diffuseMap $diffuseMap + direct_texture $texture } } } diff --git a/files/materials/sun.shader b/files/materials/sun.shader index 03ec7665c..4c7926756 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -1,12 +1,12 @@ #include "core.h" -#define MRT @shPropertyBool(mrt_output) +#define MRT @shGlobalSettingBool(mrt_output) #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shUniform(float4x4 wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) shInput(float2, uv0) shOutput(float2, UV) @@ -24,12 +24,14 @@ #if MRT shDeclareMrtOutput(1) #endif - shUniform(float4 materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) - shUniform(float4 materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) SH_START_PROGRAM { shOutputColour(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); + + shOutputColour(0) = shSample(diffuseMap, UV); #if MRT shOutputColour(1) = float4(1,1,1,1); From cd6e9986b6268baa01161c373a921c9a2577f217 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 11 Jul 2012 09:09:38 +0200 Subject: [PATCH 103/688] forgot to add files --- files/materials/stars.shader | 46 +++++++++++++++++++++++++++++++++ files/materials/stars.shaderset | 15 +++++++++++ 2 files changed, 61 insertions(+) create mode 100644 files/materials/stars.shader create mode 100644 files/materials/stars.shaderset diff --git a/files/materials/stars.shader b/files/materials/stars.shader new file mode 100644 index 000000000..75592fc72 --- /dev/null +++ b/files/materials/stars.shader @@ -0,0 +1,46 @@ +#include "core.h" + +#define MRT @shGlobalSettingBool(mrt_output) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + + shInput(float2, uv0) + shOutput(float2, UV) + shOutput(float, fade) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + + fade = (shInputPosition.z > 50) ? 1 : 0; + } + +#else + + SH_BEGIN_PROGRAM +#if MRT + shDeclareMrtOutput(1) +#endif + + shInput(float2, UV) + shInput(float, fade) + + shSampler2D(diffuseMap) + shUniform(float, nightFade) @shSharedParameter(nightFade) + + + SH_START_PROGRAM + { + shOutputColour(0) = shSample(diffuseMap, UV) * float4(1,1,1, nightFade * fade); + + +#if MRT + shOutputColour(1) = float4(1,1,1,1); +#endif + } + +#endif diff --git a/files/materials/stars.shaderset b/files/materials/stars.shaderset new file mode 100644 index 000000000..0f8803450 --- /dev/null +++ b/files/materials/stars.shaderset @@ -0,0 +1,15 @@ +shader_set stars_vertex +{ + source stars.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set stars_fragment +{ + source stars.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} From 8e324c90dcecf5c5655f89aaa37e693587bd4950 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 11 Jul 2012 06:37:17 -0700 Subject: [PATCH 104/688] Constify some RecordPtrT methods --- components/nif/record_ptr.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/components/nif/record_ptr.hpp b/components/nif/record_ptr.hpp index 755094147..7e6a43f53 100644 --- a/components/nif/record_ptr.hpp +++ b/components/nif/record_ptr.hpp @@ -71,18 +71,21 @@ public: } /// Look up the actual object from the index - X* getPtr() + X* getPtr() const { assert(ptr != NULL); return ptr; } - X& get() { return *getPtr(); } + X& get() const + { return *getPtr(); } /// Syntactic sugar - X* operator->() { return getPtr(); } + X* operator->() const + { return getPtr(); } /// Pointers are allowed to be empty - bool empty() { return ptr == NULL; } + bool empty() const + { return ptr == NULL; } }; /** A list of references to other records. These are read as a list, From 7e8c146de629b387d71a9eb5406f9e43a4640c31 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 11 Jul 2012 06:39:03 -0700 Subject: [PATCH 105/688] Constify some RecordListT methods --- components/nif/record_ptr.hpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/components/nif/record_ptr.hpp b/components/nif/record_ptr.hpp index 7e6a43f53..8f8fc244a 100644 --- a/components/nif/record_ptr.hpp +++ b/components/nif/record_ptr.hpp @@ -114,17 +114,13 @@ public: list[i].post(nif); } - X& operator[](size_t index) - { - return list.at(index).get(); - } + X& operator[](size_t index) const + { return list.at(index).get(); } - bool has(size_t index) - { - return !list.at(index).empty(); - } + bool has(size_t index) const + { return !list.at(index).empty(); } - size_t length() + size_t length() const { return list.size(); } }; From 645b507ba06a34be6aaa9a75eb1a6b2cc409da48 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 05:37:56 -0700 Subject: [PATCH 106/688] Return a reference to the RecordPtr from operator[] for consistency RecordListT is supposed to be a list of RecordPtrT objects. --- components/nif/nif_file.cpp | 4 ++-- components/nif/node.hpp | 4 ++-- components/nif/record_ptr.hpp | 7 ++----- components/nifbullet/bullet_nif_loader.cpp | 12 ++++++------ components/nifogre/ogre_nif_loader.cpp | 18 ++++++++---------- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 36badbf0d..231349302 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -205,8 +205,8 @@ void NiSkinInstance::post(NIFFile *nif) for(size_t i=0; ifail("Oops: Missing bone! Don't know how to handle this."); - bones[i].makeBone(i, data->bones[i]); + bones[i]->makeBone(i, data->bones[i]); } } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 64ef1e3e9..e7ba03276 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -151,8 +151,8 @@ struct NiNode : Node for(size_t i = 0;i < children.length();i++) { // Why would a unique list of children contain empty refs? - if(children.has(i)) - children[i].parent = this; + if(!children[i].empty()) + children[i]->parent = this; } } }; diff --git a/components/nif/record_ptr.hpp b/components/nif/record_ptr.hpp index 8f8fc244a..ef5bb1dee 100644 --- a/components/nif/record_ptr.hpp +++ b/components/nif/record_ptr.hpp @@ -114,11 +114,8 @@ public: list[i].post(nif); } - X& operator[](size_t index) const - { return list.at(index).get(); } - - bool has(size_t index) const - { return !list.at(index).empty(); } + const Ptr& operator[](size_t index) const + { return list.at(index); } size_t length() const { return list.size(); } diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 4105c4c79..bfcaf36e5 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -138,9 +138,9 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) int n = list.length(); for (int i=0; itrafo,hasCollisionNode,isCollisionNode,raycastingOnly); + handleNode(list[i].getPtr(), flags,&node->trafo,hasCollisionNode,isCollisionNode,raycastingOnly); } } } @@ -239,8 +239,8 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, int n = list.length(); for (int i=0; itrafo, hasCollisionNode,true,raycastingOnly); + if (!list[i].empty()) + handleNode(list[i].getPtr(), flags,&node->trafo, hasCollisionNode,true,raycastingOnly); } } } diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index aca1966b9..643d7acf8 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -649,10 +649,9 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou for (int i=0; irecType == RC_NiTexturingProperty) t = static_cast(pr); else if (pr->recType == RC_NiMaterialProperty) @@ -803,13 +802,13 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou { if(mSkel.isNull()) { - std::cout << "No skeleton for :" << shape->skin->bones[boneIndex].name << std::endl; + std::cout << "No skeleton for :" << shape->skin->bones[boneIndex]->name << std::endl; break; } //get the bone from bones array of skindata - if(!mSkel->hasBone(shape->skin->bones[boneIndex].name)) + if(!mSkel->hasBone(shape->skin->bones[boneIndex]->name)) std::cout << "We don't have this bone"; - bonePtr = mSkel->getBone(shape->skin->bones[boneIndex].name); + bonePtr = mSkel->getBone(shape->skin->bones[boneIndex]->name); // final_vector = old_vector + old_rotation*new_vector*old_scale @@ -817,7 +816,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou Nif::NiSkinData::BoneInfoCopy boneinfocopy; boneinfocopy.trafo.rotation = it->trafo.rotation; boneinfocopy.trafo.trans = it->trafo.trans; - boneinfocopy.bonename = shape->skin->bones[boneIndex].name; + boneinfocopy.bonename = shape->skin->bones[boneIndex]->name; boneinfocopy.bonehandle = bonePtr->getHandle(); copy.boneinfo.push_back(boneinfocopy); for (unsigned int i=0; iweights.size(); i++) @@ -1138,9 +1137,8 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, int n = list.length(); for (int i = 0; itrafo, bounds, bone, boneSequence); + if (!list[i].empty()) + handleNode(list[i].getPtr(), flags, &node->trafo, bounds, bone, boneSequence); } } else if (node->recType == RC_NiTriShape && bNiTri) From b7b9f1133305ad05450b3c8e5ed83c523075ebb7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 06:47:38 -0700 Subject: [PATCH 107/688] Add generic classes to help deal with NIF keys --- components/nif/nif_file.hpp | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index a21882c6d..9e0f6972b 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -98,6 +100,12 @@ public: throw std::runtime_error(err); } + void warn(const std::string &msg) + { + std::cerr<< "NIFFile Warning: "< +struct KeyT { + float mTime; + T mValue; + T mForwardValue; // Only for Quadratic interpolation + T mBackwardValue; // Only for Quadratic interpolation + float mTension; // Only for TBC interpolation + float mBias; // Only for TBC interpolation + float mContinuity; // Only for TBC interpolation +}; +typedef KeyT FloatKey; +typedef KeyT Vector3Key; +typedef KeyT QuaternionKey; + +template +struct KeyListT { + typedef std::vector< KeyT > VecType; + + static const int sLinearInterpolation = 1; + static const int sQuadraticInterpolation = 2; + static const int sTBCInterpolation = 3; + + int mInterpolationType; + VecType mKeys; + + void read(NIFFile *nif) + { + size_t count = nif->getInt(); + if(count == 0) return; + + mInterpolationType = nif->getInt(); + mKeys.resize(count); + if(mInterpolationType == sLinearInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + } + } + else if(mInterpolationType == sQuadraticInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + key.mForwardValue = (nif->*getValue)(); + key.mBackwardValue = (nif->*getValue)(); + } + } + else if(mInterpolationType == sTBCInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + key.mTension = nif->getFloat(); + key.mBias = nif->getFloat(); + key.mContinuity = nif->getFloat(); + } + } + else + nif->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); + } +}; +typedef KeyListT FloatKeyList; +typedef KeyListT Vector3KeyList; +typedef KeyListT QuaternionKeyList; + } // Namespace #endif From b292665de90d444d69ea9af0750cb117db233859 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 08:00:26 -0700 Subject: [PATCH 108/688] Use key lists to store some NIF data types --- components/nif/data.hpp | 47 +++++++++-------------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index ad670bc5e..552e53b68 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -202,61 +202,34 @@ public: class NiPosData : public Record { public: + Vector3KeyList mKeyList; + void read(NIFFile *nif) { - int count = nif->getInt(); - int type = nif->getInt(); - if(type != 1 && type != 2) - nif->fail("Cannot handle NiPosData type"); - - // TODO: Could make structs of these. Seems to be identical to - // translation in NiKeyframeData. - for(int i=0; igetFloat(); - nif->getVector3(); // This isn't really shared between type 1 - // and type 2, most likely - if(type == 2) - { - nif->getVector3(); - nif->getVector3(); - } - } + mKeyList.read(nif); } }; class NiUVData : public Record { public: + FloatKeyList mKeyList[4]; + void read(NIFFile *nif) { - // TODO: This is claimed to be a "float animation key", which is - // also used in FloatData and KeyframeData. We could probably - // reuse and refactor a lot of this if we actually use it at some - // point. - for(int i=0; i<2; i++) - { - int count = nif->getInt(); - if(count) - { - nif->getInt(); // always 2 - nif->skip(count * (sizeof(float) + 3*sizeof(float))); // Really one time float + one vector - } - } - // Always 0 - nif->getInt(); - nif->getInt(); + for(int i = 0;i < 4;i++) + mKeyList[i].read(nif); } }; class NiFloatData : public Record { public: + FloatKeyList mKeyList; + void read(NIFFile *nif) { - int count = nif->getInt(); - nif->getInt(); // always 2 - nif->skip(count * (sizeof(float) + 3*sizeof(float))); // Really one time float + one vector + mKeyList.read(nif); } }; From 86b37c6c11b59ff7a9c6bf7c214b12ef9c53eb66 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 11:21:52 -0700 Subject: [PATCH 109/688] Move the velocity out of the transformation object --- components/nif/nif_file.hpp | 1 - components/nif/nif_types.hpp | 3 +-- components/nif/node.hpp | 2 ++ components/nifbullet/bullet_nif_loader.cpp | 1 - components/nifogre/ogre_nif_loader.cpp | 1 - 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 9e0f6972b..fa152f14d 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -179,7 +179,6 @@ public: t.pos = getVector3(); t.rotation = getMatrix3(); t.scale = getFloat(); - t.velocity = getVector3(); return t; } diff --git a/components/nif/nif_types.hpp b/components/nif/nif_types.hpp index f364cd2d4..a5fb61361 100644 --- a/components/nif/nif_types.hpp +++ b/components/nif/nif_types.hpp @@ -37,12 +37,11 @@ struct Transformation Ogre::Vector3 pos; Ogre::Matrix3 rotation; float scale; - Ogre::Vector3 velocity; static const Transformation& getIdentity() { static const Transformation identity = { - Ogre::Vector3::ZERO, Ogre::Matrix3::IDENTITY, 1.0f, Ogre::Vector3::ZERO + Ogre::Vector3::ZERO, Ogre::Matrix3::IDENTITY, 1.0f }; return identity; } diff --git a/components/nif/node.hpp b/components/nif/node.hpp index e7ba03276..293793009 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -43,6 +43,7 @@ public: // Node flags. Interpretation depends somewhat on the type of node. int flags; Transformation trafo; + Ogre::Vector3 velocity; // Unused? Might be a run-time game state PropertyList props; // Bounding box info @@ -57,6 +58,7 @@ public: flags = nif->getUShort(); trafo = nif->getTrafo(); + velocity = nif->getVector3(); props.read(nif); hasBounds = !!nif->getInt(); diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index bfcaf36e5..c3b34e039 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -204,7 +204,6 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // For both position and rotation we have that: // final_vector = old_vector + old_rotation*new_vector*old_scale final.pos = trafo->pos + trafo->rotation*final.pos*trafo->scale; - final.velocity = trafo->velocity + trafo->rotation*final.velocity*trafo->scale; // Merge the rotations together final.rotation = trafo->rotation * final.rotation; diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 643d7acf8..36c5d3b1b 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1121,7 +1121,6 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, // For both position and rotation we have that: // final_vector = old_vector + old_rotation*new_vector*old_scale final.pos = trafo->pos + trafo->rotation*final.pos*trafo->scale; - final.velocity = trafo->velocity + trafo->rotation*final.velocity*trafo->scale; // Merge the rotations together final.rotation = trafo->rotation * final.rotation; From 3f11b6b1ae961cc745a8624904f1f728b7aea246 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 12:01:11 -0700 Subject: [PATCH 110/688] Cleanup a couple unneeded misc component references --- components/nif/record.hpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 84f253eb8..5c4141fa9 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -24,7 +24,7 @@ #ifndef _NIF_RECORD_H_ #define _NIF_RECORD_H_ -#include +#include namespace Nif { diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 36c5d3b1b..014384dd4 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -40,7 +40,6 @@ typedef unsigned char ubyte; using namespace std; using namespace Nif; -using namespace Misc; using namespace NifOgre; NIFLoader& NIFLoader::getSingleton() From 9995dff943592d85b2d3b9e2bbeaa75a74807dfd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 14:33:50 -0700 Subject: [PATCH 111/688] Use a key list for NiColorData --- components/nif/data.hpp | 12 ++---------- components/nif/nif_file.hpp | 2 ++ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 552e53b68..b7e2f172f 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -275,19 +275,11 @@ public: class NiColorData : public Record { public: - struct ColorData - { - float time; - Ogre::Vector4 rgba; - }; + Vector4KeyList mKeyList; void read(NIFFile *nif) { - int count = nif->getInt(); - nif->getInt(); // always 1 - - // Skip the data - nif->skip(count * 5*sizeof(float)); + mKeyList.read(nif); } }; diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index fa152f14d..c33790742 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -223,6 +223,7 @@ struct KeyT { }; typedef KeyT FloatKey; typedef KeyT Vector3Key; +typedef KeyT Vector4Key; typedef KeyT QuaternionKey; template @@ -281,6 +282,7 @@ struct KeyListT { }; typedef KeyListT FloatKeyList; typedef KeyListT Vector3KeyList; +typedef KeyListT Vector4KeyList; typedef KeyListT QuaternionKeyList; } // Namespace From 386ac56bdab827ef604362787d232d8278ff1d7e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 20:12:18 -0700 Subject: [PATCH 112/688] Remove the NIF loader and code to manually transform the vertices This currently breaks just about everything. They should come back as it's all reimplemented, though. --- apps/openmw/engine.cpp | 7 +- apps/openmw/mwrender/animation.cpp | 660 +++------- apps/openmw/mwrender/animation.hpp | 53 +- apps/openmw/mwrender/creatureanimation.cpp | 56 +- apps/openmw/mwrender/npcanimation.cpp | 1294 +++++++++----------- apps/openmw/mwrender/npcanimation.hpp | 94 +- components/bsa/bsa_archive.cpp | 54 +- components/nif/data.hpp | 20 - components/nif/node.hpp | 36 - components/nifbullet/bullet_nif_loader.cpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 1112 +---------------- components/nifogre/ogre_nif_loader.hpp | 98 +- libs/openengine/bullet/physic.cpp | 2 +- 13 files changed, 873 insertions(+), 2615 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 45b4ab514..fded32560 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -75,11 +75,8 @@ void OMW::Engine::executeLocalScripts() localScripts.setIgnore (MWWorld::Ptr()); } -void OMW::Engine::setAnimationVerbose(bool animverbose){ - if(animverbose){ - NifOgre::NIFLoader::getSingletonPtr()->setOutputAnimFiles(true); - NifOgre::NIFLoader::getSingletonPtr()->setVerbosePath(mCfgMgr.getLogPath().string()); - } +void OMW::Engine::setAnimationVerbose(bool animverbose) +{ } bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f0a6ab683..7e50706f9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -6,498 +6,236 @@ #include #include -namespace MWRender{ - std::map Animation::mUniqueIDs; +namespace MWRender { - Animation::Animation(OEngine::Render::OgreRenderer& _rend) - : insert(NULL) - , mRend(_rend) - , vecRotPos() - , time(0.0f) - , startTime(0.0f) - , stopTime(0.0f) - , animate(0) - , rindexI() - , tindexI() - , shapeNumber(0) - , shapeIndexI() - , shapes(NULL) - , transformations(NULL) - , textmappings(NULL) - , base(NULL) - { - } +std::map Animation::mUniqueIDs; - Animation::~Animation() - { - } - - std::string Animation::getUniqueID(std::string mesh){ - int counter; - std::string copy = mesh; - std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower); - if(mUniqueIDs.find(copy) == mUniqueIDs.end()){ - counter = mUniqueIDs[copy] = 0; - } - else{ - mUniqueIDs[copy] = mUniqueIDs[copy] + 1; - counter = mUniqueIDs[copy]; - } - - std::stringstream out; - if(counter > 99 && counter < 1000) - out << "0"; - else if(counter > 9) - out << "00"; - else - out << "000"; - out << counter; - return out.str(); +Animation::Animation(OEngine::Render::OgreRenderer& _rend) + : insert(NULL) + , mRend(_rend) + , time(0.0f) + , startTime(0.0f) + , stopTime(0.0f) + , animate(0) + , rindexI() + , tindexI() + , shapeNumber(0) + , shapeIndexI() + , transformations(NULL) + , textmappings(NULL) + , base(NULL) +{ } - void Animation::startScript(std::string groupname, int mode, int loops){ - //If groupname is recognized set animate to true - //Set the start time and stop time - //How many times to loop - if(groupname == "all"){ - animate = loops; - time = startTime; - } - else if(textmappings){ - std::string startName = groupname + ": loop start"; - std::string stopName = groupname + ": loop stop"; +Animation::~Animation() +{ +} - bool first = false; - - if(loops > 1){ - startName = groupname + ": loop start"; - stopName = groupname + ": loop stop"; - - for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++){ - - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); - - if(current == startName){ - startTime = iter->second; - animate = loops; - time = startTime; - first = true; - } - if(current2 == stopName){ - stopTime = iter->second; - if(first) - break; - } - } - } - if(!first){ - startName = groupname + ": start"; - stopName = groupname + ": stop"; - - for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++){ - - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); - - if(current == startName){ - startTime = iter->second; - animate = loops; - time = startTime; - first = true; - } - if(current2 == stopName){ - stopTime = iter->second; - if(first) - break; - } - } - } - - } - - } - void Animation::stopScript(){ - animate = 0; +void Animation::startScript(std::string groupname, int mode, int loops) +{ + //If groupname is recognized set animate to true + //Set the start time and stop time + //How many times to loop + if(groupname == "all") + { + animate = loops; + time = startTime; } + else if(textmappings) + { + std::string startName = groupname + ": loop start"; + std::string stopName = groupname + ": loop stop"; - void Animation::handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){ - shapeNumber = 0; + bool first = false; - if (allshapes == NULL || creaturemodel == NULL || skel == NULL) + if(loops > 1) { - return; + startName = groupname + ": loop start"; + stopName = groupname + ": loop stop"; + + for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++) + { + std::string current = iter->first.substr(0, startName.size()); + std::transform(current.begin(), current.end(), current.begin(), ::tolower); + std::string current2 = iter->first.substr(0, stopName.size()); + std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); + + if(current == startName) + { + startTime = iter->second; + animate = loops; + time = startTime; + first = true; + } + if(current2 == stopName) + { + stopTime = iter->second; + if(first) + break; + } + } } - std::vector::iterator allshapesiter; - for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++) + if(!first) + { + startName = groupname + ": start"; + stopName = groupname + ": stop"; + for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++) + { + std::string current = iter->first.substr(0, startName.size()); + std::transform(current.begin(), current.end(), current.begin(), ::tolower); + std::string current2 = iter->first.substr(0, stopName.size()); + std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); + + if(current == startName) { - //std::map vecPosRot; - - Nif::NiTriShapeCopy& copy = *allshapesiter; - std::vector* allvertices = ©.vertices; - - - - //std::set vertices; - //std::set normals; - //std::vector boneinfovector = copy.boneinfo; - std::map >* verticesToChange = ©.vertsToWeights; - - //std::cout << "Name " << copy.sname << "\n"; - Ogre::HardwareVertexBufferSharedPtr vbuf = creaturemodel->getMesh()->getSubMesh(copy.sname)->vertexData->vertexBufferBinding->getBuffer(0); - Ogre::Real* pReal = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL)); - - - std::vector initialVertices = copy.morph.getInitialVertices(); - //Each shape has multiple indices - if(initialVertices.size() ) - { - - if(copy.vertices.size() == initialVertices.size()) - { - //Create if it doesn't already exist - if(shapeIndexI.size() == static_cast (shapeNumber)) - { - std::vector vec; - shapeIndexI.push_back(vec); - } - if(time >= copy.morph.getStartTime() && time <= copy.morph.getStopTime()){ - float x; - for (unsigned int i = 0; i < copy.morph.getAdditionalVertices().size(); i++){ - int j = 0; - if(shapeIndexI[shapeNumber].size() <= i) - shapeIndexI[shapeNumber].push_back(0); - - - if(timeIndex(time,copy.morph.getRelevantTimes()[i],(shapeIndexI[shapeNumber])[i], j, x)){ - int indexI = (shapeIndexI[shapeNumber])[i]; - std::vector relevantData = (copy.morph.getRelevantData()[i]); - float v1 = relevantData[indexI].x; - float v2 = relevantData[j].x; - float t = v1 + (v2 - v1) * x; - if ( t < 0 ) t = 0; - if ( t > 1 ) t = 1; - if( t != 0 && initialVertices.size() == copy.morph.getAdditionalVertices()[i].size()) - { - for (unsigned int v = 0; v < initialVertices.size(); v++){ - initialVertices[v] += ((copy.morph.getAdditionalVertices()[i])[v]) * t; - } - } - - } - - - - } - - allvertices = &initialVertices; - } - shapeNumber++; - } - } - - - if(verticesToChange->size() > 0){ - - for(std::map >::iterator iter = verticesToChange->begin(); - iter != verticesToChange->end(); iter++) - { - std::vector inds = iter->second; - int verIndex = iter->first; - Ogre::Vector3 currentVertex = (*allvertices)[verIndex]; - Nif::NiSkinData::BoneInfoCopy* boneinfocopy = &(allshapesiter->boneinfo[inds[0].boneinfocopyindex]); - Ogre::Bone *bonePtr = 0; - - - - Ogre::Vector3 vecPos; - Ogre::Quaternion vecRot; - std::map::iterator result = vecRotPos.find(boneinfocopy); - - if(result == vecRotPos.end()){ - bonePtr = skel->getBone(boneinfocopy->bonename); - - vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; - vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; - - - PosAndRot both; - both.vecPos = vecPos; - both.vecRot = vecRot; - vecRotPos[boneinfocopy] = both; - - } - else{ - PosAndRot both = result->second; - vecPos = both.vecPos; - vecRot = both.vecRot; - } - - Ogre::Vector3 absVertPos = (vecPos + vecRot * currentVertex) * inds[0].weight; - - - - for(std::size_t i = 1; i < inds.size(); i++){ - boneinfocopy = &(allshapesiter->boneinfo[inds[i].boneinfocopyindex]); - result = vecRotPos.find(boneinfocopy); - - - if(result == vecRotPos.end()){ - bonePtr = skel->getBone(boneinfocopy->bonename); - vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; - vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; - - PosAndRot both; - both.vecPos = vecPos; - both.vecRot = vecRot; - vecRotPos[boneinfocopy] = both; - - } - else{ - PosAndRot both = result->second; - vecPos = both.vecPos; - vecRot = both.vecRot; - } - - - absVertPos += (vecPos + vecRot * currentVertex) * inds[i].weight; - - - } - Ogre::Real* addr = (pReal + 3 * verIndex); - *addr = absVertPos.x; - *(addr+1) = absVertPos.y; - *(addr+2) = absVertPos.z; - + startTime = iter->second; + animate = loops; + time = startTime; + first = true; } - - - - - } - else - { - //Ogre::Bone *bonePtr = creaturemodel->getSkeleton()->getBone(copy.bonename); - Ogre::Quaternion shaperot = copy.trafo.rotation; - Ogre::Vector3 shapetrans = copy.trafo.trans; - float shapescale = copy.trafo.scale; - std::vector boneSequence = copy.boneSequence; - - Ogre::Vector3 transmult; - Ogre::Quaternion rotmult; - float scale; - if(boneSequence.size() > 0){ - std::vector::iterator boneSequenceIter = boneSequence.begin(); - if(skel->hasBone(*boneSequenceIter)){ - Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); - - - - - transmult = bonePtr->getPosition(); - rotmult = bonePtr->getOrientation(); - scale = bonePtr->getScale().x; - boneSequenceIter++; - - for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) - { - if(skel->hasBone(*boneSequenceIter)){ - Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); - // Computes C = B + AxC*scale - transmult = transmult + rotmult * bonePtr->getPosition(); - rotmult = rotmult * bonePtr->getOrientation(); - scale = scale * bonePtr->getScale().x; - } - //std::cout << "Bone:" << *boneSequenceIter << " "; - } - transmult = transmult + rotmult * shapetrans; - rotmult = rotmult * shaperot; - scale = shapescale * scale; - - //std::cout << "Position: " << transmult << "Rotation: " << rotmult << "\n"; - } - } - else - { - transmult = shapetrans; - rotmult = shaperot; - scale = shapescale; - } - - - - - // Computes C = B + AxC*scale - // final_vector = old_vector + old_rotation*new_vector*old_scale/ - - for(unsigned int i = 0; i < allvertices->size(); i++){ - Ogre::Vector3 current = transmult + rotmult * (*allvertices)[i]; - Ogre::Real* addr = pReal + i * 3; - *addr = current.x; - *(addr+1) = current.y; - *(addr + 2) = current.z; - - }/* - for(int i = 0; i < allnormals.size(); i++){ - Ogre::Vector3 current =rotmult * allnormals[i]; - Ogre::Real* addr = pRealNormal + i * 3; - *addr = current.x; - *(addr+1) = current.y; - *(addr + 2) = current.z; - - }*/ - - } - vbuf->unlock(); - - } + if(current2 == stopName) + { + stopTime = iter->second; + if(first) + break; + } + } + } } - bool Animation::timeIndex( float time, const std::vector & times, int & i, int & j, float & x ){ - int count; - if ( (count = times.size()) > 0 ) - { - if ( time <= times[0] ) - { - i = j = 0; - x = 0.0; - return true; - } - if ( time >= times[count - 1] ) - { - i = j = count - 1; - x = 0.0; - return true; - } - - if ( i < 0 || i >= count ) - i = 0; - - float tI = times[i]; - if ( time > tI ) - { - j = i + 1; - float tJ; - while ( time >= ( tJ = times[j]) ) - { - i = j++; - tI = tJ; - } - x = ( time - tI ) / ( tJ - tI ); - return true; - } - else if ( time < tI ) - { - j = i - 1; - float tJ; - while ( time <= ( tJ = times[j] ) ) - { - i = j--; - tI = tJ; - } - x = ( time - tI ) / ( tJ - tI ); - return true; - } - else - { - j = i; - x = 0.0; - return true; - } - } - else - return false; } - void Animation::handleAnimationTransforms(){ + +void Animation::stopScript() +{ + animate = 0; +} +bool Animation::timeIndex(float time, const std::vector ×, int &i, int &j, float &x) +{ + size_t count; + if((count=times.size()) == 0) + return false; + + if(time <= times[0]) + { + i = j = 0; + x = 0.0; + return true; + } + if(time >= times[count-1]) + { + i = j = count - 1; + x = 0.0; + return true; + } + + if(i < 0 || (size_t)i >= count) + i = 0; + + float tI = times[i]; + if(time > tI) + { + j = i + 1; + float tJ; + while(time >= (tJ=times[j])) + { + i = j++; + tI = tJ; + } + x = (time-tI) / (tJ-tI); + return true; + } + + if(time < tI) + { + j = i - 1; + float tJ; + while(time <= (tJ=times[j])) + { + i = j--; + tI = tJ; + } + x = (time-tI) / (tJ-tI); + return true; + } + + j = i; + x = 0.0; + return true; +} + +void Animation::handleAnimationTransforms() +{ Ogre::SkeletonInstance* skel = base->getSkeleton(); - Ogre::Bone* b = skel->getRootBone(); - b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3)); //This is a trick - - skel->_updateTransforms(); - //skel->_notifyManualBonesDirty(); - - base->getAllAnimationStates()->_notifyDirty(); - //base->_updateAnimation(); - //base->_notifyMoved(); - + b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3)); //This is a trick + skel->_updateTransforms(); + //skel->_notifyManualBonesDirty(); + base->getAllAnimationStates()->_notifyDirty(); + //base->_updateAnimation(); + //base->_notifyMoved(); std::vector::iterator iter; int slot = 0; - if(transformations){ - for(iter = transformations->begin(); iter != transformations->end(); iter++){ - if(time < iter->getStartTime() || time < startTime || time > iter->getStopTime()) - { + if(transformations) + { + for(iter = transformations->begin(); iter != transformations->end(); iter++) + { + if(time < iter->getStartTime() || time < startTime || time > iter->getStopTime()) + { + slot++; + continue; + } + + float x; + float x2; + + const std::vector &quats = iter->getQuat(); + const std::vector &ttime = iter->gettTime(); + const std::vector &rtime = iter->getrTime(); + const std::vector &translist1 = iter->getTranslist1(); + + int rindexJ = rindexI[slot]; + timeIndex(time, rtime, rindexI[slot], rindexJ, x2); + + int tindexJ = tindexI[slot]; + timeIndex(time, ttime, tindexI[slot], tindexJ, x); + + Ogre::Vector3 t; + Ogre::Quaternion r; + + bool bTrans = translist1.size() > 0; + bool bQuats = quats.size() > 0; + if(skel->hasBone(iter->getBonename())) + { + Ogre::Bone* bone = skel->getBone(iter->getBonename()); + if(bTrans) + { + Ogre::Vector3 v1 = translist1[tindexI[slot]]; + Ogre::Vector3 v2 = translist1[tindexJ]; + t = (v1 + (v2 - v1) * x); + bone->setPosition(t); + } + if(bQuats) + { + r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); + bone->setOrientation(r); + } + } + slot++; - continue; - } - - float x; - float x2; - - const std::vector & quats = iter->getQuat(); - - const std::vector & ttime = iter->gettTime(); - - - const std::vector & rtime = iter->getrTime(); - int rindexJ = rindexI[slot]; - - timeIndex(time, rtime, rindexI[slot], rindexJ, x2); - int tindexJ = tindexI[slot]; - - - const std::vector & translist1 = iter->getTranslist1(); - - timeIndex(time, ttime, tindexI[slot], tindexJ, x); - - Ogre::Vector3 t; - Ogre::Quaternion r; - - bool bTrans = translist1.size() > 0; - - - bool bQuats = quats.size() > 0; - - if(skel->hasBone(iter->getBonename())){ - Ogre::Bone* bone = skel->getBone(iter->getBonename()); - if(bTrans){ - Ogre::Vector3 v1 = translist1[tindexI[slot]]; - Ogre::Vector3 v2 = translist1[tindexJ]; - t = (v1 + (v2 - v1) * x); - bone->setPosition(t); - - } - if(bQuats){ - r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); - bone->setOrientation(r); - } - - - - - - } - - - slot++; - } - skel->_updateTransforms(); + } + skel->_updateTransforms(); base->getAllAnimationStates()->_notifyDirty(); -} + } } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 4ab60cff4..9d5404305 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -12,60 +12,47 @@ -namespace MWRender{ +namespace MWRender { -struct PosAndRot{ +struct PosAndRot { Ogre::Quaternion vecRot; Ogre::Vector3 vecPos; }; -class Animation{ - - protected: +class Animation { +protected: Ogre::SceneNode* insert; OEngine::Render::OgreRenderer &mRend; - std::map vecRotPos; static std::map mUniqueIDs; - - - - float time; - float startTime; - float stopTime; - int animate; - //Represents a rotation index for each bone - std::vectorrindexI; + float startTime; + float stopTime; + int animate; + //Represents a rotation index for each bone + std::vectorrindexI; //Represents a translation index for each bone - std::vectortindexI; - - //Only shapes with morphing data will use a shape number - int shapeNumber; - std::vector > shapeIndexI; - - //Ogre::SkeletonInstance* skel; - std::vector* shapes; //All the NiTriShapeData for a creature - + std::vectortindexI; + //Only shapes with morphing data will use a shape number + int shapeNumber; + std::vector > shapeIndexI; std::vector* transformations; std::map* textmappings; Ogre::Entity* base; - void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); void handleAnimationTransforms(); bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); - std::string getUniqueID(std::string mesh); - public: - Animation(OEngine::Render::OgreRenderer& _rend); - virtual void runAnimation(float timepassed) = 0; - void startScript(std::string groupname, int mode, int loops); - void stopScript(); +public: + Animation(OEngine::Render::OgreRenderer& _rend); + virtual void runAnimation(float timepassed) = 0; + void startScript(std::string groupname, int mode, int loops); + void stopScript(); - virtual ~Animation(); - + virtual ~Animation(); }; + } #endif diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index e1fa7868c..6bdd58ac3 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -12,20 +12,22 @@ using namespace Ogre; using namespace NifOgre; namespace MWRender{ -CreatureAnimation::~CreatureAnimation(){ - +CreatureAnimation::~CreatureAnimation() +{ } -CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend){ + +CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend) +{ insert = ptr.getRefData().getBaseNode(); - MWWorld::LiveCellRef *ref = - ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); - if(!ref->base->model.empty()){ - const std::string &mesh = "meshes\\" + ref->base->model; - std::string meshNumbered = mesh + getUniqueID(mesh) + ">|"; - NifOgre::NIFLoader::load(meshNumbered); - base = mRend.getScene()->createEntity(meshNumbered); + if(!ref->base->model.empty()) + { + std::string mesh = "meshes\\" + ref->base->model; + + NifOgre::NIFLoader::load(mesh); + base = mRend.getScene()->createEntity(mesh); base->setVisibilityFlags(RV_Actors); bool transparent = false; @@ -48,33 +50,22 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O } base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - std::string meshZero = mesh + "0000>|"; - - if((transformations = (NIFLoader::getSingletonPtr())->getAnim(meshZero))){ - - for(std::size_t init = 0; init < transformations->size(); init++){ - rindexI.push_back(0); - tindexI.push_back(0); - } - stopTime = transformations->begin()->getStopTime(); - startTime = transformations->begin()->getStartTime(); - shapes = (NIFLoader::getSingletonPtr())->getShapes(meshZero); - } - textmappings = NIFLoader::getSingletonPtr()->getTextIndices(meshZero); insert->attachObject(base); } } -void CreatureAnimation::runAnimation(float timepassed){ - vecRotPos.clear(); - if(animate > 0){ - //Add the amount of time passed to time +void CreatureAnimation::runAnimation(float timepassed) +{ + if(animate > 0) + { + //Add the amount of time passed to time - //Handle the animation transforms dependent on time + //Handle the animation transforms dependent on time - //Handle the shapes dependent on animation transforms + //Handle the shapes dependent on animation transforms time += timepassed; - if(time >= stopTime){ + if(time >= stopTime) + { animate--; //std::cout << "Stopping the animation\n"; if(animate == 0) @@ -84,8 +75,7 @@ void CreatureAnimation::runAnimation(float timepassed){ } handleAnimationTransforms(); - handleShapes(shapes, base, base->getSkeleton()); + } +} - } -} } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index fa88b7277..fa33d18ff 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -13,13 +13,16 @@ using namespace Ogre; using namespace NifOgre; -namespace MWRender{ -NpcAnimation::~NpcAnimation(){ +namespace MWRender{ + +NpcAnimation::~NpcAnimation() +{ } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv): Animation(_rend), mStateID(-1), inv(_inv), timeToChange(0), +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv) + : Animation(_rend), mStateID(-1), inv(_inv), timeToChange(0), robe(inv.end()), helmet(inv.end()), shirt(inv.end()), cuirass(inv.end()), greaves(inv.end()), leftpauldron(inv.end()), rightpauldron(inv.end()), @@ -27,98 +30,59 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere leftglove(inv.end()), rightglove(inv.end()), skirtiter(inv.end()), pants(inv.end()), lclavicle(0), - rclavicle(0), - rupperArm(0), - lupperArm(0), - rUpperLeg(0), - lUpperLeg(0), - lForearm(0), - rForearm(0), - lWrist(0), - rWrist(0), - rKnee(0), - lKnee(0), - neck(0), - rAnkle(0), - lAnkle(0), - groin(0), - lfoot(0), - rfoot(0) + rclavicle(0), + rupperArm(0), + lupperArm(0), + rUpperLeg(0), + lUpperLeg(0), + lForearm(0), + rForearm(0), + lWrist(0), + rWrist(0), + rKnee(0), + lKnee(0), + neck(0), + rAnkle(0), + lAnkle(0), + groin(0), + lfoot(0), + rfoot(0) +{ + MWWorld::LiveCellRef *ref = ptr.get(); + + for (int init = 0; init < 27; init++) { - MWWorld::LiveCellRef *ref = - ptr.get(); - Ogre::Entity* blank = 0; - std::vector* blankshape = 0; - zero = std::make_pair(blank, blankshape); - chest = std::make_pair(blank, blankshape); - tail = std::make_pair(blank, blankshape); - lFreeFoot = std::make_pair(blank, blankshape); - rFreeFoot = std::make_pair(blank, blankshape); - rhand = std::make_pair(blank, blankshape); - lhand = std::make_pair(blank, blankshape); - skirt = std::make_pair(blank, blankshape); - for (int init = 0; init < 27; init++){ - partslots[init] = -1; //each slot is empty - partpriorities[init] = 0; - } + partslots[init] = -1; //each slot is empty + partpriorities[init] = 0; + } + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.races.find(ref->base->race); - //Part selection on last character of the file string - // " Tri Chest - // * Tri Tail - // : Tri Left Foot - // < Tri Right Foot - // > Tri Left Hand - // ? Tri Right Hand - // | Normal + std::string hairID = ref->base->hair; + std::string headID = ref->base->head; + headModel = "meshes\\" + store.bodyParts.find(headID)->model; + hairModel = "meshes\\" + store.bodyParts.find(hairID)->model; + npcName = ref->base->name; - //Mirroring Parts on second to last character - //suffix == '*' - // vector = Ogre::Vector3(-1,1,1); - // suffix == '?' - // vector = Ogre::Vector3(1,-1,1); - // suffix == '<' - // vector = Ogre::Vector3(1,1,-1); + isFemale = !!(ref->base->flags&ESM::NPC::Female); + isBeast = !!(race->data.flags&ESM::Race::Beast); + bodyRaceID = "b_n_"+race->name; + std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); - std::string hairID = ref->base->hair; - std::string headID = ref->base->head; - headModel = "meshes\\" + - MWBase::Environment::get().getWorld()->getStore().bodyParts.find(headID)->model; + /*std::cout << "Race: " << ref->base->race ; + if(female) + std::cout << " Sex: Female" << " Height: " << race->data.height.female << "\n"; + else + std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; + */ - hairModel = "meshes\\" + - MWBase::Environment::get().getWorld()->getStore().bodyParts.find(hairID)->model; - npcName = ref->base->name; - - //ESMStore::Races r = - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().races.find(ref->base->race); - - - bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); - char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2); - isFemale = tolower(secondtolast) == 'f'; - std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); - isBeast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; - - /*std::cout << "Race: " << ref->base->race ; - if(female){ - std::cout << " Sex: Female" << " Height: " << race->data.height.female << "\n"; - } - else{ - std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; - }*/ - - - - std::string smodel = "meshes\\base_anim.nif"; - if(isBeast) - smodel = "meshes\\base_animkna.nif"; - - insert = ptr.getRefData().getBaseNode(); - assert(insert); - - NifOgre::NIFLoader::load(smodel); + insert = ptr.getRefData().getBaseNode(); + assert(insert); + std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); + NifOgre::NIFLoader::load(smodel); base = mRend.getScene()->createEntity(smodel); base->setVisibilityFlags(RV_Actors); @@ -134,372 +98,290 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere while (passIt.hasMoreElements()) { Ogre::Pass* pass = passIt.getNext(); - if (pass->getDepthWriteEnabled() == false) transparent = true; } } } base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - - base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones //stay in the same place when we skipanim, or open a gui window - - - if((transformations = (NIFLoader::getSingletonPtr())->getAnim(smodel))){ - - for(unsigned int init = 0; init < transformations->size(); init++){ - rindexI.push_back(0); - tindexI.push_back(0); - } - - stopTime = transformations->begin()->getStopTime(); - startTime = transformations->begin()->getStartTime(); - } - textmappings = NIFLoader::getSingletonPtr()->getTextIndices(smodel); insert->attachObject(base); + if(isFemale) + insert->scale(race->data.height.female, race->data.height.female, race->data.height.female); + else + insert->scale(race->data.height.male, race->data.height.male, race->data.height.male); - - if(isFemale) - insert->scale(race->data.height.female, race->data.height.female, race->data.height.female); - else - insert->scale(race->data.height.male, race->data.height.male, race->data.height.male); - updateParts(); - + updateParts(); } -void NpcAnimation::updateParts(){ +void NpcAnimation::updateParts() +{ + bool apparelChanged = false; - bool apparelChanged = false; + //inv.getSlot(MWWorld::InventoryStore::Slot_Robe); + if(robe != inv.getSlot(MWWorld::InventoryStore::Slot_Robe)) + { + // A robe was added or removed + robe = inv.getSlot(MWWorld::InventoryStore::Slot_Robe); + removePartGroup(MWWorld::InventoryStore::Slot_Robe); + apparelChanged = true; + } + if(skirtiter != inv.getSlot(MWWorld::InventoryStore::Slot_Skirt)) + { + skirtiter = inv.getSlot(MWWorld::InventoryStore::Slot_Skirt); + removePartGroup(MWWorld::InventoryStore::Slot_Skirt); + apparelChanged = true; + } + if(helmet != inv.getSlot(MWWorld::InventoryStore::Slot_Helmet)) + { + helmet = inv.getSlot(MWWorld::InventoryStore::Slot_Helmet); + removePartGroup(MWWorld::InventoryStore::Slot_Helmet); + apparelChanged = true; + } + if(cuirass != inv.getSlot(MWWorld::InventoryStore::Slot_Cuirass)) + { + cuirass = inv.getSlot(MWWorld::InventoryStore::Slot_Cuirass); + removePartGroup(MWWorld::InventoryStore::Slot_Cuirass); + apparelChanged = true; + } + if(greaves != inv.getSlot(MWWorld::InventoryStore::Slot_Greaves)) + { + greaves = inv.getSlot(MWWorld::InventoryStore::Slot_Greaves); + removePartGroup(MWWorld::InventoryStore::Slot_Greaves); + apparelChanged = true; + } + if(leftpauldron != inv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron)) + { + leftpauldron = inv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron); + removePartGroup(MWWorld::InventoryStore::Slot_LeftPauldron); + apparelChanged = true; + } + if(rightpauldron != inv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron)) + { + rightpauldron = inv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron); + removePartGroup(MWWorld::InventoryStore::Slot_RightPauldron); + apparelChanged = true; + } + if(!isBeast && boots != inv.getSlot(MWWorld::InventoryStore::Slot_Boots)) + { + boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); + removePartGroup(MWWorld::InventoryStore::Slot_Boots); + apparelChanged = true; + } + if(leftglove != inv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet)) + { + leftglove = inv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet); + removePartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet); + apparelChanged = true; + } + if(rightglove != inv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet)) + { + rightglove = inv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet); + removePartGroup(MWWorld::InventoryStore::Slot_RightGauntlet); + apparelChanged = true; + } + if(shirt != inv.getSlot(MWWorld::InventoryStore::Slot_Shirt)) + { + shirt = inv.getSlot(MWWorld::InventoryStore::Slot_Shirt); + removePartGroup(MWWorld::InventoryStore::Slot_Shirt); + apparelChanged = true; + } + if(pants != inv.getSlot(MWWorld::InventoryStore::Slot_Pants)) + { + pants = inv.getSlot(MWWorld::InventoryStore::Slot_Pants); + removePartGroup(MWWorld::InventoryStore::Slot_Pants); + apparelChanged = true; + } + if(apparelChanged) + { + if(robe != inv.end()) + { + MWWorld::Ptr ptr = *robe; - //inv.getSlot(MWWorld::InventoryStore::Slot_Robe); - if(robe != inv.getSlot(MWWorld::InventoryStore::Slot_Robe)){ - //A robe was added or removed - removePartGroup(MWWorld::InventoryStore::Slot_Robe); - robe = inv.getSlot(MWWorld::InventoryStore::Slot_Robe); - apparelChanged = true; - } - if(skirtiter != inv.getSlot(MWWorld::InventoryStore::Slot_Skirt)){ - //A robe was added or removed - removePartGroup(MWWorld::InventoryStore::Slot_Skirt); - skirtiter = inv.getSlot(MWWorld::InventoryStore::Slot_Skirt); - apparelChanged = true; - } - if(helmet != inv.getSlot(MWWorld::InventoryStore::Slot_Helmet)){ - apparelChanged = true; - helmet = inv.getSlot(MWWorld::InventoryStore::Slot_Helmet); - removePartGroup(MWWorld::InventoryStore::Slot_Helmet); - + const ESM::Clothing *clothes = (ptr.get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts); + reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5); } - if(cuirass != inv.getSlot(MWWorld::InventoryStore::Slot_Cuirass)){ - cuirass = inv.getSlot(MWWorld::InventoryStore::Slot_Cuirass); - removePartGroup(MWWorld::InventoryStore::Slot_Cuirass); - apparelChanged = true; - - } - if(greaves != inv.getSlot(MWWorld::InventoryStore::Slot_Greaves)){ - greaves = inv.getSlot(MWWorld::InventoryStore::Slot_Greaves); - removePartGroup(MWWorld::InventoryStore::Slot_Greaves); - apparelChanged = true; - } - if(leftpauldron != inv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron)){ - leftpauldron = inv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_LeftPauldron); - apparelChanged = true; - - } - if(rightpauldron != inv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron)){ - rightpauldron = inv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_RightPauldron); - apparelChanged = true; - - } - if(!isBeast && boots != inv.getSlot(MWWorld::InventoryStore::Slot_Boots)){ - boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); - removePartGroup(MWWorld::InventoryStore::Slot_Boots); - apparelChanged = true; - - } - if(leftglove != inv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet)){ - leftglove = inv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet); - apparelChanged = true; - - } - if(rightglove != inv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet)){ - rightglove = inv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_RightGauntlet); - apparelChanged = true; - - } - if(shirt != inv.getSlot(MWWorld::InventoryStore::Slot_Shirt)){ - shirt = inv.getSlot(MWWorld::InventoryStore::Slot_Shirt); - removePartGroup(MWWorld::InventoryStore::Slot_Shirt); - apparelChanged = true; - - } - if(pants != inv.getSlot(MWWorld::InventoryStore::Slot_Pants)){ - pants = inv.getSlot(MWWorld::InventoryStore::Slot_Pants); - removePartGroup(MWWorld::InventoryStore::Slot_Pants); - apparelChanged = true; + if(skirtiter != inv.end()) + { + MWWorld::Ptr ptr = *skirtiter; + const ESM::Clothing *clothes = (ptr.get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts); + reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4); + reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4); + reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4); } - if(apparelChanged){ + if(helmet != inv.end()) + { + removeIndividualPart(ESM::PRT_Hair); + const ESM::Armor *armor = (helmet->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts); + } + if(cuirass != inv.end()) + { + const ESM::Armor *armor = (cuirass->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts); + } + if(greaves != inv.end()) + { + const ESM::Armor *armor = (greaves->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts); + } - if(robe != inv.end()) + if(leftpauldron != inv.end()) + { + const ESM::Armor *armor = (leftpauldron->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts); + } + if(rightpauldron != inv.end()) + { + const ESM::Armor *armor = (rightpauldron->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); + } + if(!isBeast && boots != inv.end()) + { + if(boots->getTypeName() == typeid(ESM::Clothing).name()) { - MWWorld::Ptr ptr = *robe; - - const ESM::Clothing *clothes = (ptr.get())->base; + const ESM::Clothing *clothes = (boots->get())->base; std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5); + addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts); } - if(skirtiter != inv.end()) + else if(boots->getTypeName() == typeid(ESM::Armor).name()) { - MWWorld::Ptr ptr = *skirtiter; - - const ESM::Clothing *clothes = (ptr.get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4); - } - - if(helmet != inv.end()){ - removeIndividualPart(ESM::PRT_Hair); - const ESM::Armor *armor = (helmet->get())->base; + const ESM::Armor *armor = (boots->get())->base; std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts); - - } - if(cuirass != inv.end()){ - const ESM::Armor *armor = (cuirass->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts); - - } - if(greaves != inv.end()){ - const ESM::Armor *armor = (greaves->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts); - - } - - if(leftpauldron != inv.end()){ - const ESM::Armor *armor = (leftpauldron->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts); - - } - if(rightpauldron != inv.end()){ - const ESM::Armor *armor = (rightpauldron->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); - - } - if(!isBeast && boots != inv.end()){ - if(boots->getTypeName() == typeid(ESM::Clothing).name()){ - const ESM::Clothing *clothes = (boots->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts); - } - else if(boots->getTypeName() == typeid(ESM::Armor).name()) - { - const ESM::Armor *armor = (boots->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts); - } - - } - if(leftglove != inv.end()){ - if(leftglove->getTypeName() == typeid(ESM::Clothing).name()){ - const ESM::Clothing *clothes = (leftglove->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (leftglove->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts); - } - - } - if(rightglove != inv.end()){ - if(rightglove->getTypeName() == typeid(ESM::Clothing).name()){ - const ESM::Clothing *clothes = (rightglove->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (rightglove->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts); - } - - } - - if(shirt != inv.end()){ - const ESM::Clothing *clothes = (shirt->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts); - } - if(pants != inv.end()){ - const ESM::Clothing *clothes = (pants->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts); + addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts); } } + if(leftglove != inv.end()) + { + if(leftglove->getTypeName() == typeid(ESM::Clothing).name()) + { + const ESM::Clothing *clothes = (leftglove->get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts); + } + else + { + const ESM::Armor *armor = (leftglove->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts); + } + } + if(rightglove != inv.end()) + { + if(rightglove->getTypeName() == typeid(ESM::Clothing).name()) + { + const ESM::Clothing *clothes = (rightglove->get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts); + } + else + { + const ESM::Armor *armor = (rightglove->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts); + } - if(partpriorities[ESM::PRT_Head] < 1){ - addOrReplaceIndividualPart(ESM::PRT_Head, -1,1,headModel); - } - if(partpriorities[ESM::PRT_Hair] < 1 && partpriorities[ESM::PRT_Head] <= 1){ - addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1,hairModel); - } - if(partpriorities[ESM::PRT_Neck] < 1){ - const ESM::BodyPart *neckPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "neck"); - if(neckPart) - addOrReplaceIndividualPart(ESM::PRT_Neck, -1,1,"meshes\\" + neckPart->model); - } - if(partpriorities[ESM::PRT_Cuirass] < 1){ - const ESM::BodyPart *chestPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "chest"); - if(chestPart) - addOrReplaceIndividualPart(ESM::PRT_Cuirass, -1,1,"meshes\\" + chestPart->model); - } - - if(partpriorities[ESM::PRT_Groin] < 1){ - const ESM::BodyPart *groinPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "groin"); - if(groinPart) - addOrReplaceIndividualPart(ESM::PRT_Groin, -1,1,"meshes\\" + groinPart->model); - } - if(partpriorities[ESM::PRT_RHand] < 1){ - const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); - if(!handPart) - handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); - if(handPart) - addOrReplaceIndividualPart(ESM::PRT_RHand, -1,1,"meshes\\" + handPart->model); - } - if(partpriorities[ESM::PRT_LHand] < 1){ - const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); - if(!handPart) - handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); - if(handPart) - addOrReplaceIndividualPart(ESM::PRT_LHand, -1,1,"meshes\\" + handPart->model); - } - - if(partpriorities[ESM::PRT_RWrist] < 1){ - const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); - if(wristPart) - addOrReplaceIndividualPart(ESM::PRT_RWrist, -1,1,"meshes\\" + wristPart->model); - } - if(partpriorities[ESM::PRT_LWrist] < 1){ - const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); - if(wristPart) - addOrReplaceIndividualPart(ESM::PRT_LWrist, -1,1,"meshes\\" + wristPart->model); - } - if(partpriorities[ESM::PRT_RForearm] < 1){ - const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); - if(bodyRaceID == "b_n_argonian_f_") - forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); - if(forearmPart) - addOrReplaceIndividualPart(ESM::PRT_RForearm, -1,1,"meshes\\" + forearmPart->model); - } - if(partpriorities[ESM::PRT_LForearm] < 1){ - const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); - if(bodyRaceID == "b_n_argonian_f_") - forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); - if(forearmPart) - addOrReplaceIndividualPart(ESM::PRT_LForearm, -1,1,"meshes\\" + forearmPart->model); - } - if(partpriorities[ESM::PRT_RUpperarm] < 1){ - const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); - if(armPart) - addOrReplaceIndividualPart(ESM::PRT_RUpperarm, -1,1,"meshes\\" + armPart->model); - } - if(partpriorities[ESM::PRT_LUpperarm] < 1){ - const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); - if(armPart) - addOrReplaceIndividualPart(ESM::PRT_LUpperarm, -1,1,"meshes\\" + armPart->model); - } - if(partpriorities[ESM::PRT_RFoot] < 1){ - const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); - if(isBeast && !footPart) - footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); - if(footPart) - addOrReplaceIndividualPart(ESM::PRT_RFoot, -1,1,"meshes\\" + footPart->model); - } - if(partpriorities[ESM::PRT_LFoot] < 1){ - const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); - if(isBeast && !footPart) - footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); - if(footPart) - addOrReplaceIndividualPart(ESM::PRT_LFoot, -1,1,"meshes\\" + footPart->model); - } - if(partpriorities[ESM::PRT_RAnkle] < 1){ - const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); - if(anklePart) - addOrReplaceIndividualPart(ESM::PRT_RAnkle, -1,1,"meshes\\" + anklePart->model); - } - if(partpriorities[ESM::PRT_LAnkle] < 1){ - const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); - if(anklePart) - addOrReplaceIndividualPart(ESM::PRT_LAnkle, -1,1,"meshes\\" + anklePart->model); - } - if(partpriorities[ESM::PRT_RKnee] < 1){ - const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); - if(kneePart) - addOrReplaceIndividualPart(ESM::PRT_RKnee, -1,1,"meshes\\" + kneePart->model); - } - if(partpriorities[ESM::PRT_LKnee] < 1){ - const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); - if(kneePart) - addOrReplaceIndividualPart(ESM::PRT_LKnee, -1,1,"meshes\\" + kneePart->model); - } - if(partpriorities[ESM::PRT_RLeg] < 1){ - const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); - if(legPart) - addOrReplaceIndividualPart(ESM::PRT_RLeg, -1,1,"meshes\\" + legPart->model); - } - if(partpriorities[ESM::PRT_LLeg] < 1){ - const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); - if(legPart) - addOrReplaceIndividualPart(ESM::PRT_LLeg, -1,1,"meshes\\" + legPart->model); - } - if(partpriorities[ESM::PRT_Tail] < 1){ - const ESM::BodyPart *tailPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "tail"); - if(tailPart) - addOrReplaceIndividualPart(ESM::PRT_Tail, -1,1,"meshes\\" + tailPart->model); - } - - + } + if(shirt != inv.end()) + { + const ESM::Clothing *clothes = (shirt->get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts); + } + if(pants != inv.end()) + { + const ESM::Clothing *clothes = (pants->get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts); + } + } + if(partpriorities[ESM::PRT_Head] < 1) + addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, headModel); + if(partpriorities[ESM::PRT_Hair] < 1 && partpriorities[ESM::PRT_Head] <= 1) + addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, hairModel); + static const struct { + ESM::PartReferenceType type; + const char name[2][12]; + } PartTypeList[] = { + { ESM::PRT_Neck, { "neck", "" } }, + { ESM::PRT_Cuirass, { "chest", "" } }, + { ESM::PRT_Groin, { "groin", "" } }, + { ESM::PRT_RHand, { "hand", "hands" } }, + { ESM::PRT_LHand, { "hand", "hands" } }, + { ESM::PRT_RWrist, { "wrist", "" } }, + { ESM::PRT_LWrist, { "wrist", "" } }, + { ESM::PRT_RForearm, { "forearm", "" } }, + { ESM::PRT_LForearm, { "forearm", "" } }, + { ESM::PRT_RUpperarm, { "upper arm", "" } }, + { ESM::PRT_LUpperarm, { "upper arm", "" } }, + { ESM::PRT_RFoot, { "foot", "feet" } }, + { ESM::PRT_LFoot, { "foot", "feet" } }, + { ESM::PRT_RAnkle, { "ankle", "" } }, + { ESM::PRT_LAnkle, { "ankle", "" } }, + { ESM::PRT_RKnee, { "knee", "" } }, + { ESM::PRT_LKnee, { "knee", "" } }, + { ESM::PRT_RLeg, { "upper leg", "" } }, + { ESM::PRT_LLeg, { "upper leg", "" } }, + { ESM::PRT_Tail, { "tail", "" } } + }; + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) + { + if(partpriorities[PartTypeList[i].type] < 1) + { + const ESM::BodyPart *part = NULL; + bool tryfemale = isFemale; + int ni = 0; + do { + part = store.bodyParts.search(bodyRaceID+(tryfemale?"_f_":"_m_")+PartTypeList[i].name[ni]); + if(part) break; + ni ^= 1; + if(ni == 0) + { + if(!tryfemale) + break; + tryfemale = false; + } + } while(1); + if(part) + addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->model); + } + } } -Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, std::string bonename){ +Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) +{ NIFLoader::load(mesh); Ogre::Entity* part = mRend.getScene()->createEntity(mesh); @@ -508,361 +390,275 @@ Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, std::stri base->attachObjectToBone(bonename, part); return part; } -void NpcAnimation::insertFootPart(int type, const std::string &mesh){ - std::string meshAndSuffix = mesh; - if(type == ESM::PRT_LFoot) - meshAndSuffix += "*|"; - NIFLoader::load(meshAndSuffix); - Ogre::Entity* part = mRend.getScene()->createEntity(meshAndSuffix); - std::vector* shape = ((NIFLoader::getSingletonPtr())->getShapes(meshAndSuffix)); - if(shape == 0){ - if(type == ESM::PRT_LFoot){ - base->attachObjectToBone("Left Foot", part); - lfoot = part; - } - else if (type == ESM::PRT_RFoot){ - base->attachObjectToBone("Right Foot", part); - rfoot = part; - } - } - else{ - if(type == ESM::PRT_LFoot) - lFreeFoot = insertFreePart(mesh, "::"); - else if (type == ESM::PRT_RFoot) - rFreeFoot = insertFreePart(mesh, ":<"); + +void NpcAnimation::runAnimation(float timepassed) +{ + if(timeToChange > .2) + { + timeToChange = 0; + updateParts(); } - - -} -std::pair*> NpcAnimation::insertFreePart(const std::string &mesh, const std::string& suffix){ - - std::string meshNumbered = mesh + getUniqueID(mesh + suffix) + suffix; - NIFLoader::load(meshNumbered); - - Ogre::Entity* part = mRend.getScene()->createEntity(meshNumbered); - part->setVisibilityFlags(RV_Actors); - - insert->attachObject(part); - - std::vector* shape = ((NIFLoader::getSingletonPtr())->getShapes(mesh + "0000" + suffix)); - if(shape){ - handleShapes(shape, part, base->getSkeleton()); - } - std::pair*> pair = std::make_pair(part, shape); - return pair; -} - - - - -void NpcAnimation::runAnimation(float timepassed){ - - if(timeToChange > .2){ - - timeToChange = 0; - - updateParts(); - } - - timeToChange += timepassed; + timeToChange += timepassed; //1. Add the amount of time passed to time - //2. Handle the animation transforms dependent on time + //2. Handle the animation transforms dependent on time - //3. Handle the shapes dependent on animation transforms - if(animate > 0){ + //3. Handle the shapes dependent on animation transforms + if(animate > 0) + { time += timepassed; - - if(time > stopTime){ + if(time > stopTime) + { animate--; - if(animate == 0) time = stopTime; else time = startTime + (time - stopTime); } - handleAnimationTransforms(); - - - vecRotPos.clear(); - - - if(lFreeFoot.first) - handleShapes(lFreeFoot.second, lFreeFoot.first, base->getSkeleton()); - if(rFreeFoot.first) - handleShapes(rFreeFoot.second, rFreeFoot.first, base->getSkeleton()); - - if(chest.first) - handleShapes(chest.second, chest.first, base->getSkeleton()); - if(tail.first) - handleShapes(tail.second, tail.first, base->getSkeleton()); - if(skirt.first){ - handleShapes(skirt.second, skirt.first, base->getSkeleton()); - } - if(lhand.first) - handleShapes(lhand.second, lhand.first, base->getSkeleton()); - if(rhand.first) - handleShapes(rhand.second, rhand.first, base->getSkeleton()); - -} + handleAnimationTransforms(); + } } -void NpcAnimation::removeIndividualPart(int type){ +void NpcAnimation::removeIndividualPart(int type) +{ partpriorities[type] = 0; partslots[type] = -1; - if(type == ESM::PRT_Head && head){ //0 - base->detachObjectFromBone(head); - head = 0; - } - else if(type == ESM::PRT_Hair && hair){//1 - base->detachObjectFromBone(hair); - hair = 0; - } - else if(type == ESM::PRT_Neck && neck){//2 - base->detachObjectFromBone(neck); - neck = 0; - } - else if(type == ESM::PRT_Cuirass && chest.first){//3 - insert->detachObject(chest.first); - chest = zero; - } - else if(type == ESM::PRT_Groin && groin){//4 - base->detachObjectFromBone(groin); - groin = 0; - } - else if(type == ESM::PRT_Skirt && skirt.first){//5 - insert->detachObject(skirt.first); - skirt = zero; - } - else if(type == ESM::PRT_RHand && rhand.first){//6 - insert->detachObject(rhand.first); - rhand = zero; - } - else if(type == ESM::PRT_LHand && lhand.first){//7 - insert->detachObject(lhand.first); - lhand = zero; - } - else if(type == ESM::PRT_RWrist && rWrist){//8 - base->detachObjectFromBone(rWrist); - rWrist = 0; - } - else if(type == ESM::PRT_LWrist && lWrist){//9 - base->detachObjectFromBone(lWrist); - lWrist = 0; - } - else if(type == ESM::PRT_Shield){//10 - - } - else if(type == ESM::PRT_RForearm && rForearm){//11 - base->detachObjectFromBone(rForearm); - rForearm = 0; - } - else if(type == ESM::PRT_LForearm && lForearm){//12 - base->detachObjectFromBone(lForearm); - lForearm = 0; - } - else if(type == ESM::PRT_RUpperarm && rupperArm){//13 - base->detachObjectFromBone(rupperArm); - rupperArm = 0; - } - else if(type == ESM::PRT_LUpperarm && lupperArm){//14 - base->detachObjectFromBone(lupperArm); - lupperArm = 0; - } - else if(type == ESM::PRT_RFoot){ //15 - if(rfoot){ - base->detachObjectFromBone(rfoot); - rfoot = 0; - } - else if(rFreeFoot.first){ - insert->detachObject(rFreeFoot.first); - rFreeFoot = zero; - } - } - else if(type == ESM::PRT_LFoot){ //16 - if(lfoot){ - base->detachObjectFromBone(lfoot); - lfoot = 0; - } - else if(lFreeFoot.first){ - insert->detachObject(lFreeFoot.first); - lFreeFoot = zero; - } - } - else if(type == ESM::PRT_RAnkle && rAnkle){ //17 - base->detachObjectFromBone(rAnkle); - rAnkle = 0; - } - else if(type == ESM::PRT_LAnkle && lAnkle){ //18 - base->detachObjectFromBone(lAnkle); - lAnkle = 0; - } - else if(type == ESM::PRT_RKnee && rKnee){ //19 - base->detachObjectFromBone(rKnee); - rKnee = 0; - } - else if(type == ESM::PRT_LKnee && lKnee){ //20 - base->detachObjectFromBone(lKnee); - lKnee = 0; - } - else if(type == ESM::PRT_RLeg && rUpperLeg){ //21 - base->detachObjectFromBone(rUpperLeg); - rUpperLeg = 0; - } - else if(type == ESM::PRT_LLeg && lUpperLeg){ //22 - base->detachObjectFromBone(lUpperLeg); - lUpperLeg = 0; - } - else if(type == ESM::PRT_RPauldron && rclavicle){ //23 - base->detachObjectFromBone(rclavicle); - rclavicle = 0; - } - else if(type == ESM::PRT_LPauldron && lclavicle){ //24 - base->detachObjectFromBone(lclavicle); - lclavicle = 0; - } - else if(type == ESM::PRT_Weapon){ //25 - - } - else if(type == ESM::PRT_Tail && tail.first){ //26 - insert->detachObject(tail.first); - tail = zero; - } - - - - + if(type == ESM::PRT_Head && head) //0 + { + base->detachObjectFromBone(head); + head = 0; } - - void NpcAnimation::reserveIndividualPart(int type, int group, int priority){ - if(priority > partpriorities[type]){ - removeIndividualPart(type); - partpriorities[type] = priority; - partslots[type] = group; - } + else if(type == ESM::PRT_Hair && hair) //1 + { + base->detachObjectFromBone(hair); + hair = 0; } - - void NpcAnimation::removePartGroup(int group){ - for(int i = 0; i < 27; i++){ - if(partslots[i] == group){ - removeIndividualPart(i); - } - } + else if(type == ESM::PRT_Neck && neck) //2 + { + base->detachObjectFromBone(neck); + neck = 0; } - bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh){ - if(priority > partpriorities[type]){ - removeIndividualPart(type); - partslots[type] = group; - partpriorities[type] = priority; - switch(type){ - case ESM::PRT_Head: //0 - head = insertBoundedPart(mesh, "Head"); - break; - case ESM::PRT_Hair: //1 - hair = insertBoundedPart(mesh, "Head"); - break; - case ESM::PRT_Neck: //2 - neck = insertBoundedPart(mesh, "Neck"); - break; - case ESM::PRT_Cuirass: //3 - chest = insertFreePart(mesh, ":\""); - break; - case ESM::PRT_Groin: //4 - groin = insertBoundedPart(mesh, "Groin"); - break; - case ESM::PRT_Skirt: //5 - skirt = insertFreePart(mesh, ":|"); - break; - case ESM::PRT_RHand: //6 - rhand = insertFreePart(mesh, ":?"); - break; - case ESM::PRT_LHand: //7 - lhand = insertFreePart(mesh, ":>"); - break; - case ESM::PRT_RWrist: //8 - rWrist = insertBoundedPart(mesh, "Right Wrist"); - break; - case ESM::PRT_LWrist: //9 - lWrist = insertBoundedPart(mesh + "*|", "Left Wrist"); - break; - case ESM::PRT_Shield: //10 - break; - case ESM::PRT_RForearm: //11 - rForearm = insertBoundedPart(mesh, "Right Forearm"); - break; - case ESM::PRT_LForearm: //12 - lForearm = insertBoundedPart(mesh + "*|", "Left Forearm"); - break; - case ESM::PRT_RUpperarm: //13 - rupperArm = insertBoundedPart(mesh, "Right Upper Arm"); - break; - case ESM::PRT_LUpperarm: //14 - lupperArm = insertBoundedPart(mesh + "*|", "Left Upper Arm"); - break; - case ESM::PRT_RFoot: //15 - insertFootPart(type, mesh); - break; - case ESM::PRT_LFoot: //16 - insertFootPart(type, mesh); - break; - case ESM::PRT_RAnkle: //17 - rAnkle = insertBoundedPart(mesh , "Right Ankle"); - break; - case ESM::PRT_LAnkle: //18 - lAnkle = insertBoundedPart(mesh + "*|", "Left Ankle"); - break; - case ESM::PRT_RKnee: //19 - rKnee = insertBoundedPart(mesh , "Right Knee"); - break; - case ESM::PRT_LKnee: //20 - lKnee = insertBoundedPart(mesh + "*|", "Left Knee"); - break; - case ESM::PRT_RLeg: //21 - rUpperLeg = insertBoundedPart(mesh, "Right Upper Leg"); - break; - case ESM::PRT_LLeg: //22 - lUpperLeg = insertBoundedPart(mesh + "*|", "Left Upper Leg"); - break; - case ESM::PRT_RPauldron: //23 - rclavicle = insertBoundedPart(mesh , "Right Clavicle"); - break; - case ESM::PRT_LPauldron: //24 - lclavicle = insertBoundedPart(mesh + "*|", "Left Clavicle"); - break; - case ESM::PRT_Weapon: //25 - break; - case ESM::PRT_Tail: //26 - tail = insertFreePart(mesh, ":*"); - break; - - - } - return true; - } - return false; + else if(type == ESM::PRT_Groin && groin)//4 + { + base->detachObjectFromBone(groin); + groin = 0; } - - void NpcAnimation::addPartGroup(int group, int priority, std::vector& parts){ - for(std::size_t i = 0; i < parts.size(); i++) - { - ESM::PartReference part = parts[i]; - - const ESM::BodyPart *bodypart = 0; - - if(isFemale) - bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.female); - if(!bodypart) - bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.male); - if(bodypart){ - addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); - } - else - reserveIndividualPart(part.part, group, priority); - - } + else if(type == ESM::PRT_RWrist && rWrist)//8 + { + base->detachObjectFromBone(rWrist); + rWrist = 0; + } + else if(type == ESM::PRT_LWrist && lWrist) //9 + { + base->detachObjectFromBone(lWrist); + lWrist = 0; + } + else if(type == ESM::PRT_Shield) //10 + { + } + else if(type == ESM::PRT_RForearm && rForearm) //11 + { + base->detachObjectFromBone(rForearm); + rForearm = 0; + } + else if(type == ESM::PRT_LForearm && lForearm) //12 + { + base->detachObjectFromBone(lForearm); + lForearm = 0; + } + else if(type == ESM::PRT_RUpperarm && rupperArm) //13 + { + base->detachObjectFromBone(rupperArm); + rupperArm = 0; + } + else if(type == ESM::PRT_LUpperarm && lupperArm) //14 + { + base->detachObjectFromBone(lupperArm); + lupperArm = 0; + } + else if(type == ESM::PRT_RFoot && rfoot) //15 + { + base->detachObjectFromBone(rfoot); + rfoot = 0; + } + else if(type == ESM::PRT_LFoot && lfoot) //16 + { + base->detachObjectFromBone(lfoot); + lfoot = 0; + } + else if(type == ESM::PRT_RAnkle && rAnkle) //17 + { + base->detachObjectFromBone(rAnkle); + rAnkle = 0; + } + else if(type == ESM::PRT_LAnkle && lAnkle) //18 + { + base->detachObjectFromBone(lAnkle); + lAnkle = 0; + } + else if(type == ESM::PRT_RKnee && rKnee) //19 + { + base->detachObjectFromBone(rKnee); + rKnee = 0; + } + else if(type == ESM::PRT_LKnee && lKnee) //20 + { + base->detachObjectFromBone(lKnee); + lKnee = 0; + } + else if(type == ESM::PRT_RLeg && rUpperLeg) //21 + { + base->detachObjectFromBone(rUpperLeg); + rUpperLeg = 0; + } + else if(type == ESM::PRT_LLeg && lUpperLeg) //22 + { + base->detachObjectFromBone(lUpperLeg); + lUpperLeg = 0; + } + else if(type == ESM::PRT_RPauldron && rclavicle) //23 + { + base->detachObjectFromBone(rclavicle); + rclavicle = 0; + } + else if(type == ESM::PRT_LPauldron && lclavicle) //24 + { + base->detachObjectFromBone(lclavicle); + lclavicle = 0; + } + else if(type == ESM::PRT_Weapon) //25 + { } } + +void NpcAnimation::reserveIndividualPart(int type, int group, int priority) +{ + if(priority > partpriorities[type]) + { + removeIndividualPart(type); + partpriorities[type] = priority; + partslots[type] = group; + } +} + +void NpcAnimation::removePartGroup(int group) +{ + for(int i = 0; i < 27; i++) + { + if(partslots[i] == group) + removeIndividualPart(i); + } +} + +bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh) +{ + if(priority <= partpriorities[type]) + return false; + + removeIndividualPart(type); + partslots[type] = group; + partpriorities[type] = priority; + switch(type) + { + case ESM::PRT_Head: //0 + head = insertBoundedPart(mesh, "Head"); + break; + case ESM::PRT_Hair: //1 + hair = insertBoundedPart(mesh, "Head"); + break; + case ESM::PRT_Neck: //2 + neck = insertBoundedPart(mesh, "Neck"); + break; + case ESM::PRT_Cuirass: //3 + break; + case ESM::PRT_Groin: //4 + groin = insertBoundedPart(mesh, "Groin"); + break; + case ESM::PRT_Skirt: //5 + break; + case ESM::PRT_RHand: //6 + break; + case ESM::PRT_LHand: //7 + break; + case ESM::PRT_RWrist: //8 + rWrist = insertBoundedPart(mesh, "Right Wrist"); + break; + case ESM::PRT_LWrist: //9 + lWrist = insertBoundedPart(mesh, "Left Wrist"); + break; + case ESM::PRT_Shield: //10 + break; + case ESM::PRT_RForearm: //11 + rForearm = insertBoundedPart(mesh, "Right Forearm"); + break; + case ESM::PRT_LForearm: //12 + lForearm = insertBoundedPart(mesh, "Left Forearm"); + break; + case ESM::PRT_RUpperarm: //13 + rupperArm = insertBoundedPart(mesh, "Right Upper Arm"); + break; + case ESM::PRT_LUpperarm: //14 + lupperArm = insertBoundedPart(mesh, "Left Upper Arm"); + break; + case ESM::PRT_RFoot: //15 + lupperArm = insertBoundedPart(mesh, "Right Foot"); + break; + case ESM::PRT_LFoot: //16 + lupperArm = insertBoundedPart(mesh, "Left Foot"); + break; + case ESM::PRT_RAnkle: //17 + rAnkle = insertBoundedPart(mesh, "Right Ankle"); + break; + case ESM::PRT_LAnkle: //18 + lAnkle = insertBoundedPart(mesh, "Left Ankle"); + break; + case ESM::PRT_RKnee: //19 + rKnee = insertBoundedPart(mesh, "Right Knee"); + break; + case ESM::PRT_LKnee: //20 + lKnee = insertBoundedPart(mesh, "Left Knee"); + break; + case ESM::PRT_RLeg: //21 + rUpperLeg = insertBoundedPart(mesh, "Right Upper Leg"); + break; + case ESM::PRT_LLeg: //22 + lUpperLeg = insertBoundedPart(mesh, "Left Upper Leg"); + break; + case ESM::PRT_RPauldron: //23 + rclavicle = insertBoundedPart(mesh , "Right Clavicle"); + break; + case ESM::PRT_LPauldron: //24 + lclavicle = insertBoundedPart(mesh, "Left Clavicle"); + break; + case ESM::PRT_Weapon: //25 + break; + case ESM::PRT_Tail: //26 + break; + } + return true; +} + +void NpcAnimation::addPartGroup(int group, int priority, std::vector &parts) +{ + for(std::size_t i = 0; i < parts.size(); i++) + { + ESM::PartReference &part = parts[i]; + + const ESM::BodyPart *bodypart = 0; + if(isFemale) + bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search(part.female); + if(!bodypart) + bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search(part.male); + + if(bodypart) + addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); + else + reserveIndividualPart(part.part, group, priority); + } +} + +} diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 8f4f8181d..151af4163 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -20,54 +20,43 @@ namespace MWRender{ class NpcAnimation: public Animation{ private: - MWWorld::InventoryStore& inv; - int mStateID; - //Free Parts - std::pair*> chest; - std::pair*> skirt; - std::pair*> lhand; - std::pair*> rhand; - std::pair*> tail; - std::pair*> lFreeFoot; - std::pair*> rFreeFoot; + MWWorld::InventoryStore& inv; + int mStateID; - int partslots[27]; //Each part slot is taken by clothing, armor, or is empty - int partpriorities[27]; - std::pair*> zero; + int partslots[27]; //Each part slot is taken by clothing, armor, or is empty + int partpriorities[27]; + //Bounded Parts + Ogre::Entity* lclavicle; + Ogre::Entity* rclavicle; + Ogre::Entity* rupperArm; + Ogre::Entity* lupperArm; + Ogre::Entity* rUpperLeg; + Ogre::Entity* lUpperLeg; + Ogre::Entity* lForearm; + Ogre::Entity* rForearm; + Ogre::Entity* lWrist; + Ogre::Entity* rWrist; + Ogre::Entity* rKnee; + Ogre::Entity* lKnee; + Ogre::Entity* neck; + Ogre::Entity* rAnkle; + Ogre::Entity* lAnkle; + Ogre::Entity* groin; + Ogre::Entity* lfoot; + Ogre::Entity* rfoot; + Ogre::Entity* hair; + Ogre::Entity* head; - - //Bounded Parts - Ogre::Entity* lclavicle; - Ogre::Entity* rclavicle; - Ogre::Entity* rupperArm; - Ogre::Entity* lupperArm; - Ogre::Entity* rUpperLeg; - Ogre::Entity* lUpperLeg; - Ogre::Entity* lForearm; - Ogre::Entity* rForearm; - Ogre::Entity* lWrist; - Ogre::Entity* rWrist; - Ogre::Entity* rKnee; - Ogre::Entity* lKnee; - Ogre::Entity* neck; - Ogre::Entity* rAnkle; - Ogre::Entity* lAnkle; - Ogre::Entity* groin; - Ogre::Entity* lfoot; - Ogre::Entity* rfoot; - Ogre::Entity* hair; - Ogre::Entity* head; - - Ogre::SceneNode* insert; + Ogre::SceneNode* insert; bool isBeast; bool isFemale; - std::string headModel; - std::string hairModel; - std::string npcName; - std::string bodyRaceID; - float timeToChange; - MWWorld::ContainerStoreIterator robe; + std::string headModel; + std::string hairModel; + std::string npcName; + std::string bodyRaceID; + float timeToChange; + MWWorld::ContainerStoreIterator robe; MWWorld::ContainerStoreIterator helmet; MWWorld::ContainerStoreIterator shirt; MWWorld::ContainerStoreIterator cuirass; @@ -80,22 +69,19 @@ private: MWWorld::ContainerStoreIterator rightglove; MWWorld::ContainerStoreIterator skirtiter; - public: - NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); - virtual ~NpcAnimation(); - Ogre::Entity* insertBoundedPart(const std::string &mesh, std::string bonename); - std::pair*> insertFreePart(const std::string &mesh, const std::string& suffix); - void insertFootPart(int type, const std::string &mesh); - virtual void runAnimation(float timepassed); - void updateParts(); +public: + NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); + virtual ~NpcAnimation(); + Ogre::Entity* insertBoundedPart(const std::string &mesh, const std::string &bonename); + virtual void runAnimation(float timepassed); + void updateParts(); void removeIndividualPart(int type); void reserveIndividualPart(int type, int group, int priority); bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); - void removePartGroup(int group); + void removePartGroup(int group); void addPartGroup(int group, int priority, std::vector& parts); - - }; + } #endif diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index e9ce3f615..dd12a53ff 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -64,28 +64,13 @@ static bool fsstrict = false; /// An OGRE Archive wrapping a BSAFile archive class DirArchive: public Ogre::FileSystemArchive { - boost::filesystem::path currentdir; std::map, ciLessBoost> m; unsigned int cutoff; bool findFile(const String& filename, std::string& copy) const { - { - String passed = filename; - if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); - else if(filename.at(filename.length() - 2) == '"') - passed = filename.substr(0, filename.length() - 9); - else 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); - - - copy = passed; - } - + copy = filename; std::replace(copy.begin(), copy.end(), '\\', '/'); if(copy.at(0) == '/') @@ -225,46 +210,23 @@ public: // OGRE's fault. You should NOT expect an open() command not to // have any side effects on the archive, and hence this function // should not have been declared const in the first place. - BSAFile *narc = (BSAFile*)&arc; + BSAFile *narc = const_cast(&arc); - String passed = filename; - if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); - else if(filename.at(filename.length() - 2) == '"') - passed = filename.substr(0, filename.length() - 9); - else 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); - - // Open the file - StreamPtr strm = narc->getFile(passed.c_str()); + StreamPtr strm = narc->getFile(filename.c_str()); // Wrap it into an Ogre::DataStream. return DataStreamPtr(new Mangle2OgreStream(strm)); } -bool exists(const String& filename) { - return cexists(filename); -} + bool exists(const String& filename) { + return arc.exists(filename.c_str()); + } - // Check if the file exists. bool cexists(const String& filename) const { - String passed = filename; - if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); - else if(filename.at(filename.length() - 2) == '"') - passed = filename.substr(0, filename.length() - 9); - else 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); - - + return arc.exists(filename.c_str()); + } -return arc.exists(passed.c_str()); -} time_t getModifiedTime(const String&) { return 0; } // This is never called as far as I can see. diff --git a/components/nif/data.hpp b/components/nif/data.hpp index b7e2f172f..2d1e8bd3b 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -326,12 +326,6 @@ public: Ogre::Vector3 trans; // Translation float scale; // Probably scale (always 1) }; - struct BoneTrafoCopy - { - Ogre::Quaternion rotation; - Ogre::Vector3 trans; - float scale; - }; struct VertWeight { @@ -339,26 +333,12 @@ public: float weight; }; - struct BoneInfo { BoneTrafo trafo; Ogre::Vector4 unknown; std::vector weights; }; - struct BoneInfoCopy - { - std::string bonename; - unsigned short bonehandle; - BoneTrafoCopy trafo; - Ogre::Vector4 unknown; - //std::vector weights; - }; - struct IndividualWeight - { - float weight; - unsigned int boneinfocopyindex; - }; BoneTrafo trafo; std::vector bones; diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 293793009..1f1b91a46 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -110,20 +110,6 @@ public: } }; -struct NiTriShapeCopy -{ - std::string sname; - std::vector boneSequence; - Nif::NiSkinData::BoneTrafoCopy trafo; - //Ogre::Quaternion initialBoneRotation; - //Ogre::Vector3 initialBoneTranslation; - std::vector vertices; - std::vector normals; - std::vector boneinfo; - std::map > vertsToWeights; - Nif::NiMorphData morph; -}; - struct NiNode : Node { NodeList children; @@ -184,28 +170,6 @@ struct NiTriShape : Node data.post(nif); skin.post(nif); } - - NiTriShapeCopy clone() - { - NiTriShapeCopy copy; - copy.sname = name; - float *ptr = (float*)&data->vertices[0]; - float *ptrNormals = (float*)&data->normals[0]; - int numVerts = data->vertices.size() / 3; - for(int i = 0; i < numVerts; i++) - { - float *current = (float*) (ptr + i * 3); - copy.vertices.push_back(Ogre::Vector3(*current, *(current + 1), *(current + 2))); - - if(ptrNormals) - { - float *currentNormals = (float*) (ptrNormals + i * 3); - copy.normals.push_back(Ogre::Vector3(*currentNormals, *(currentNormals + 1), *(currentNormals + 2))); - } - } - - return copy; - } }; struct NiCamera : Node diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index c3b34e039..5f562504b 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -79,7 +79,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) // of the early stages of development. Right now we WANT to catch // every error as early and intrusively as possible, as it's most // likely a sign of incomplete code rather than faulty input. - Nif::NIFFile nif(resourceName); + Nif::NIFFile nif(resourceName.substr(0, resourceName.length()-7)); if (nif.numRecords() < 1) { warn("Found no records in NIF."); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 014384dd4..883cf7fce 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -42,28 +42,6 @@ using namespace std; using namespace Nif; using namespace NifOgre; -NIFLoader& NIFLoader::getSingleton() -{ - static NIFLoader instance; - return instance; -} - -NIFLoader* NIFLoader::getSingletonPtr() -{ - return &getSingleton(); -} - -void NIFLoader::warn(string msg) -{ - std::cerr << "NIFLoader: Warn:" << msg << "\n"; -} - -void NIFLoader::fail(string msg) -{ - std::cerr << "NIFLoader: Fail: "<< msg << std::endl; - assert(1); -} - // Helper class that computes the bounding box and of a mesh class BoundsFinder @@ -149,6 +127,19 @@ public: } }; + +void NIFLoader::warn(const std::string &msg) +{ + std::cerr << "NIFLoader: Warn:" << msg << "\n"; +} + +void NIFLoader::fail(const std::string &msg) +{ + std::cerr << "NIFLoader: Fail: "<< msg << std::endl; + assert(1); +} + + // Conversion of blend / test mode from NIF -> OGRE. // Not in use yet, so let's comment it out. /* @@ -193,12 +184,7 @@ static CompareFunction getTestMode(int mode) } */ -void NIFLoader::setOutputAnimFiles(bool output){ - mOutputAnimFiles = output; -} -void NIFLoader::setVerbosePath(std::string path){ - verbosePath = path; -} +#if 0 void NIFLoader::createMaterial(const Ogre::String &name, const Ogre::Vector3 &ambient, const Ogre::Vector3 &diffuse, @@ -211,17 +197,6 @@ void NIFLoader::createMaterial(const Ogre::String &name, Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(name, resourceGroup); - //Hardware Skinning code, textures may be the wrong color if enabled - - /* if(!mSkel.isNull()){ - material->removeAllTechniques(); - - Ogre::Technique* tech = material->createTechnique(); - //tech->setSchemeName("blahblah"); - Pass* pass = tech->createPass(); - pass->setVertexProgram("Ogre/BasicVertexPrograms/AmbientOneTexture");*/ - - // This assigns the texture to this material. If the texture name is // a file name, and this file exists (in a resource directory), it // will automatically be loaded when needed. If not (such as for @@ -302,14 +277,14 @@ void NIFLoader::createMaterial(const Ogre::String &name, { bool split = Settings::Manager::getBool("split", "Shadows"); const int numsplits = 3; - for (int i = 0; i < (split ? numsplits : 1); ++i) - { + for (int i = 0; i < (split ? numsplits : 1); ++i) + { Ogre::TextureUnitState* tu = material->getTechnique(0)->getPass(0)->createTextureUnitState(); tu->setName("shadowMap" + Ogre::StringConverter::toString(i)); tu->setContentType(Ogre::TextureUnitState::CONTENT_SHADOW); tu->setTextureAddressingMode(Ogre::TextureUnitState::TAM_BORDER); tu->setTextureBorderColour(Ogre::ColourValue::White); - } + } } if (Settings::Manager::getBool("shaders", "Objects")) @@ -340,1067 +315,28 @@ void NIFLoader::createMaterial(const Ogre::String &name, material->setSelfIllumination(emissive[0], emissive[1], emissive[2]); material->setShininess(glossiness); } +#endif -// Takes a name and adds a unique part to it. This is just used to -// make sure that all materials are given unique names. -Ogre::String NIFLoader::getUniqueName(const Ogre::String &input) -{ - static int addon = 0; - static char buf[8]; - snprintf(buf, 8, "_%d", addon++); - - // Don't overflow the buffer - if (addon > 999999) addon = 0; - - return input + buf; -} - -// Check if the given texture name exists in the real world. If it -// does not, change the string IN PLACE to say .dds instead and try -// that. The texture may still not exist, but no information of value -// is lost in that case. -void NIFLoader::findRealTexture(Ogre::String &texName) -{ - if(Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) - return; - - // Change texture extension to .dds - Ogre::String::size_type pos = texName.rfind('.'); - texName.replace(pos, texName.length(), ".dds"); -} - -//Handle node at top - -// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given -// mesh. -void NIFLoader::createOgreSubMesh(NiTriShape *shape, const Ogre::String &material, std::list &vertexBoneAssignments) -{ - // cout << "s:" << shape << "\n"; - NiTriShapeData *data = shape->data.getPtr(); - Ogre::SubMesh *sub = mesh->createSubMesh(shape->name); - - int nextBuf = 0; - - // This function is just one long stream of Ogre-barf, but it works - // great. - - // Add vertices - int numVerts = data->vertices.size() / 3; - sub->vertexData = new Ogre::VertexData(); - sub->vertexData->vertexCount = numVerts; - sub->useSharedVertices = false; - - Ogre::VertexDeclaration *decl = sub->vertexData->vertexDeclaration; - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - - Ogre::HardwareVertexBufferSharedPtr vbuf = - Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - numVerts, Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, false); - - if(flip) - { - float *datamod = new float[data->vertices.size()]; - //std::cout << "Shape" << shape->name.toString() << "\n"; - for(int i = 0; i < numVerts; i++) - { - int index = i * 3; - const float *pos = &data->vertices[index]; - Ogre::Vector3 original = Ogre::Vector3(*pos ,*(pos+1), *(pos+2)); - original = mTransform * original; - mBoundingBox.merge(original); - datamod[index] = original.x; - datamod[index+1] = original.y; - datamod[index+2] = original.z; - } - vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false); - delete [] datamod; - } - else - { - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->vertices[0], false); - } - - - Ogre::VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding; - bind->setBinding(nextBuf++, vbuf); - - if (data->normals.size()) - { - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); - vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); - - if(flip) - { - Ogre::Quaternion rotation = mTransform.extractQuaternion(); - rotation.normalise(); - - float *datamod = new float[data->normals.size()]; - for(int i = 0; i < numVerts; i++) - { - int index = i * 3; - const float *pos = &data->normals[index]; - Ogre::Vector3 original = Ogre::Vector3(*pos ,*(pos+1), *(pos+2)); - original = rotation * original; - if (mNormaliseNormals) - { - original.normalise(); - } - - - datamod[index] = original.x; - datamod[index+1] = original.y; - datamod[index+2] = original.z; - } - vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false); - delete [] datamod; - } - else - { - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->normals[0], false); - } - bind->setBinding(nextBuf++, vbuf); - } - - - // Vertex colors - if (data->colors.size()) - { - const float *colors = &data->colors[0]; - Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); - std::vector colorsRGB(numVerts); - Ogre::RGBA *pColour = &colorsRGB.front(); - for (int i=0; iconvertColourValue(Ogre::ColourValue(colors[0],colors[1],colors[2], - colors[3]),pColour++); - colors += 4; - } - decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); - vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), - numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); - vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB.front(), true); - bind->setBinding(nextBuf++, vbuf); - } - - if (data->uvlist.size()) - { - - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES); - vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), - numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,false); - - if(flip) - { - float *datamod = new float[data->uvlist.size()]; - - for(unsigned int i = 0; i < data->uvlist.size(); i+=2){ - float x = data->uvlist[i]; - - float y = data->uvlist[i + 1]; - - datamod[i] =x; - datamod[i + 1] =y; - } - vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false); - delete [] datamod; - } - else - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[0], false); - bind->setBinding(nextBuf++, vbuf); - } - - // Triangle faces - The total number of triangle points - int numFaces = data->triangles.size(); - if (numFaces) - { - - sub->indexData->indexCount = numFaces; - sub->indexData->indexStart = 0; - Ogre::HardwareIndexBufferSharedPtr ibuf = Ogre::HardwareBufferManager::getSingleton(). - createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, numFaces, - Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); - - if(flip && mFlipVertexWinding && sub->indexData->indexCount % 3 == 0){ - - sub->indexData->indexBuffer = ibuf; - - uint16_t *datamod = new uint16_t[numFaces]; - int index = 0; - for (size_t i = 0; i < sub->indexData->indexCount; i+=3) - { - - const short *pos = &data->triangles[index]; - uint16_t i0 = (uint16_t) *(pos+0); - uint16_t i1 = (uint16_t) *(pos+1); - uint16_t i2 = (uint16_t) *(pos+2); - - //std::cout << "i0: " << i0 << "i1: " << i1 << "i2: " << i2 << "\n"; - - - datamod[index] = i2; - datamod[index+1] = i1; - datamod[index+2] = i0; - - index += 3; - } - - ibuf->writeData(0, ibuf->getSizeInBytes(), datamod, false); - delete [] datamod; - - } - else - ibuf->writeData(0, ibuf->getSizeInBytes(), &data->triangles[0], false); - sub->indexData->indexBuffer = ibuf; - } - - // Set material if one was given - if (!material.empty()) sub->setMaterialName(material); - - //add vertex bone assignments - - for (std::list::iterator it = vertexBoneAssignments.begin(); - it != vertexBoneAssignments.end(); it++) - { - sub->addBoneAssignment(*it); - } - if(mSkel.isNull()) - needBoneAssignments.push_back(sub); -} - -// Helper math functions. Reinventing linear algebra for the win! - -// Computes C = B + AxC*scale -static void vectorMulAdd(const Ogre::Matrix3 &A, const Ogre::Vector3 &B, float *C, float scale) -{ - // Keep the original values - float a = C[0]; - float b = C[1]; - float c = C[2]; - - // Perform matrix multiplication, scaling and addition - for (int i=0;i<3;i++) - C[i] = B[i] + (a*A[i][0] + b*A[i][1] + c*A[i][2])*scale; -} - -// Computes B = AxB (matrix*vector) -static void vectorMul(const Ogre::Matrix3 &A, float *C) -{ - // Keep the original values - float a = C[0]; - float b = C[1]; - float c = C[2]; - - // Perform matrix multiplication, scaling and addition - for (int i=0;i<3;i++) - C[i] = a*A[i][0] + b*A[i][1] + c*A[i][2]; -} - - -void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bounds, Transformation original, std::vector boneSequence) -{ - assert(shape != NULL); - - bool saveTheShape = inTheSkeletonTree; - // Interpret flags - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision - - // Bounding box collision isn't implemented, always use mesh for now. - if (bbcollide) - { - collide = true; - bbcollide = false; - } - - // If the object was marked "NCO" earlier, it shouldn't collide with - // anything. - if (flags & 0x800) - { - collide = false; - bbcollide = false; - } - - if (!collide && !bbcollide && hidden) - // This mesh apparently isn't being used for anything, so don't - // bother setting it up. - return; - - // Material name for this submesh, if any - Ogre::String material; - - // Skip the entire material phase for hidden nodes - if (!hidden) - { - // These are set below if present - NiTexturingProperty *t = NULL; - NiMaterialProperty *m = NULL; - NiAlphaProperty *a = NULL; - - // Scan the property list for material information - PropertyList &list = shape->props; - int n = list.length(); - for (int i=0; irecType == RC_NiTexturingProperty) - t = static_cast(pr); - else if (pr->recType == RC_NiMaterialProperty) - m = static_cast(pr); - else if (pr->recType == RC_NiAlphaProperty) - a = static_cast(pr); - } - - // Texture - Ogre::String texName; - if (t && t->textures[0].inUse) - { - NiSourceTexture *st = t->textures[0].texture.getPtr(); - if (st->external) - { - /* findRealTexture checks if the file actually - exists. If it doesn't, and the name ends in .tga, it - will try replacing the extension with .dds instead - and search for that. Bethesda at some at some point - converted all their BSA textures from tga to dds for - increased load speed, but all texture file name - references were kept as .tga. - - The function replaces the name in place (that's why - we cast away the const modifier), but this is no - problem since all the nif data is stored in a local - throwaway buffer. - */ - texName = "textures\\" + st->filename; - findRealTexture(texName); - } - else warn("Found internal texture, ignoring."); - } - - // Alpha modifiers - int alphaFlags = -1; - ubyte alphaTest = 0; - if (a) - { - alphaFlags = a->flags; - alphaTest = a->data.threshold; - } - - // Material - if (m || !texName.empty()) - { - // If we're here, then this mesh has a material. Thus we - // need to calculate a snappy material name. It should - // contain the mesh name (mesh->getName()) but also has to - // be unique. One mesh may use many materials. - material = getUniqueName(mesh->getName()); - - if (m) - { - // Use NiMaterialProperty data to create the data - const S_MaterialProperty *d = &m->data; - - std::multimap::iterator itr = MaterialMap.find(texName); - std::multimap::iterator lastElement; - lastElement = MaterialMap.upper_bound(texName); - if (itr != MaterialMap.end()) - { - for ( ; itr != lastElement; ++itr) - { - //std::cout << "OK!"; - //MaterialPtr mat = MaterialManager::getSingleton().getByName(itr->second,recourceGroup); - material = itr->second; - //if( mat->getA - } - } - else - { - //std::cout << "new"; - createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, - d->glossiness, d->alpha, alphaFlags, alphaTest, texName); - MaterialMap.insert(std::make_pair(texName,material)); - } - } - else - { - // We only have a texture name. Create a default - // material for it. - const Ogre::Vector3 zero(0.0f), one(1.0f); - createMaterial(material, one, one, zero, zero, 0.0f, 1.0f, - alphaFlags, alphaTest, texName); - } - } - } // End of material block, if(!hidden) ... - - /* Do in-place transformation of all the vertices and normals. This - is pretty messy stuff, but we need it to make the sub-meshes - appear in the correct place. Neither Ogre nor Bullet support - nested levels of sub-meshes with transformations applied to each - level. - */ - NiTriShapeData *data = shape->data.getPtr(); - int numVerts = data->vertices.size() / 3; - - float *ptr = (float*)&data->vertices[0]; - float *optr = ptr; - - std::list vertexBoneAssignments; - - Nif::NiTriShapeCopy copy = shape->clone(); - - if(!shape->controller.empty()) - { - Nif::Controller* cont = shape->controller.getPtr(); - if(cont->recType == RC_NiGeomMorpherController) - { - Nif::NiGeomMorpherController* morph = dynamic_cast (cont); - copy.morph = morph->data.get(); - copy.morph.setStartTime(morph->timeStart); - copy.morph.setStopTime(morph->timeStop); - saveTheShape = true; - } - - } - //use niskindata for the position of vertices. - if (!shape->skin.empty()) - { - - - - // vector that stores if the position of a vertex is absolute - std::vector vertexPosAbsolut(numVerts,false); - std::vector vertexPosOriginal(numVerts, Ogre::Vector3::ZERO); - std::vector vertexNormalOriginal(numVerts, Ogre::Vector3::ZERO); - - float *ptrNormals = (float*)&data->normals[0]; - //the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex] - //the first one contains a link to the bone, the second vertex transformation - //relative to the bone - int boneIndex = 0; - Ogre::Bone *bonePtr; - Ogre::Vector3 vecPos; - Ogre::Quaternion vecRot; - - std::vector boneList = shape->skin->data->bones; - - /* - Iterate through the boneList which contains what vertices are linked to - the bone (it->weights array) and at what position (it->trafo) - That position is added to every vertex. - */ - for (std::vector::iterator it = boneList.begin(); - it != boneList.end(); it++) - { - if(mSkel.isNull()) - { - std::cout << "No skeleton for :" << shape->skin->bones[boneIndex]->name << std::endl; - break; - } - //get the bone from bones array of skindata - if(!mSkel->hasBone(shape->skin->bones[boneIndex]->name)) - std::cout << "We don't have this bone"; - bonePtr = mSkel->getBone(shape->skin->bones[boneIndex]->name); - - // final_vector = old_vector + old_rotation*new_vector*old_scale - - - Nif::NiSkinData::BoneInfoCopy boneinfocopy; - boneinfocopy.trafo.rotation = it->trafo.rotation; - boneinfocopy.trafo.trans = it->trafo.trans; - boneinfocopy.bonename = shape->skin->bones[boneIndex]->name; - boneinfocopy.bonehandle = bonePtr->getHandle(); - copy.boneinfo.push_back(boneinfocopy); - for (unsigned int i=0; iweights.size(); i++) - { - vecPos = bonePtr->_getDerivedPosition() + - bonePtr->_getDerivedOrientation() * it->trafo.trans; - - vecRot = bonePtr->_getDerivedOrientation() * it->trafo.rotation; - unsigned int verIndex = it->weights[i].vertex; - //boneinfo.weights.push_back(*(it->weights.ptr + i)); - Nif::NiSkinData::IndividualWeight ind; - ind.weight = it->weights[i].weight; - ind.boneinfocopyindex = copy.boneinfo.size() - 1; - if(copy.vertsToWeights.find(verIndex) == copy.vertsToWeights.end()) - { - std::vector blank; - blank.push_back(ind); - copy.vertsToWeights[verIndex] = blank; - } - else - { - copy.vertsToWeights[verIndex].push_back(ind); - } - - //Check if the vertex is relativ, FIXME: Is there a better solution? - if (vertexPosAbsolut[verIndex] == false) - { - //apply transformation to the vertices - Ogre::Vector3 absVertPos = vecPos + vecRot * Ogre::Vector3(ptr + verIndex *3); - absVertPos = absVertPos * it->weights[i].weight; - vertexPosOriginal[verIndex] = Ogre::Vector3(ptr + verIndex *3); - - mBoundingBox.merge(absVertPos); - //convert it back to float * - for (int j=0; j<3; j++) - (ptr + verIndex*3)[j] = absVertPos[j]; - - //apply rotation to the normals (not every vertex has a normal) - //FIXME: I guessed that vertex[i] = normal[i], is that true? - if (verIndex < data->normals.size()) - { - Ogre::Vector3 absNormalsPos = vecRot * Ogre::Vector3(ptrNormals + verIndex *3); - absNormalsPos = absNormalsPos * it->weights[i].weight; - vertexNormalOriginal[verIndex] = Ogre::Vector3(ptrNormals + verIndex *3); - - for (int j=0; j<3; j++) - (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; - } - - vertexPosAbsolut[verIndex] = true; - } - else - { - Ogre::Vector3 absVertPos = vecPos + vecRot * vertexPosOriginal[verIndex]; - absVertPos = absVertPos * it->weights[i].weight; - Ogre::Vector3 old = Ogre::Vector3(ptr + verIndex *3); - absVertPos = absVertPos + old; - - mBoundingBox.merge(absVertPos); - //convert it back to float * - for (int j=0; j<3; j++) - (ptr + verIndex*3)[j] = absVertPos[j]; - - //apply rotation to the normals (not every vertex has a normal) - //FIXME: I guessed that vertex[i] = normal[i], is that true? - if (verIndex < data->normals.size()) - { - Ogre::Vector3 absNormalsPos = vecRot * vertexNormalOriginal[verIndex]; - absNormalsPos = absNormalsPos * it->weights[i].weight; - Ogre::Vector3 oldNormal = Ogre::Vector3(ptrNormals + verIndex *3); - absNormalsPos = absNormalsPos + oldNormal; - - for (int j=0; j<3; j++) - (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; - } - } - - - Ogre::VertexBoneAssignment vba; - vba.boneIndex = bonePtr->getHandle(); - vba.vertexIndex = verIndex; - vba.weight = it->weights[i].weight; - - - vertexBoneAssignments.push_back(vba); - } - - - boneIndex++; - } - - - } - else - { - - copy.boneSequence = boneSequence; - // Rotate, scale and translate all the vertices, - const Ogre::Matrix3 &rot = shape->trafo.rotation; - const Ogre::Vector3 &pos = shape->trafo.pos; - float scale = shape->trafo.scale; - - copy.trafo.trans = original.pos; - copy.trafo.rotation = original.rotation; - copy.trafo.scale = original.scale; - //We don't use velocity for anything yet, so it does not need to be saved - - // Computes C = B + AxC*scale - for (int i=0; inormals.size()) - { - ptr = (float*)&data->normals[0]; - for (int i=0; igetNumBones() - 1; - for(int i = 0; i < numVerts; i++){ - Ogre::VertexBoneAssignment vba; - vba.boneIndex = boneIndex; - vba.vertexIndex = i; - vba.weight = 1; - vertexBoneAssignments.push_back(vba); - } - } - } - - if (!hidden) - { - // Add this vertex set to the bounding box - bounds.add(optr, numVerts); - if(saveTheShape) - shapes.push_back(copy); - - // Create the submesh - createOgreSubMesh(shape, material, vertexBoneAssignments); - } -} - -void NIFLoader::calculateTransform() -{ - // Calculate transform - Ogre::Matrix4 transform = Ogre::Matrix4::IDENTITY; - transform = Ogre::Matrix4::getScale(vector) * transform; - - // Check whether we have to flip vertex winding. - // We do have to, if we changed our right hand base. - // We can test it by using the cross product from X and Y and see, if it is a non-negative - // projection on Z. Actually it should be exactly Z, as we don't do non-uniform scaling yet, - // but the test is cheap either way. - Ogre::Matrix3 m3; - transform.extract3x3Matrix(m3); - - if (m3.GetColumn(0).crossProduct(m3.GetColumn(1)).dotProduct(m3.GetColumn(2)) < 0) - { - mFlipVertexWinding = true; - } - - mTransform = transform; -} -void NIFLoader::handleNode(Nif::Node *node, int flags, - const Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone, std::vector boneSequence) -{ - // Accumulate the flags from all the child nodes. This works for all - // the flags we currently use, at least. - flags |= node->flags; - - // Check for extra data - Extra *e = node; - while (!e->extra.empty()) - { - // Get the next extra data in the list - e = e->extra.getPtr(); - assert(e != NULL); - - if (e->recType == RC_NiStringExtraData) - { - // String markers may contain important information - // affecting the entire subtree of this node - NiStringExtraData *sd = (NiStringExtraData*)e; - - if (sd->string == "NCO") - // No collision. Use an internal flag setting to mark this. - flags |= 0x800; - else if (sd->string == "MRK") - // Marker objects. These are only visible in the - // editor. Until and unless we add an editor component to - // the engine, just skip this entire node. - return; - } - - if (e->recType == RC_NiTextKeyExtraData){ - Nif::NiTextKeyExtraData* extra = dynamic_cast (e); - - std::ofstream file; - - if(mOutputAnimFiles){ - std::string cut = ""; - for(unsigned int i = 0; i < name.length(); i++) - { - if(!(name.at(i) == '\\' || name.at(i) == '/' || name.at(i) == '>' || name.at(i) == '<' || name.at(i) == '?' || name.at(i) == '*' || name.at(i) == '|' || name.at(i) == ':' || name.at(i) == '"')) - { - cut += name.at(i); - } - } - - std::cout << "Outputting " << cut << "\n"; - - file.open((verbosePath + "/Indices" + cut + ".txt").c_str()); - } - - for(std::vector::iterator textiter = extra->list.begin(); textiter != extra->list.end(); textiter++) - { - std::string text = textiter->text; - - replace(text.begin(), text.end(), '\n', '/'); - - text.erase(std::remove(text.begin(), text.end(), '\r'), text.end()); - std::size_t i = 0; - while(i < text.length()){ - while(i < text.length() && text.at(i) == '/' ){ - i++; - } - std::size_t first = i; - int length = 0; - while(i < text.length() && text.at(i) != '/' ){ - i++; - length++; - } - if(first < text.length()){ - //length = text.length() - first; - std::string sub = text.substr(first, length); - - if(mOutputAnimFiles) - file << "Time: " << textiter->time << "|" << sub << "\n"; - - textmappings[sub] = textiter->time; - } - } - } - file.close(); - } - } - - Ogre::Bone *bone = 0; - - // create skeleton or add bones - if (node->recType == RC_NiNode) - { - //FIXME: "Bip01" isn't every time the root bone - if (node->name == "Bip01" || node->name == "Root Bone") //root node, create a skeleton - { - inTheSkeletonTree = true; - - mSkel = Ogre::SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true); - } - else if (!mSkel.isNull() && !parentBone) - inTheSkeletonTree = false; - - if (!mSkel.isNull()) //if there is a skeleton - { - std::string name = node->name; - - // Quick-n-dirty workaround for the fact that several - // bones may have the same name. - if(!mSkel->hasBone(name)) - { - boneSequence.push_back(name); - bone = mSkel->createBone(name); - - if (parentBone) - parentBone->addChild(bone); - - bone->setInheritOrientation(true); - bone->setPosition(node->trafo.pos); - bone->setOrientation(node->trafo.rotation); - } - } - } - Transformation original = node->trafo; - // Apply the parent transformation to this node. We overwrite the - // existing data with the final transformation. - if (trafo) - { - // Get a non-const reference to the node's data, since we're - // overwriting it. TODO: Is this necessary? - Transformation &final = node->trafo; - - // For both position and rotation we have that: - // final_vector = old_vector + old_rotation*new_vector*old_scale - final.pos = trafo->pos + trafo->rotation*final.pos*trafo->scale; - - // Merge the rotations together - final.rotation = trafo->rotation * final.rotation; - - // Scale - final.scale *= trafo->scale; - } - - // For NiNodes, loop through children - if (node->recType == RC_NiNode) - { - NodeList &list = ((NiNode*)node)->children; - int n = list.length(); - for (int i = 0; itrafo, bounds, bone, boneSequence); - } - } - else if (node->recType == RC_NiTriShape && bNiTri) - { - std::string nodename = node->name; - - if (triname == "") - { - handleNiTriShape(dynamic_cast(node), flags, bounds, original, boneSequence); - } - else if(nodename.length() >= triname.length()) - { - std::transform(nodename.begin(), nodename.end(), nodename.begin(), ::tolower); - if(triname == nodename.substr(0, triname.length())) - handleNiTriShape(dynamic_cast(node), flags, bounds, original, boneSequence); - } - } -} void NIFLoader::loadResource(Ogre::Resource *resource) { - inTheSkeletonTree = false; - allanim.clear(); - shapes.clear(); - needBoneAssignments.clear(); - // needBoneAssignments.clear(); - mBoundingBox.setNull(); - mesh = 0; - mSkel.setNull(); - flip = false; - name = resource->getName(); - char suffix = name.at(name.length() - 2); - bool addAnim = true; - bool hasAnim = false; - bool linkSkeleton = true; - //bool baddin = false; - bNiTri = true; - if(name == "meshes\\base_anim.nif" || name == "meshes\\base_animkna.nif") - { - bNiTri = false; - } - - if(suffix == '*') - { - vector = Ogre::Vector3(-1,1,1); - flip = true; - } - else if(suffix == '?'){ - vector = Ogre::Vector3(1,-1,1); - flip = true; - } - else if(suffix == '<'){ - vector = Ogre::Vector3(1,1,-1); - flip = true; - } - else if(suffix == '>') - { - //baddin = true; - bNiTri = true; - std::string sub = name.substr(name.length() - 6, 4); - - if(sub.compare("0000") != 0) - addAnim = false; - - } - else if(suffix == ':') - { - //baddin = true; - linkSkeleton = false; - bNiTri = true; - std::string sub = name.substr(name.length() - 6, 4); - - if(sub.compare("0000") != 0) - addAnim = false; - - } - - switch(name.at(name.length() - 1)) - { - case '"': - triname = "tri chest"; - break; - case '*': - triname = "tri tail"; - break; - case ':': - triname = "tri left foot"; - break; - case '<': - triname = "tri right foot"; - break; - case '>': - triname = "tri left hand"; - break; - case '?': - triname = "tri right hand"; - break; - default: - triname = ""; - break; - } - if(flip) - { - calculateTransform(); - } - // Get the mesh - mesh = dynamic_cast(resource); - assert(mesh); - - // Look it up - resourceName = mesh->getName(); - - - // Helper that computes bounding boxes for us. - BoundsFinder bounds; - - // Load the NIF. TODO: Wrap this in a try-catch block once we're out - // of the early stages of development. Right now we WANT to catch - // every error as early and intrusively as possible, as it's most - // likely a sign of incomplete code rather than faulty input. - NIFFile nif(resourceName); - if (nif.numRecords() < 1) - { - warn("Found no records in NIF."); - return; - } - - // The first record is assumed to be the root node - Record *r = nif.getRecord(0); - assert(r != NULL); - - Nif::Node *node = dynamic_cast(r); - - if (node == NULL) - { - warn("First record in file was not a node, but a " + - r->recName + ". Skipping file."); - return; - } - - // Handle the node - std::vector boneSequence; - - - - handleNode(node, 0, NULL, bounds, 0, boneSequence); - if(addAnim) - { - for(int i = 0; i < nif.numRecords(); i++) - { - Nif::NiKeyframeController *f = dynamic_cast(nif.getRecord(i)); - - if(f != NULL) - { - hasAnim = true; - Nif::Node *o = dynamic_cast(f->target.getPtr()); - Nif::NiKeyframeDataPtr data = f->data; - - if (f->timeStart >= 10000000000000000.0f) - continue; - data->setBonename(o->name); - data->setStartTime(f->timeStart); - data->setStopTime(f->timeStop); - - allanim.push_back(data.get()); - } - } - } - // set the bounding value. - if (bounds.isValid()) - { - mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ())); - mesh->_setBoundingSphereRadius(bounds.getRadius()); - } - if(hasAnim && addAnim){ - allanimmap[name] = allanim; - alltextmappings[name] = textmappings; - } - if(!mSkel.isNull() && shapes.size() > 0 && addAnim) - { - allshapesmap[name] = shapes; - - } - - if(flip){ - mesh->_setBounds(mBoundingBox, false); - } - - if (!mSkel.isNull() ) - { - for(std::vector::iterator iter = needBoneAssignments.begin(); iter != needBoneAssignments.end(); iter++) - { - int boneIndex = mSkel->getNumBones() - 1; - Ogre::VertexBoneAssignment vba; - vba.boneIndex = boneIndex; - vba.vertexIndex = 0; - vba.weight = 1; - - - (*iter)->addBoneAssignment(vba); - } - //Don't link on npc parts to eliminate redundant skeletons - //Will have to be changed later slightly for robes/skirts - if(linkSkeleton) - mesh->_notifySkeleton(mSkel); - } + warn("Found no records in NIF."); } - - - - Ogre::MeshPtr NIFLoader::load(const std::string &name, const std::string &group) { + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - Ogre::MeshManager *m = Ogre::MeshManager::getSingletonPtr(); // Check if the resource already exists - Ogre::ResourcePtr ptr = m->getByName(name, group); - Ogre::MeshPtr themesh; - if (!ptr.isNull()){ - themesh = Ogre::MeshPtr(ptr); - } - else // Nope, create a new one. + Ogre::MeshPtr themesh = meshMgr.getByName(name, group); + if(themesh.isNull()) { - themesh = Ogre::MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr()); + static NIFLoader loader; + themesh = meshMgr.createManual(name, group, &loader); } return themesh; } -/* -This function shares much of the same code handleShapes() in MWRender::Animation -This function also creates new position and normal buffers for submeshes. -This function points to existing texture and IndexData buffers -*/ - -std::vector* NIFLoader::getAnim(std::string lowername){ - - std::map,ciLessBoost>::iterator iter = allanimmap.find(lowername); - std::vector* pass = 0; - if(iter != allanimmap.end()) - pass = &(iter->second); - return pass; - -} -std::vector* NIFLoader::getShapes(std::string lowername){ - - std::map,ciLessBoost>::iterator iter = allshapesmap.find(lowername); - std::vector* pass = 0; - if(iter != allshapesmap.end()) - pass = &(iter->second); - return pass; -} - -std::map* NIFLoader::getTextIndices(std::string lowername){ - std::map, ciLessBoost>::iterator iter = alltextmappings.find(lowername); - std::map* pass = 0; - if(iter != alltextmappings.end()) - pass = &(iter->second); - return pass; -} - - - /* More code currently not in use, from the old D source. This was used in the first attempt at loading NIF meshes, where each submesh diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 64efc70c7..bc1ef304c 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -67,12 +67,9 @@ namespace Nif namespace NifOgre { - - /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into - something Ogre can use. Later it will also handle the insertion of - collision meshes into Bullet / OgreBullet. + something Ogre can use. You have to insert meshes manually into Ogre like this: @@ -86,93 +83,18 @@ namespace NifOgre */ class NIFLoader : Ogre::ManualResourceLoader { - public: - static int numberOfMeshes; - static NIFLoader& getSingleton(); - static NIFLoader* getSingletonPtr(); +public: + virtual void loadResource(Ogre::Resource *resource); - virtual void loadResource(Ogre::Resource *resource); - - static Ogre::MeshPtr load(const std::string &name, - const std::string &group="General"); - //void insertMeshInsideBase(Ogre::Mesh* mesh); - std::vector* getAnim(std::string name); - std::vector* getShapes(std::string name); - std::map* getTextIndices(std::string name); - - - void setOutputAnimFiles(bool output); - void setVerbosePath(std::string path); - - private: - - NIFLoader() : resourceName(""), resourceGroup("General"), flip(false), mNormaliseNormals(false), - mFlipVertexWinding(false), mOutputAnimFiles(false), inTheSkeletonTree(false) {} - NIFLoader(NIFLoader& n) {} - - void calculateTransform(); - - - void warn(std::string msg); - void fail(std::string msg); - - void handleNode( Nif::Node *node, int flags, - const Nif::Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone, std::vector boneSequence); - - void handleNiTriShape(Nif::NiTriShape *shape, int flags, BoundsFinder &bounds, Nif::Transformation original, std::vector boneSequence); - - void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material, std::list &vertexBoneAssignments); - - void createMaterial(const Ogre::String &name, - const Ogre::Vector3 &ambient, - const Ogre::Vector3 &diffuse, - const Ogre::Vector3 &specular, - const Ogre::Vector3 &emissive, - float glossiness, float alpha, - int alphaFlags, float alphaTest, - const Ogre::String &texName); - - void findRealTexture(Ogre::String &texName); - - Ogre::String getUniqueName(const Ogre::String &input); - - //returns the skeleton name of this mesh - std::string getSkeletonName() - { - return resourceName + ".skel"; - } - - std::string verbosePath; - std::string resourceName; - std::string resourceGroup; - Ogre::Matrix4 mTransform; - Ogre::AxisAlignedBox mBoundingBox; - bool flip; - bool mNormaliseNormals; - bool mFlipVertexWinding; - bool bNiTri; - bool mOutputAnimFiles; - std::multimap MaterialMap; - - // pointer to the ogre mesh which is currently build - Ogre::Mesh *mesh; - Ogre::SkeletonPtr mSkel; - Ogre::Vector3 vector; - std::vector shapes; - std::string name; - std::string triname; - std::vector allanim; - - std::map textmappings; - std::map,ciLessBoost> alltextmappings; - std::map,ciLessBoost> allanimmap; - std::map,ciLessBoost> allshapesmap; - std::vector mAnim; - std::vector mS; - std::vector needBoneAssignments; - bool inTheSkeletonTree; + static Ogre::MeshPtr load(const std::string &name, + const std::string &group="General"); +private: + NIFLoader() {} + NIFLoader(NIFLoader& n) {} + void warn(const std::string &msg); + void fail(const std::string &msg); }; } diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 11c18010e..61d0c7b0e 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -336,7 +336,7 @@ namespace Physic char uniqueID[8]; sprintf( uniqueID, "%07.3f", scale ); std::string sid = uniqueID; - std::string outputstring = mesh + uniqueID + "\"|"; + std::string outputstring = mesh + uniqueID; //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif From ecdd4ee23f329cce4d48796f5a27a9af2eb22d5f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 12 Jul 2012 20:55:11 -0700 Subject: [PATCH 113/688] Load NiMorphData and NiKeyframeData using proper key lists --- apps/openmw/mwrender/animation.cpp | 65 ----- components/nif/data.hpp | 371 ++--------------------------- 2 files changed, 19 insertions(+), 417 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7e50706f9..13b797734 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -171,71 +171,6 @@ bool Animation::timeIndex(float time, const std::vector ×, int &i, i void Animation::handleAnimationTransforms() { - Ogre::SkeletonInstance* skel = base->getSkeleton(); - - Ogre::Bone* b = skel->getRootBone(); - b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3)); //This is a trick - - skel->_updateTransforms(); - //skel->_notifyManualBonesDirty(); - - base->getAllAnimationStates()->_notifyDirty(); - //base->_updateAnimation(); - //base->_notifyMoved(); - - std::vector::iterator iter; - int slot = 0; - if(transformations) - { - for(iter = transformations->begin(); iter != transformations->end(); iter++) - { - if(time < iter->getStartTime() || time < startTime || time > iter->getStopTime()) - { - slot++; - continue; - } - - float x; - float x2; - - const std::vector &quats = iter->getQuat(); - const std::vector &ttime = iter->gettTime(); - const std::vector &rtime = iter->getrTime(); - const std::vector &translist1 = iter->getTranslist1(); - - int rindexJ = rindexI[slot]; - timeIndex(time, rtime, rindexI[slot], rindexJ, x2); - - int tindexJ = tindexI[slot]; - timeIndex(time, ttime, tindexI[slot], tindexJ, x); - - Ogre::Vector3 t; - Ogre::Quaternion r; - - bool bTrans = translist1.size() > 0; - bool bQuats = quats.size() > 0; - if(skel->hasBone(iter->getBonename())) - { - Ogre::Bone* bone = skel->getBone(iter->getBonename()); - if(bTrans) - { - Ogre::Vector3 v1 = translist1[tindexI[slot]]; - Ogre::Vector3 v2 = translist1[tindexJ]; - t = (v1 + (v2 - v1) * x); - bone->setPosition(t); - } - if(bQuats) - { - r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); - bone->setOrientation(r); - } - } - - slot++; - } - skel->_updateTransforms(); - base->getAllAnimationStates()->_notifyDirty(); - } } } diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 2d1e8bd3b..208337f35 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -373,378 +373,45 @@ public: } }; -class NiMorphData : public Record +struct NiMorphData : public Record { - float startTime; - float stopTime; - std::vector initialVertices; - std::vector > relevantTimes; - std::vector > relevantData; - std::vector > additionalVertices; - -public: - float getStartTime() const - { return startTime; } - float getStopTime() const - { return stopTime; } - - void setStartTime(float time) - { startTime = time; } - void setStopTime(float time) - { stopTime = time; } - - const std::vector& getInitialVertices() const - { return initialVertices; } - const std::vector >& getRelevantData() const - { return relevantData; } - const std::vector >& getRelevantTimes() const - { return relevantTimes; } - const std::vector >& getAdditionalVertices() const - { return additionalVertices; } + struct MorphData { + FloatKeyList mData; + std::vector mVertices; + }; + std::vector mMorphs; void read(NIFFile *nif) { int morphCount = nif->getInt(); int vertCount = nif->getInt(); nif->getChar(); - int magic = nif->getInt(); - /*int type =*/ nif->getInt(); - for(int i = 0; i < vertCount; i++) + mMorphs.resize(morphCount); + for(int i=0; igetFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - initialVertices.push_back(Ogre::Vector3(x, y, z)); - } + mMorphs[i].mData.read(nif); - for(int i=1; igetInt(); - /*type =*/ nif->getInt(); - std::vector current; - std::vector currentTime; - for(int i = 0; i < magic; i++) - { - // Time, data, forward, backward tangents - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - current.push_back(Ogre::Vector3(x,y,z)); - currentTime.push_back(time); - //nif->getFloatLen(4*magic); - } - - if(magic) - { - relevantData.push_back(current); - relevantTimes.push_back(currentTime); - } - - std::vector verts; - for(int i = 0; i < vertCount; i++) - { - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - verts.push_back(Ogre::Vector3(x, y, z)); - } - additionalVertices.push_back(verts); + mMorphs[i].mVertices.resize(vertCount); + for(int j = 0;j < vertCount;j++) + mMorphs[i].mVertices[j] = nif->getVector3(); } } }; -class NiKeyframeData : public Record +struct NiKeyframeData : public Record { - std::string bonename; - //Rotations - std::vector quats; - std::vector tbc; - std::vector rottime; - float startTime; - float stopTime; - int rtype; - - //Translations - std::vector translist1; - std::vector translist2; - std::vector translist3; - std::vector transtbc; - std::vector transtime; - int ttype; - - //Scalings - std::vector scalefactor; - std::vector scaletime; - std::vector forwards; - std::vector backwards; - std::vector tbcscale; - int stype; - -public: - void clone(const NiKeyframeData &c) - { - quats = c.getQuat(); - tbc = c.getrTbc(); - rottime = c.getrTime(); - - //types - ttype = c.getTtype(); - rtype = c.getRtype(); - stype = c.getStype(); - - - translist1 = c.getTranslist1(); - translist2 = c.getTranslist2(); - translist3 = c.getTranslist3(); - - transtime = c.gettTime(); - - bonename = c.getBonename(); - } - - void setBonename(std::string bone) - { bonename = bone; } - void setStartTime(float start) - { startTime = start; } - void setStopTime(float end) - { stopTime = end; } + QuaternionKeyList mRotations; + Vector3KeyList mTranslations; + FloatKeyList mScales; void read(NIFFile *nif) { - // Rotations first - int count = nif->getInt(); - //std::vector quat(count); - //std::vector rottime(count); - if(count) - { - //TYPE1 LINEAR_KEY - //TYPE2 QUADRATIC_KEY - //TYPE3 TBC_KEY - //TYPE4 XYZ_ROTATION_KEY - //TYPE5 UNKNOWN_KEY - rtype = nif->getInt(); - //std::cout << "Count: " << count << "Type: " << type << "\n"; - - if(rtype == 1) - { - //We need to actually read in these values instead of skipping them - //nif->skip(count*4*5); // time + quaternion - for (int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float w = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); - quats.push_back(quat); - rottime.push_back(time); - //if(time == 0.0 || time > 355.5) - // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; - } - } - else if(rtype == 3) - { - //Example - node 116 in base_anim.nif - for (int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float w = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - - float tbcx = nif->getFloat(); - float tbcy = nif->getFloat(); - float tbcz = nif->getFloat(); - - Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); - Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); - quats.push_back(quat); - rottime.push_back(time); - tbc.push_back(vec); - //if(time == 0.0 || time > 355.5) - // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; - } - } - else if(rtype == 4) - { - for(int j=0;jgetFloat(); // time - for(int i=0; i<3; i++) - { - int cnt = nif->getInt(); - int type = nif->getInt(); - if(type == 1) - nif->skip(cnt*4*2); // time + unknown - else if(type == 2) - nif->skip(cnt*4*4); // time + unknown vector - else - nif->fail("Unknown sub-rotation type"); - } - } - } - else - nif->fail("Unknown rotation type in NiKeyframeData"); - } - //first = false; - - // Then translation - count = nif->getInt(); - if(count) - { - ttype = nif->getInt(); - - //std::cout << "TransCount:" << count << " Type: " << type << "\n"; - if(ttype == 1) - { - for(int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - translist1.push_back(trans); - transtime.push_back(time); - } - //nif->getFloatLen(count*4); // time + translation - } - else if(ttype == 2) - { - //Example - node 116 in base_anim.nif - for(int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - float x2 = nif->getFloat(); - float y2 = nif->getFloat(); - float z2 = nif->getFloat(); - float x3 = nif->getFloat(); - float y3 = nif->getFloat(); - float z3 = nif->getFloat(); - - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - Ogre::Vector3 trans2 = Ogre::Vector3(x2, y2, z2); - Ogre::Vector3 trans3 = Ogre::Vector3(x3, y3, z3); - transtime.push_back(time); - translist1.push_back(trans); - translist2.push_back(trans2); - translist3.push_back(trans3); - } - - //nif->getFloatLen(count*10); // trans1 + forward + backward - } - else if(ttype == 3) - { - for(int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - float t = nif->getFloat(); - float b = nif->getFloat(); - float c = nif->getFloat(); - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - Ogre::Vector3 tbc = Ogre::Vector3(t, b, c); - translist1.push_back(trans); - transtbc.push_back(tbc); - transtime.push_back(time); - } - //nif->getFloatLen(count*7); // trans1 + tension,bias,continuity - } - else nif->fail("Unknown translation type"); - } - - // Finally, scalings - count = nif->getInt(); - if(count) - { - stype = nif->getInt(); - - for(int i = 0; i < count; i++) - { - //int size = 0; - if(stype >= 1 && stype < 4) - { - float time = nif->getFloat(); - float scale = nif->getFloat(); - scaletime.push_back(time); - scalefactor.push_back(scale); - //size = 2; // time+scale - } - else - nif->fail("Unknown scaling type"); - - if(stype == 2) - { - //size = 4; // 1 + forward + backward (floats) - float forward = nif->getFloat(); - float backward = nif->getFloat(); - forwards.push_back(forward); - backwards.push_back(backward); - } - else if(stype == 3) - { - //size = 5; // 1 + tbc - float tbcx = nif->getFloat(); - float tbcy = nif->getFloat(); - float tbcz = nif->getFloat(); - Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); - tbcscale.push_back(vec); - } - } - } - else - stype = 0; + mRotations.read(nif); + mTranslations.read(nif); + mScales.read(nif); } - - int getRtype() const - { return rtype; } - int getStype() const - { return stype; } - int getTtype() const - { return ttype; } - float getStartTime() const - { return startTime; } - float getStopTime() const - { return stopTime; } - const std::vector& getQuat() const - { return quats; } - const std::vector& getrTbc() const - { return tbc; } - const std::vector& getrTime() const - { return rottime; } - - const std::vector& getTranslist1() const - { return translist1; } - const std::vector& getTranslist2() const - { return translist2; } - const std::vector& getTranslist3() const - { return translist3; } - const std::vector& gettTime() const - { return transtime; } - const std::vector& getScalefactor() const - { return scalefactor; } - const std::vector& getForwards() const - { return forwards; } - const std::vector& getBackwards() const - { return backwards; } - const std::vector& getScaleTbc() const - { return tbcscale; } - - const std::vector& getsTime() const - { return scaletime; } - const std::string& getBonename() const - { return bonename; } }; } // Namespace From b8384162b6a2e1016ea982898d40376699d722d5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jul 2012 06:16:55 +0200 Subject: [PATCH 114/688] merge --- apps/openmw/mwrender/renderingmanager.cpp | 2 + apps/openmw/mwrender/terrain.cpp | 14 +- apps/openmw/mwrender/terrain.hpp | 2 +- apps/openmw/mwrender/terrainmaterial.cpp | 1776 +-------------------- apps/openmw/mwrender/terrainmaterial.hpp | 248 +-- extern/shiny | 2 +- files/materials/objects.mat | 6 - files/materials/objects.shader | 6 +- files/materials/openmw.configuration | 7 +- files/materials/terrain.shader | 57 + files/materials/terrain.shaderset | 15 + 11 files changed, 175 insertions(+), 1960 deletions(-) create mode 100644 files/materials/terrain.shader create mode 100644 files/materials/terrain.shaderset diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 475e906c4..d8aefaaa0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -105,6 +105,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const Settings::Manager::setBool("enabled", "Shadows", false); sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); + sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); + sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); applyCompositors(); diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index f14db34e6..7610acadc 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -28,15 +28,16 @@ namespace MWRender mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)), mRendering(rend) { mTerrainGlobals = OGRE_NEW TerrainGlobalOptions(); + TerrainMaterialGeneratorPtr matGen; - TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB(); + TerrainMaterial* matGenP = new TerrainMaterial(); matGen.bind(matGenP); mTerrainGlobals->setDefaultMaterialGenerator(matGen); TerrainMaterialGenerator::Profile* const activeProfile = mTerrainGlobals->getDefaultMaterialGenerator() ->getActiveProfile(); - mActiveProfile = static_cast(activeProfile); + mActiveProfile = static_cast(activeProfile); //The pixel error should be as high as possible without it being noticed //as it governs how fast mesh quality decreases. @@ -52,6 +53,7 @@ namespace MWRender //this seemed the distance where it wasn't too noticeable mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); + /* mActiveProfile->setLightmapEnabled(false); mActiveProfile->setLayerSpecularMappingEnabled(false); mActiveProfile->setLayerNormalMappingEnabled(false); @@ -71,6 +73,7 @@ namespace MWRender //composite maps lead to a drastic increase in loading time so are //disabled mActiveProfile->setCompositeMapEnabled(false); + */ mTerrainGroup.setOrigin(Vector3(mWorldSize/2, 0, @@ -181,7 +184,7 @@ namespace MWRender if ( land->landData->usingColours ) { // disable or enable global colour map (depends on available vertex colours) - mActiveProfile->setGlobalColourMapEnabled(true); + //mActiveProfile->setGlobalColourMapEnabled(true); TexturePtr vertex = getVertexColours(land, cellX, cellY, x*(mLandSize-1), @@ -191,7 +194,10 @@ namespace MWRender //this is a hack to get around the fact that Ogre seems to //corrupt the global colour map leading to rendering errors MaterialPtr mat = terrain->getMaterial(); - mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); + /// \todo + //mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); + + //mat = terrain->_getCompositeMapMaterial(); //mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 7ac48047d..c83d96cf4 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -46,7 +46,7 @@ namespace MWRender{ RenderingManager* mRendering; - Ogre::TerrainMaterialGeneratorB::SM2Profile* mActiveProfile; + TerrainMaterial::Profile* mActiveProfile; /** * The length in verticies of a single terrain block. diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index a3265b2a5..577b346df 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -1,1746 +1,82 @@ -/* ------------------------------------------------------------------------------ -This source file is part of OGRE -(Object-oriented Graphics Rendering Engine) -For the latest info, see http://www.ogre3d.org/ - -Copyright (c) 2000-2011 Torus Knot Software Ltd - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ------------------------------------------------------------------------------ -*/ #include "terrainmaterial.hpp" -#include "OgreTerrain.h" -#include "OgreMaterialManager.h" -#include "OgreTechnique.h" -#include "OgrePass.h" -#include "OgreTextureUnitState.h" -#include "OgreGpuProgramManager.h" -#include "OgreHighLevelGpuProgramManager.h" -#include "OgreHardwarePixelBuffer.h" -#include "OgreShadowCameraSetupPSSM.h" -#include -#include "renderingmanager.hpp" +#include -#undef far +#include -namespace Ogre + +namespace MWRender { - //--------------------------------------------------------------------- - TerrainMaterialGeneratorB::TerrainMaterialGeneratorB() - { - // define the layers - // We expect terrain textures to have no alpha, so we use the alpha channel - // in the albedo texture to store specular reflection - // similarly we double-up the normal and height (for parallax) - mLayerDecl.samplers.push_back(TerrainLayerSampler("albedo_specular", PF_BYTE_RGBA)); - //mLayerDecl.samplers.push_back(TerrainLayerSampler("normal_height", PF_BYTE_RGBA)); - - mLayerDecl.elements.push_back( - TerrainLayerSamplerElement(0, TLSS_ALBEDO, 0, 3)); - //mLayerDecl.elements.push_back( - // TerrainLayerSamplerElement(0, TLSS_SPECULAR, 3, 1)); - //mLayerDecl.elements.push_back( - // TerrainLayerSamplerElement(1, TLSS_NORMAL, 0, 3)); - //mLayerDecl.elements.push_back( - // TerrainLayerSamplerElement(1, TLSS_HEIGHT, 3, 1)); - - mProfiles.push_back(OGRE_NEW SM2Profile(this, "SM2", "Profile for rendering on Shader Model 2 capable cards")); - // TODO - check hardware capabilities & use fallbacks if required (more profiles needed) - setActiveProfile("SM2"); - - } - //--------------------------------------------------------------------- - TerrainMaterialGeneratorB::~TerrainMaterialGeneratorB() - { - - } - //--------------------------------------------------------------------- - //--------------------------------------------------------------------- - TerrainMaterialGeneratorB::SM2Profile::SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc) - : Profile(parent, name, desc) - , mShaderGen(0) - , mLayerNormalMappingEnabled(true) - , mLayerParallaxMappingEnabled(true) - , mLayerSpecularMappingEnabled(true) - , mGlobalColourMapEnabled(true) - , mLightmapEnabled(true) - , mCompositeMapEnabled(true) - , mReceiveDynamicShadows(true) - , mPSSM(0) - , mDepthShadows(false) - , mLowLodShadows(false) - , mShadowFar(1300) - { - - } - //--------------------------------------------------------------------- - TerrainMaterialGeneratorB::SM2Profile::~SM2Profile() - { - OGRE_DELETE mShaderGen; - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::requestOptions(Terrain* terrain) - { - terrain->_setMorphRequired(true); - terrain->_setNormalMapRequired(true); - terrain->_setLightMapRequired(mLightmapEnabled, true); - terrain->_setCompositeMapRequired(mCompositeMapEnabled); - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setShadowFar(float far) + TerrainMaterial::TerrainMaterial() { - if (mShadowFar != far) - { - mShadowFar = far; - mParent->_markChanged(); - } + mLayerDecl.samplers.push_back(Ogre::TerrainLayerSampler("albedo_specular", Ogre::PF_BYTE_RGBA)); + //mLayerDecl.samplers.push_back(Ogre::TerrainLayerSampler("normal_height", Ogre::PF_BYTE_RGBA)); + + mLayerDecl.elements.push_back( + Ogre::TerrainLayerSamplerElement(0, Ogre::TLSS_ALBEDO, 0, 3)); + //mLayerDecl.elements.push_back( + // Ogre::TerrainLayerSamplerElement(0, Ogre::TLSS_SPECULAR, 3, 1)); + //mLayerDecl.elements.push_back( + // Ogre::TerrainLayerSamplerElement(1, Ogre::TLSS_NORMAL, 0, 3)); + //mLayerDecl.elements.push_back( + // Ogre::TerrainLayerSamplerElement(1, Ogre::TLSS_HEIGHT, 3, 1)); + + + mProfiles.push_back(OGRE_NEW Profile(this, "SM2", "Profile for rendering on Shader Model 2 capable cards")); + setActiveProfile("SM2"); } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setShadowFadeStart(float fadestart) + + // ----------------------------------------------------------------------------------------------------------------------- + + TerrainMaterial::Profile::Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc) + : Ogre::TerrainMaterialGenerator::Profile(parent, name, desc) { - if (mShadowFadeStart != fadestart) - { - mShadowFadeStart = fadestart; - mParent->_markChanged(); - } } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setLayerNormalMappingEnabled(bool enabled) - { - if (enabled != mLayerNormalMappingEnabled) - { - mLayerNormalMappingEnabled = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setLayerParallaxMappingEnabled(bool enabled) - { - if (enabled != mLayerParallaxMappingEnabled) - { - mLayerParallaxMappingEnabled = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setLayerSpecularMappingEnabled(bool enabled) - { - if (enabled != mLayerSpecularMappingEnabled) - { - mLayerSpecularMappingEnabled = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setGlobalColourMapEnabled(bool enabled) - { - if (enabled != mGlobalColourMapEnabled) - { - mGlobalColourMapEnabled = enabled; - //mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setLightmapEnabled(bool enabled) - { - if (enabled != mLightmapEnabled) - { - mLightmapEnabled = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setCompositeMapEnabled(bool enabled) - { - if (enabled != mCompositeMapEnabled) - { - mCompositeMapEnabled = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsEnabled(bool enabled) - { - if (enabled != mReceiveDynamicShadows) - { - mReceiveDynamicShadows = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings) - { - if (pssmSettings != mPSSM) - { - mPSSM = pssmSettings; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsDepth(bool enabled) - { - if (enabled != mDepthShadows) - { - mDepthShadows = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsLowLod(bool enabled) - { - if (enabled != mLowLodShadows) - { - mLowLodShadows = enabled; - mParent->_markChanged(); - } - } - //--------------------------------------------------------------------- - uint8 TerrainMaterialGeneratorB::SM2Profile::getMaxLayers(const Terrain* terrain) const - { - // count the texture units free - uint8 freeTextureUnits = 16; - // lightmap - if (mLightmapEnabled) - --freeTextureUnits; - // normalmap - --freeTextureUnits; - // colourmap - //if (terrain->getGlobalColourMapEnabled()) - --freeTextureUnits; - if (isShadowingEnabled(HIGH_LOD, terrain)) - { - uint numShadowTextures = 1; - if (getReceiveDynamicShadowsPSSM()) - { - numShadowTextures = getReceiveDynamicShadowsPSSM()->getSplitCount(); - } - freeTextureUnits -= numShadowTextures; - } - - // each layer needs 2.25 units (1xdiffusespec, (1xnormalheight), 0.25xblend) - return static_cast(freeTextureUnits / (1.25f + (mLayerNormalMappingEnabled||mLayerParallaxMappingEnabled))); - - - } - int TerrainMaterialGeneratorB::SM2Profile::getNumberOfLightsSupported() const + TerrainMaterial::Profile::~Profile() { - return Settings::Manager::getInt("num lights", "Terrain"); } - //--------------------------------------------------------------------- - MaterialPtr TerrainMaterialGeneratorB::SM2Profile::generate(const Terrain* terrain) - { - // re-use old material if exists - MaterialPtr mat = terrain->_getMaterial(); - if (mat.isNull()) - { - MaterialManager& matMgr = MaterialManager::getSingleton(); - // it's important that the names are deterministic for a given terrain, so - // use the terrain pointer as an ID - const String& matName = terrain->getMaterialName(); - mat = matMgr.getByName(matName); - if (mat.isNull()) - { - mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - } - } - // clear everything - mat->removeAllTechniques(); - - // Automatically disable normal & parallax mapping if card cannot handle it - // We do this rather than having a specific technique for it since it's simpler - GpuProgramManager& gmgr = GpuProgramManager::getSingleton(); - if (!gmgr.isSyntaxSupported("ps_3_0") && !gmgr.isSyntaxSupported("ps_2_x") - && !gmgr.isSyntaxSupported("fp40") && !gmgr.isSyntaxSupported("arbfp1")) - { - setLayerNormalMappingEnabled(false); - setLayerParallaxMappingEnabled(false); - } - addTechnique(mat, terrain, HIGH_LOD); + Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain) + { + const Ogre::String& matName = terrain->getMaterialName(); + Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(matName); + if (!mat.isNull()) + Ogre::MaterialManager::getSingleton().remove(matName); - // LOD - if(mCompositeMapEnabled) - { - addTechnique(mat, terrain, LOW_LOD); - Material::LodValueList lodValues; - lodValues.push_back(TerrainGlobalOptions::getSingleton().getCompositeMapDistance()); - mat->setLodLevels(lodValues); - Technique* lowLodTechnique = mat->getTechnique(1); - lowLodTechnique->setLodIndex(1); - } - updateParams(mat, terrain); + sh::MaterialInstance* m = sh::Factory::getInstance().createMaterialInstance (matName); - return mat; - } - //--------------------------------------------------------------------- - MaterialPtr TerrainMaterialGeneratorB::SM2Profile::generateForCompositeMap(const Terrain* terrain) - { - // re-use old material if exists - MaterialPtr mat = terrain->_getCompositeMapMaterial(); - if (mat.isNull()) - { - MaterialManager& matMgr = MaterialManager::getSingleton(); + return Ogre::MaterialManager::getSingleton().getByName(matName); + } - // it's important that the names are deterministic for a given terrain, so - // use the terrain pointer as an ID - const String& matName = terrain->getMaterialName() + "/comp"; - mat = matMgr.getByName(matName); - if (mat.isNull()) - { - mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - } - } - // clear everything - mat->removeAllTechniques(); + Ogre::MaterialPtr TerrainMaterial::Profile::generateForCompositeMap(const Ogre::Terrain* terrain) + { + throw std::runtime_error ("composite map not supported"); + } - addTechnique(mat, terrain, RENDER_COMPOSITE_MAP); + Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const + { + return 32; + } - updateParamsForCompositeMap(mat, terrain); + void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) + { + } - return mat; - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::addTechnique( - const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt) - { - - Technique* tech = mat->createTechnique(); - - // Only supporting one pass - Pass* pass = tech->createPass(); - - GpuProgramManager& gmgr = GpuProgramManager::getSingleton(); - HighLevelGpuProgramManager& hmgr = HighLevelGpuProgramManager::getSingleton(); - if (!mShaderGen) - { - bool check2x = mLayerNormalMappingEnabled || mLayerParallaxMappingEnabled; - if (hmgr.isLanguageSupported("cg")) - mShaderGen = OGRE_NEW ShaderHelperCg(); - else if (hmgr.isLanguageSupported("hlsl") && - ((check2x && gmgr.isSyntaxSupported("ps_2_x")) || - (!check2x && gmgr.isSyntaxSupported("ps_2_0")))) - mShaderGen = OGRE_NEW ShaderHelperHLSL(); - else if (hmgr.isLanguageSupported("glsl")) - mShaderGen = OGRE_NEW ShaderHelperGLSL(); - else - { - // todo - } - - // check SM3 features - mSM3Available = GpuProgramManager::getSingleton().isSyntaxSupported("ps_3_0"); - - } - HighLevelGpuProgramPtr vprog = mShaderGen->generateVertexProgram(this, terrain, tt); - HighLevelGpuProgramPtr fprog = mShaderGen->generateFragmentProgram(this, terrain, tt); - - pass->setVertexProgram(vprog->getName()); - pass->setFragmentProgram(fprog->getName()); - - if (tt == HIGH_LOD || tt == RENDER_COMPOSITE_MAP) - { - // global normal map - TextureUnitState* tu = pass->createTextureUnitState(); - tu->setTextureName(terrain->getTerrainNormalMap()->getName()); - tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); - - // global colour map - if (isGlobalColourMapEnabled()) - { - tu = pass->createTextureUnitState(""); - tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); - } - - // light map - if (isLightmapEnabled()) - { - tu = pass->createTextureUnitState(terrain->getLightmap()->getName()); - tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); - } - - // blend maps - uint maxLayers = getMaxLayers(terrain); - uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - for (uint i = 0; i < numBlendTextures; ++i) - { - tu = pass->createTextureUnitState(terrain->getBlendTextureName(i)); - tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); - } - - // layer textures - for (uint i = 0; i < numLayers; ++i) - { - // diffuse / specular - tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 0)); - - // normal / height - if (mLayerNormalMappingEnabled || mLayerParallaxMappingEnabled) - tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 1)); - } - - } - else - { - // LOW_LOD textures - // composite map - TextureUnitState* tu = pass->createTextureUnitState(); - tu->setTextureName(terrain->getCompositeMap()->getName()); - tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); - - // That's it! - - } - - // Add shadow textures (always at the end) - if (isShadowingEnabled(tt, terrain)) - { - uint numTextures = 1; - if (getReceiveDynamicShadowsPSSM()) - { - numTextures = getReceiveDynamicShadowsPSSM()->getSplitCount(); - } - for (uint i = 0; i < numTextures; ++i) - { - TextureUnitState* tu = pass->createTextureUnitState(); - tu->setContentType(TextureUnitState::CONTENT_SHADOW); - tu->setTextureAddressingMode(TextureUnitState::TAM_BORDER); - tu->setTextureBorderColour(ColourValue::White); - } - } - - } - //--------------------------------------------------------------------- - bool TerrainMaterialGeneratorB::SM2Profile::isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const - { - return getReceiveDynamicShadowsEnabled() && tt != RENDER_COMPOSITE_MAP && - (tt != LOW_LOD || mLowLodShadows) && - terrain->getSceneManager()->isShadowTechniqueTextureBased(); - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::updateParams(const MaterialPtr& mat, const Terrain* terrain) - { - mShaderGen->updateParams(this, mat, terrain, false); - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain) - { - mShaderGen->updateParams(this, mat, terrain, true); - } - //--------------------------------------------------------------------- - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateVertexProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramPtr ret = createVertexProgram(prof, terrain, tt); - - StringUtil::StrStreamType sourceStr; - generateVertexProgramSource(prof, terrain, tt, sourceStr); - ret->setSource(sourceStr.str()); - ret->load(); - defaultVpParams(prof, terrain, tt, ret); -#if OGRE_DEBUG_MODE - LogManager::getSingleton().stream(LML_TRIVIAL) << "*** Terrain Vertex Program: " - << ret->getName() << " ***\n" << ret->getSource() << "\n*** ***"; -#endif - - return ret; - - } - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateFragmentProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramPtr ret = createFragmentProgram(prof, terrain, tt); - - StringUtil::StrStreamType sourceStr; - generateFragmentProgramSource(prof, terrain, tt, sourceStr); - - ret->setSource(sourceStr.str()); - ret->load(); - defaultFpParams(prof, terrain, tt, ret); - -#if OGRE_DEBUG_MODE - LogManager::getSingleton().stream(LML_TRIVIAL) << "*** Terrain Fragment Program: " - << ret->getName() << " ***\n" << ret->getSource() << "\n*** ***"; -#endif - - return ret; - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateVertexProgramSource( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - generateVpHeader(prof, terrain, tt, outStream); - - if (tt != LOW_LOD) - { - uint maxLayers = prof->getMaxLayers(terrain); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - - for (uint i = 0; i < numLayers; ++i) - generateVpLayer(prof, terrain, tt, i, outStream); - } - - generateVpFooter(prof, terrain, tt, outStream); - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateFragmentProgramSource( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - generateFpHeader(prof, terrain, tt, outStream); - - if (tt != LOW_LOD) - { - uint maxLayers = prof->getMaxLayers(terrain); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - - for (uint i = 0; i < numLayers; ++i) - generateFpLayer(prof, terrain, tt, i, outStream); - } - - generateFpFooter(prof, terrain, tt, outStream); - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::defaultVpParams( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog) - { - GpuProgramParametersSharedPtr params = prog->getDefaultParameters(); - params->setIgnoreMissingParams(true); - params->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX); - params->setNamedAutoConstant("viewProjMatrix", GpuProgramParameters::ACT_VIEWPROJ_MATRIX); - params->setNamedAutoConstant("lodMorph", GpuProgramParameters::ACT_CUSTOM, - Terrain::LOD_MORPH_CUSTOM_PARAM); - - if (prof->isShadowingEnabled(tt, terrain)) - { - uint numTextures = 1; - if (prof->getReceiveDynamicShadowsPSSM()) - { - numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); - } - for (uint i = 0; i < numTextures; ++i) - { - params->setNamedAutoConstant("texViewProjMatrix" + StringConverter::toString(i), - GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i); - if (prof->getReceiveDynamicShadowsDepth()) - { - //params->setNamedAutoConstant("depthRange" + StringConverter::toString(i), - //GpuProgramParameters::ACT_SHADOW_SCENE_DEPTH_RANGE, i); - } - } - } - - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::defaultFpParams( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog) - { - GpuProgramParametersSharedPtr params = prog->getDefaultParameters(); - params->setIgnoreMissingParams(true); - - params->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); - - for (int i=0; igetNumberOfLightsSupported(); ++i) - { - params->setNamedAutoConstant("lightPosObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i); - params->setNamedAutoConstant("lightDiffuseColour"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i); - if (i > 0) - params->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); - //params->setNamedAutoConstant("lightSpecularColour"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, i); - } - - if (MWRender::RenderingManager::useMRT()) - params->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE); - - params->setNamedAutoConstant("eyePosObjSpace", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE); - params->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); - params->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS); - - if (prof->isShadowingEnabled(tt, terrain)) - { - params->setNamedConstant("shadowFar_fadeStart", Vector4(prof->mShadowFar, prof->mShadowFadeStart * prof->mShadowFar, 0, 0)); - uint numTextures = 1; - if (prof->getReceiveDynamicShadowsPSSM()) - { - PSSMShadowCameraSetup* pssm = prof->getReceiveDynamicShadowsPSSM(); - numTextures = pssm->getSplitCount(); - Vector4 splitPoints; - const PSSMShadowCameraSetup::SplitPointList& splitPointList = pssm->getSplitPoints(); - // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) - for (uint i = 1; i < numTextures; ++i) - { - splitPoints[i-1] = splitPointList[i]; - } - params->setNamedConstant("pssmSplitPoints", splitPoints); - } - - if (prof->getReceiveDynamicShadowsDepth()) - { - size_t samplerOffset = (tt == HIGH_LOD) ? mShadowSamplerStartHi : mShadowSamplerStartLo; - for (uint i = 0; i < numTextures; ++i) - { - params->setNamedAutoConstant("inverseShadowmapSize" + StringConverter::toString(i), - GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i + samplerOffset); - } - } - } - - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateParams( - const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap) - { - Pass* p = mat->getTechnique(0)->getPass(0); - if (compositeMap) - { - updateVpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getVertexProgramParameters()); - updateFpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getFragmentProgramParameters()); - } - else - { - // high lod - updateVpParams(prof, terrain, HIGH_LOD, p->getVertexProgramParameters()); - updateFpParams(prof, terrain, HIGH_LOD, p->getFragmentProgramParameters()); - - if(prof->isCompositeMapEnabled()) - { - // low lod - p = mat->getTechnique(1)->getPass(0); - updateVpParams(prof, terrain, LOW_LOD, p->getVertexProgramParameters()); - updateFpParams(prof, terrain, LOW_LOD, p->getFragmentProgramParameters()); - } - } - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateVpParams( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params) - { - params->setIgnoreMissingParams(true); - uint maxLayers = prof->getMaxLayers(terrain); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - uint numUVMul = numLayers / 4; - if (numLayers % 4) - ++numUVMul; - for (uint i = 0; i < numUVMul; ++i) - { - Vector4 uvMul( - terrain->getLayerUVMultiplier(i * 4), - terrain->getLayerUVMultiplier(i * 4 + 1), - terrain->getLayerUVMultiplier(i * 4 + 2), - terrain->getLayerUVMultiplier(i * 4 + 3) - ); - params->setNamedConstant("uvMul" + StringConverter::toString(i), uvMul); - } - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateFpParams( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params) - { - params->setIgnoreMissingParams(true); - // TODO - parameterise this? - Vector4 scaleBiasSpecular(0.03, -0.04, 32, 1); - params->setNamedConstant("scaleBiasSpecular", scaleBiasSpecular); - - } - //--------------------------------------------------------------------- - String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getChannel(uint idx) - { - uint rem = idx % 4; - switch(rem) - { - case 0: - default: - return "r"; - case 1: - return "g"; - case 2: - return "b"; - case 3: - return "a"; - }; - } - //--------------------------------------------------------------------- - String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getVertexProgramName( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - String progName = terrain->getMaterialName() + "/sm2/vp"; - - switch(tt) - { - case HIGH_LOD: - progName += "/hlod"; - break; - case LOW_LOD: - progName += "/llod"; - break; - case RENDER_COMPOSITE_MAP: - progName += "/comp"; - break; - } - - return progName; - - } - //--------------------------------------------------------------------- - String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getFragmentProgramName( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - - String progName = terrain->getMaterialName() + "/sm2/fp"; - - switch(tt) - { - case HIGH_LOD: - progName += "/hlod"; - break; - case LOW_LOD: - progName += "/llod"; - break; - case RENDER_COMPOSITE_MAP: - progName += "/comp"; - break; - } - - return progName; - } - //--------------------------------------------------------------------- - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::createVertexProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - String progName = getVertexProgramName(prof, terrain, tt); - HighLevelGpuProgramPtr ret = mgr.getByName(progName); - if (ret.isNull()) - { - ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_VERTEX_PROGRAM); - } - else - { - ret->unload(); - } - - ret->setParameter("profiles", "vs_3_0 vs_2_0 vp40 arbvp1"); - ret->setParameter("entry_point", "main_vp"); - - return ret; - - } - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::createFragmentProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - String progName = getFragmentProgramName(prof, terrain, tt); - - HighLevelGpuProgramPtr ret = mgr.getByName(progName); - if (ret.isNull()) - { - ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_FRAGMENT_PROGRAM); - } - else - { - ret->unload(); - } - - ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); - ret->setParameter("entry_point", "main_fp"); - - return ret; - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpHeader( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - outStream << - "void main_vp(\n" - "float4 pos : POSITION,\n" - "float2 uv : TEXCOORD0,\n"; - if (tt != RENDER_COMPOSITE_MAP) - outStream << "float2 delta : TEXCOORD1,\n"; // lodDelta, lodThreshold - - outStream << - "uniform float4x4 worldMatrix,\n" - "uniform float4x4 viewProjMatrix,\n" - "uniform float2 lodMorph,\n"; // morph amount, morph LOD target - - // uv multipliers - uint maxLayers = prof->getMaxLayers(terrain); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - uint numUVMultipliers = (numLayers / 4); - if (numLayers % 4) - ++numUVMultipliers; - for (uint i = 0; i < numUVMultipliers; ++i) - outStream << "uniform float4 uvMul" << i << ", \n"; - - outStream << - "out float4 oPos : POSITION,\n" - "out float4 oPosObj : TEXCOORD0 \n"; - - uint texCoordSet = 1; - outStream << - ", out float4 oUVMisc : COLOR0 // xy = uv, z = camDepth\n"; - - // layer UV's premultiplied, packed as xy/zw - uint numUVSets = numLayers / 2; - if (numLayers % 2) - ++numUVSets; - if (tt != LOW_LOD) - { - for (uint i = 0; i < numUVSets; ++i) - { - outStream << - ", out float4 oUV" << i << " : TEXCOORD" << texCoordSet++ << "\n"; - } - } - - if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP) - { - outStream << ", out float2 lodInfo : TEXCOORD" << texCoordSet++ << "\n"; - } - - if (prof->isShadowingEnabled(tt, terrain)) - { - texCoordSet = generateVpDynamicShadowsParams(texCoordSet, prof, terrain, tt, outStream); - } - - // check we haven't exceeded texture coordinates - if (texCoordSet > 8) - { - OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, - "Requested options require too many texture coordinate sets! Try reducing the number of layers. requested: " + StringConverter::toString(texCoordSet), - __FUNCTION__); - } - - outStream << - ")\n" - "{\n" - " float4 worldPos = mul(worldMatrix, pos);\n" - " oPosObj = pos;\n"; - - if (tt != RENDER_COMPOSITE_MAP) - { - // determine whether to apply the LOD morph to this vertex - // we store the deltas against all vertices so we only want to apply - // the morph to the ones which would disappear. The target LOD which is - // being morphed to is stored in lodMorph.y, and the LOD at which - // the vertex should be morphed is stored in uv.w. If we subtract - // the former from the latter, and arrange to only morph if the - // result is negative (it will only be -1 in fact, since after that - // the vertex will never be indexed), we will achieve our aim. - // sign(vertexLOD - targetLOD) == -1 is to morph - outStream << - " float toMorph = -min(0, sign(delta.y - lodMorph.y));\n"; - // this will either be 1 (morph) or 0 (don't morph) - if (prof->getParent()->getDebugLevel()) - { - // x == LOD level (-1 since value is target level, we want to display actual) - outStream << "lodInfo.x = (lodMorph.y - 1) / " << terrain->getNumLodLevels() << ";\n"; - // y == LOD morph - outStream << "lodInfo.y = toMorph * lodMorph.x;\n"; - } - - // morph - switch (terrain->getAlignment()) - { - case Terrain::ALIGN_X_Y: - outStream << " worldPos.z += delta.x * toMorph * lodMorph.x;\n"; - break; - case Terrain::ALIGN_X_Z: - outStream << " worldPos.y += delta.x * toMorph * lodMorph.x;\n"; - break; - case Terrain::ALIGN_Y_Z: - outStream << " worldPos.x += delta.x * toMorph * lodMorph.x;\n"; - break; - }; - } - - - // generate UVs - if (tt != LOW_LOD) - { - for (uint i = 0; i < numUVSets; ++i) - { - uint layer = i * 2; - uint uvMulIdx = layer / 4; - - outStream << - " oUV" << i << ".xy = " << " uv.xy * uvMul" << uvMulIdx << "." << getChannel(layer) << ";\n"; - outStream << - " oUV" << i << ".zw = " << " uv.xy * uvMul" << uvMulIdx << "." << getChannel(layer+1) << ";\n"; - - } - - } - - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpHeader( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - - // Main header - outStream << - // helpers - "float4 expand(float4 v)\n" - "{ \n" - " return v * 2 - 1;\n" - "}\n\n\n"; - - if (prof->isShadowingEnabled(tt, terrain)) - generateFpDynamicShadowsHelpers(prof, terrain, tt, outStream); - - - outStream << - "void main_fp(\n" - "float4 position : TEXCOORD0,\n"; - - uint texCoordSet = 1; - outStream << - "float4 uvMisc : COLOR0,\n"; - - // UV's premultiplied, packed as xy/zw - uint maxLayers = prof->getMaxLayers(terrain); - uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); - uint numUVSets = numLayers / 2; - if (numLayers % 2) - ++numUVSets; - if (tt != LOW_LOD) - { - for (uint i = 0; i < numUVSets; ++i) - { - outStream << - "float4 layerUV" << i << " : TEXCOORD" << texCoordSet++ << ", \n"; - } - - } - if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP) - { - outStream << "float2 lodInfo : TEXCOORD" << texCoordSet++ << ", \n"; - } - - bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; - if (fog) - { - outStream << - "uniform float4 fogParams, \n" - "uniform float3 fogColour, \n"; - } - - uint currentSamplerIdx = 0; - - outStream << - // Only 1 light supported in this version - // deferred shading profile / generator later, ok? :) - "uniform float3 ambient,\n"; - - - for (int i=0; igetNumberOfLightsSupported(); ++i) - { - outStream << - "uniform float4 lightPosObjSpace"< 0) - outStream << - "uniform float4 lightAttenuation"<isGlobalColourMapEnabled()) - { - outStream << ", uniform sampler2D globalColourMap : register(s" - << currentSamplerIdx++ << ")\n"; - } - if (prof->isLightmapEnabled()) - { - outStream << ", uniform sampler2D lightMap : register(s" - << currentSamplerIdx++ << ")\n"; - } - // Blend textures - sampler definitions - for (uint i = 0; i < numBlendTextures; ++i) - { - outStream << ", uniform sampler2D blendTex" << i - << " : register(s" << currentSamplerIdx++ << ")\n"; - } - - // Layer textures - sampler definitions & UV multipliers - for (uint i = 0; i < numLayers; ++i) - { - outStream << ", uniform sampler2D difftex" << i - << " : register(s" << currentSamplerIdx++ << ")\n"; - - if (prof->mLayerNormalMappingEnabled || prof->mLayerParallaxMappingEnabled) - outStream << ", uniform sampler2D normtex" << i - << " : register(s" << currentSamplerIdx++ << ")\n"; - } - } - - if (prof->isShadowingEnabled(tt, terrain)) - { - generateFpDynamicShadowsParams(&texCoordSet, ¤tSamplerIdx, prof, terrain, tt, outStream); - } - - // check we haven't exceeded samplers - if (currentSamplerIdx > 16) - { - OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, - "Requested options require too many texture samplers! Try reducing the number of layers.", - __FUNCTION__); - } - - if (MWRender::RenderingManager::useMRT()) outStream << - " , out float4 oColor : COLOR \n" - " , out float4 oColor1 : COLOR1 \n" - " , uniform float far \n"; - else outStream << - " , out float4 oColor : COLOR \n"; - - outStream << - ")\n" - "{\n" - " float4 outputCol;\n" - " float shadow = 1.0;\n" - " float2 uv = uvMisc.xy;\n" - // base colour - " outputCol = float4(0,0,0,1);\n"; - - if (tt != LOW_LOD) - { - outStream << - // global normal - " float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n"; - - // not needed anymore apparently - //" normal = float3(normal.x, normal.z, -normal.y); \n"; // convert Ogre to MW coordinate system - - } - - for (int i=0; igetNumberOfLightsSupported(); ++i) - outStream << - " float3 lightDir"<isLayerNormalMappingEnabled()) - { - // derive the tangent space basis - // we do this in the pixel shader because we don't have per-vertex normals - // because of the LOD, we use a normal map - // tangent is always +x or -z in object space depending on alignment - switch(terrain->getAlignment()) - { - case Terrain::ALIGN_X_Y: - case Terrain::ALIGN_X_Z: - outStream << " float3 tangent = float3(1, 0, 0);\n"; - break; - case Terrain::ALIGN_Y_Z: - outStream << " float3 tangent = float3(0, 0, -1);\n"; - break; - }; - - outStream << " float3 binormal = normalize(cross(tangent, normal));\n"; - // note, now we need to re-cross to derive tangent again because it wasn't orthonormal - outStream << " tangent = normalize(cross(normal, binormal));\n"; - // derive final matrix - outStream << " float3x3 TBN = float3x3(tangent, binormal, normal);\n"; - - // set up lighting result placeholders for interpolation - outStream << " float4 litRes, litResLayer;\n"; - outStream << " float3 TSlightDir, TSeyeDir, TShalfAngle, TSnormal;\n"; - if (prof->isLayerParallaxMappingEnabled()) - outStream << " float displacement;\n"; - // move - outStream << " TSlightDir = normalize(mul(TBN, lightDir));\n"; - outStream << " TSeyeDir = normalize(mul(TBN, eyeDir));\n"; - - } - else - { - if (prof->getNumberOfLightsSupported() > 1) - outStream << "float d; \n" - "float attn; \n"; - - outStream << - " eyeDir = normalize(eyeDir); \n"; - - // simple per-pixel lighting with no normal mapping - for (int i=0; igetNumberOfLightsSupported(); ++i) - { - outStream << - " float4 litRes"< 0) - outStream << - // pre-multiply light color with attenuation factor - "d = length( lightDir"<_isSM3Available()) - outStream << " if (" << blendWeightStr << " > 0.0003)\n { \n"; - - - // generate UV - outStream << " float2 uv" << layer << " = layerUV" << uvIdx << uvChannels << ";\n"; - - // calculate lighting here if normal mapping - if (prof->isLayerNormalMappingEnabled()) - { - if (prof->isLayerParallaxMappingEnabled() && tt != RENDER_COMPOSITE_MAP) - { - // modify UV - note we have to sample an extra time - outStream << " displacement = tex2D(normtex" << layer << ", uv" << layer << ").a\n" - " * scaleBiasSpecular.x + scaleBiasSpecular.y;\n"; - outStream << " uv" << layer << " += TSeyeDir.xy * displacement;\n"; - } - - // access TS normal map - outStream << " TSnormal = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n"; - outStream << " TShalfAngle = normalize(TSlightDir + TSeyeDir);\n"; - outStream << " litResLayer = lit(dot(TSlightDir, TSnormal), dot(TShalfAngle, TSnormal), scaleBiasSpecular.z);\n"; - if (!layer) - outStream << " litRes = litResLayer;\n"; - else - outStream << " litRes = lerp(litRes, litResLayer, " << blendWeightStr << ");\n"; - - } - - // sample diffuse texture - outStream << " float4 diffuseSpecTex" << layer - << " = tex2D(difftex" << layer << ", uv" << layer << ");\n"; - - // apply to common - if (!layer) - { - outStream << " diffuse = diffuseSpecTex0.rgb;\n"; - if (prof->isLayerSpecularMappingEnabled()) - outStream << " specular = diffuseSpecTex0.a;\n"; - } - else - { - outStream << " diffuse = lerp(diffuse, diffuseSpecTex" << layer - << ".rgb, " << blendWeightStr << ");\n"; - if (prof->isLayerSpecularMappingEnabled()) - outStream << " specular = lerp(specular, diffuseSpecTex" << layer - << ".a, " << blendWeightStr << ");\n"; - - } - - // End early-out - // Disable - causing some issues even when trying to force the use of texldd - - // comment by scrawl: - // on a NVIDIA card in opengl mode, didn't produce any problems, - // while increasing FPS from 170 to 185 (!!!) in the same area - // so let's try this out - if something does cause problems for - // someone else (with a different card / renderer) we can just - // add a vendor-specific check here - if (layer && prof->_isSM3Available()) - outStream << " } // early-out blend value\n"; - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpFooter( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - - outStream << - " oPos = mul(viewProjMatrix, worldPos);\n" - " oUVMisc.xy = uv.xy;\n"; - - outStream << - " // pass cam depth\n" - " oPosObj.w = oPos.z;\n"; - - if (prof->isShadowingEnabled(tt, terrain)) - generateVpDynamicShadows(prof, terrain, tt, outStream); - - outStream << - "}\n"; - - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpFooter( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - - if (tt == LOW_LOD) - { - if (prof->isShadowingEnabled(tt, terrain)) - { - generateFpDynamicShadows(prof, terrain, tt, outStream); - outStream << - " outputCol.rgb = diffuse * rtshadow;\n"; - } - else - { - outStream << - " outputCol.rgb = diffuse;\n"; - } - } - else - { - if (prof->isGlobalColourMapEnabled()) - { - // sample colour map and apply to diffuse - outStream << " diffuse *= tex2D(globalColourMap, uv).rgb;\n"; - } - if (prof->isLightmapEnabled()) - { - // sample lightmap - outStream << " shadow = tex2D(lightMap, uv).r;\n"; - } - - if (prof->isShadowingEnabled(tt, terrain)) - { - generateFpDynamicShadows(prof, terrain, tt, outStream); - } - - outStream << " outputCol.rgb += ambient * diffuse; \n"; - - // diffuse lighting - for (int i=0; igetNumberOfLightsSupported(); ++i) - { - // shadows only for first light (directional) - if (i==0) - outStream << " outputCol.rgb += litRes"<isLayerSpecularMappingEnabled()) - outStream << " specular = 0.0;\n"; - - if (tt == RENDER_COMPOSITE_MAP) - { - // Lighting embedded in alpha - outStream << - " outputCol.a = shadow;\n"; - - } - else - { - // Apply specular - //outStream << " outputCol.rgb += litRes.z * lightSpecularColour * specular * shadow;\n"; - - if (prof->getParent()->getDebugLevel()) - { - outStream << " outputCol.rg += lodInfo.xy;\n"; - } - } - } - - bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; - if (fog) - { - if (terrain->getSceneManager()->getFogMode() == FOG_LINEAR) - { - outStream << - " float fogVal = saturate((position.w - fogParams.y) * fogParams.w);\n"; - } - else - { - outStream << - " float fogVal = saturate(1 / (exp(position.w * fogParams.x)));\n"; - } - outStream << " outputCol.rgb = lerp(outputCol.rgb, fogColour, fogVal);\n"; - } - - // Final return - outStream << " oColor = outputCol;\n"; - - if (MWRender::RenderingManager::useMRT()) outStream << - " oColor1 = float4(position.w / far, 0, 0, 1); \n"; - - outStream - << "}\n"; - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsHelpers( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - // TODO make filtering configurable - outStream << - "// Simple PCF \n" - "// Number of samples in one dimension (square for total samples) \n" - "#define NUM_SHADOW_SAMPLES_1D 1.0 \n" - "#define SHADOW_FILTER_SCALE 1 \n" - - "#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D \n" - - "float4 offsetSample(float4 uv, float2 offset, float invMapSize) \n" - "{ \n" - " return float4(uv.xy + offset * invMapSize * uv.w, uv.z, uv.w); \n" - "} \n"; - - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << - "float calcDepthShadow(sampler2D shadowMap, float4 shadowMapPos, float2 offset) \n" - " { \n" - " shadowMapPos = shadowMapPos / shadowMapPos.w; \n" - " float2 uv = shadowMapPos.xy; \n" - " float3 o = float3(offset, -offset.x) * 0.3f; \n" - " // Note: We using 2x2 PCF. Good enough and is alot faster. \n" - " float c = (shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.xy).r) ? 1 : 0; // top left \n" - " c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.xy).r) ? 1 : 0; // bottom right \n" - " c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.zy).r) ? 1 : 0; // bottom left \n" - " c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.zy).r) ? 1 : 0; // top right \n" - " return c / 4; \n" - " } \n"; - } - else - { - outStream << - "float calcSimpleShadow(sampler2D shadowMap, float4 shadowMapPos) \n" - "{ \n" - " return tex2Dproj(shadowMap, shadowMapPos).x; \n" - "} \n"; - - } - - if (prof->getReceiveDynamicShadowsPSSM()) - { - uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); - - - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << - "float calcPSSMDepthShadow("; - } - else - { - outStream << - "float calcPSSMSimpleShadow("; - } - - outStream << "\n "; - for (uint i = 0; i < numTextures; ++i) - outStream << "sampler2D shadowMap" << i << ", "; - outStream << "\n "; - for (uint i = 0; i < numTextures; ++i) - outStream << "float4 lsPos" << i << ", "; - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << "\n "; - for (uint i = 0; i < numTextures; ++i) - outStream << "float2 invShadowmapSize" << i << ", "; - } - outStream << "\n" - " float4 pssmSplitPoints, float camDepth) \n" - "{ \n" - " float shadow; \n" - " // calculate shadow \n"; - - for (uint i = 0; i < numTextures; ++i) - { - if (!i) - outStream << " if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n"; - else if (i < numTextures - 1) - outStream << " else if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n"; - else - outStream << " else \n"; - - outStream << - " { \n"; - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << - " shadow = calcDepthShadow(shadowMap" << i << ", lsPos" << i << ", invShadowmapSize" << i << ".xy); \n"; - } - else - { - outStream << - " shadow = calcSimpleShadow(shadowMap" << i << ", lsPos" << i << "); \n"; - } - outStream << - " } \n"; - - } - - outStream << - " return shadow; \n" - "} \n\n\n"; - } - - - } - //--------------------------------------------------------------------- - uint TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpDynamicShadowsParams( - uint texCoord, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - // out semantics & params - uint numTextures = 1; - if (prof->getReceiveDynamicShadowsPSSM()) - { - numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); - } - for (uint i = 0; i < numTextures; ++i) - { - outStream << - ", out float4 oLightSpacePos" << i << " : TEXCOORD" << texCoord++ << " \n" << - ", uniform float4x4 texViewProjMatrix" << i << " \n"; - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << - ", uniform float4 depthRange" << i << " // x = min, y = max, z = range, w = 1/range \n"; - } - } - - return texCoord; - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpDynamicShadows( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - uint numTextures = 1; - if (prof->getReceiveDynamicShadowsPSSM()) - { - numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); - } - - // Calculate the position of vertex in light space - for (uint i = 0; i < numTextures; ++i) - { - outStream << - " oLightSpacePos" << i << " = mul(texViewProjMatrix" << i << ", worldPos); \n"; - if (prof->getReceiveDynamicShadowsDepth()) - { - // make linear - //outStream << - // "oLightSpacePos" << i << ".z = (oLightSpacePos" << i << ".z - depthRange" << i << ".x) * depthRange" << i << ".w;\n"; - - } - } - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsParams( - uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - if (tt == HIGH_LOD) - mShadowSamplerStartHi = *sampler; - else if (tt == LOW_LOD) - mShadowSamplerStartLo = *sampler; - - // in semantics & params - uint numTextures = 1; - outStream << - ", uniform float4 shadowFar_fadeStart \n"; - if (prof->getReceiveDynamicShadowsPSSM()) - { - numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); - outStream << - ", uniform float4 pssmSplitPoints \n"; - } - for (uint i = 0; i < numTextures; ++i) - { - outStream << - ", float4 lightSpacePos" << i << " : TEXCOORD" << *texCoord << " \n" << - ", uniform sampler2D shadowMap" << i << " : register(s" << *sampler << ") \n"; - *sampler = *sampler + 1; - *texCoord = *texCoord + 1; - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << - ", uniform float4 inverseShadowmapSize" << i << " \n"; - } - } - - } - //--------------------------------------------------------------------- - void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadows( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) - { - if (prof->getReceiveDynamicShadowsPSSM()) - { - uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); - outStream << - " float camDepth = position.w;\n"; - - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << - " float rtshadow = calcPSSMDepthShadow("; - } - else - { - outStream << - " float rtshadow = calcPSSMSimpleShadow("; - } - for (uint i = 0; i < numTextures; ++i) - outStream << "shadowMap" << i << ", "; - outStream << "\n "; - - for (uint i = 0; i < numTextures; ++i) - outStream << "lightSpacePos" << i << ", "; - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << "\n "; - for (uint i = 0; i < numTextures; ++i) - outStream << "inverseShadowmapSize" << i << ".xy, "; - } - outStream << "\n" << - " pssmSplitPoints, camDepth);\n"; - - } - else - { - if (prof->getReceiveDynamicShadowsDepth()) - { - outStream << - " float rtshadow = calcDepthShadow(shadowMap0, lightSpacePos0, inverseShadowmapSize0.xy);"; - } - else - { - outStream << - " float rtshadow = calcSimpleShadow(shadowMap0, lightSpacePos0);"; - } - } - - outStream << - " float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; \n" - " float fade = 1-((position.w - shadowFar_fadeStart.y) / fadeRange); \n" - " rtshadow = (position.w > shadowFar_fadeStart.x) ? 1 : ((position.w > shadowFar_fadeStart.y) ? 1-((1-rtshadow)*fade) : rtshadow); \n" - " rtshadow = (1-(1-rtshadow)*0.6); \n" // make the shadow a little less intensive - " shadow = min(shadow, rtshadow);\n"; - - } - //--------------------------------------------------------------------- - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelperHLSL::createVertexProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - String progName = getVertexProgramName(prof, terrain, tt); - - HighLevelGpuProgramPtr ret = mgr.getByName(progName); - if (ret.isNull()) - { - ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "hlsl", GPT_VERTEX_PROGRAM); - } - else - { - ret->unload(); - } - - if (prof->_isSM3Available()) - ret->setParameter("target", "vs_3_0"); - else - ret->setParameter("target", "vs_2_0"); - ret->setParameter("entry_point", "main_vp"); - - return ret; - - } - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelperHLSL::createFragmentProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - String progName = getFragmentProgramName(prof, terrain, tt); - - - HighLevelGpuProgramPtr ret = mgr.getByName(progName); - if (ret.isNull()) - { - ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "hlsl", GPT_FRAGMENT_PROGRAM); - } - else - { - ret->unload(); - } - - if (prof->_isSM3Available()) - ret->setParameter("target", "ps_3_0"); - else - ret->setParameter("target", "ps_2_x"); - ret->setParameter("entry_point", "main_fp"); - - return ret; - - } - //--------------------------------------------------------------------- - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelperGLSL::createVertexProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - String progName = getVertexProgramName(prof, terrain, tt); - - switch(tt) - { - case HIGH_LOD: - progName += "/hlod"; - break; - case LOW_LOD: - progName += "/llod"; - break; - case RENDER_COMPOSITE_MAP: - progName += "/comp"; - break; - } - - HighLevelGpuProgramPtr ret = mgr.getByName(progName); - if (ret.isNull()) - { - ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "glsl", GPT_VERTEX_PROGRAM); - } - else - { - ret->unload(); - } - - return ret; - - } - //--------------------------------------------------------------------- - HighLevelGpuProgramPtr - TerrainMaterialGeneratorB::SM2Profile::ShaderHelperGLSL::createFragmentProgram( - const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) - { - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - String progName = getFragmentProgramName(prof, terrain, tt); - - HighLevelGpuProgramPtr ret = mgr.getByName(progName); - if (ret.isNull()) - { - ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "glsl", GPT_FRAGMENT_PROGRAM); - } - else - { - ret->unload(); - } - - return ret; - - } + void TerrainMaterial::Profile::updateParamsForCompositeMap(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) + { + } + void TerrainMaterial::Profile::requestOptions(Ogre::Terrain* terrain) + { + terrain->_setMorphRequired(true); + terrain->_setNormalMapRequired(false); + terrain->_setLightMapRequired(false); + terrain->_setCompositeMapRequired(false); + } } diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index e94e20b7e..7cafbb97a 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -26,246 +26,50 @@ THE SOFTWARE. ----------------------------------------------------------------------------- */ -#ifndef __Ogre_TerrainMaterialGeneratorB_H__ -#define __Ogre_TerrainMaterialGeneratorB_H__ +#ifndef MWRENDER_TERRAINMATERIAL_H +#define MWRENDER_TERRAINMATERIAL_H #include "OgreTerrainPrerequisites.h" #include "OgreTerrainMaterialGenerator.h" #include "OgreGpuProgramParams.h" -namespace Ogre +namespace MWRender { - class PSSMShadowCameraSetup; - /** \addtogroup Optional Components - * @{ - */ - /** \addtogroup Terrain - * Some details on the terrain component - * @{ - */ + class TerrainMaterial : public Ogre::TerrainMaterialGenerator + { + public: + class Profile : public Ogre::TerrainMaterialGenerator::Profile + { + protected: + Ogre::TerrainMaterialGenerator* mParent; + Ogre::String mName; + Ogre::String mDesc; + public: + Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc); + virtual ~Profile(); - /** A TerrainMaterialGenerator which can cope with normal mapped, specular mapped - terrain. - @note Requires the Cg plugin to render correctly - */ - class TerrainMaterialGeneratorB : public TerrainMaterialGenerator - { - public: - TerrainMaterialGeneratorB(); - ~TerrainMaterialGeneratorB(); + virtual bool isVertexCompressionSupported() const { return false; } - /** Shader model 2 profile target. - */ - class SM2Profile : public TerrainMaterialGenerator::Profile - { - public: - SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc); - ~SM2Profile(); + virtual Ogre::MaterialPtr generate(const Ogre::Terrain* terrain); - bool isVertexCompressionSupported() const {return false;} - - MaterialPtr generate(const Terrain* terrain); - MaterialPtr generateForCompositeMap(const Terrain* terrain); - uint8 getMaxLayers(const Terrain* terrain) const; - void updateParams(const MaterialPtr& mat, const Terrain* terrain); - void updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain); - void requestOptions(Terrain* terrain); + virtual Ogre::MaterialPtr generateForCompositeMap(const Ogre::Terrain* terrain); - void setShadowFar(float far); - void setShadowFadeStart(float fadestart); + virtual Ogre::uint8 getMaxLayers(const Ogre::Terrain* terrain) const; - /** Whether to support normal mapping per layer in the shader (default true). - */ - bool isLayerNormalMappingEnabled() const { return mLayerNormalMappingEnabled; } - /** Whether to support normal mapping per layer in the shader (default true). - */ - void setLayerNormalMappingEnabled(bool enabled); - /** Whether to support parallax mapping per layer in the shader (default true). - */ - bool isLayerParallaxMappingEnabled() const { return mLayerParallaxMappingEnabled; } - /** Whether to support parallax mapping per layer in the shader (default true). - */ - void setLayerParallaxMappingEnabled(bool enabled); - /** Whether to support specular mapping per layer in the shader (default true). - */ - bool isLayerSpecularMappingEnabled() const { return mLayerSpecularMappingEnabled; } - /** Whether to support specular mapping per layer in the shader (default true). - */ - void setLayerSpecularMappingEnabled(bool enabled); - /** Whether to support a global colour map over the terrain in the shader, - if it's present (default true). - */ - bool isGlobalColourMapEnabled() const { return mGlobalColourMapEnabled; } - /** Whether to support a global colour map over the terrain in the shader, - if it's present (default true). - */ - void setGlobalColourMapEnabled(bool enabled); - /** Whether to support a light map over the terrain in the shader, - if it's present (default true). - */ - bool isLightmapEnabled() const { return mLightmapEnabled; } - /** Whether to support a light map over the terrain in the shader, - if it's present (default true). - */ - void setLightmapEnabled(bool enabled); - /** Whether to use the composite map to provide a lower LOD technique - in the distance (default true). - */ - bool isCompositeMapEnabled() const { return mCompositeMapEnabled; } - /** Whether to use the composite map to provide a lower LOD technique - in the distance (default true). - */ - void setCompositeMapEnabled(bool enabled); - /** Whether to support dynamic texture shadows received from other - objects, on the terrain (default true). - */ - bool getReceiveDynamicShadowsEnabled() const { return mReceiveDynamicShadows; } - /** Whether to support dynamic texture shadows received from other - objects, on the terrain (default true). - */ - void setReceiveDynamicShadowsEnabled(bool enabled); + virtual void updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain); - /** Whether to use PSSM support dynamic texture shadows, and if so the - settings to use (default 0). - */ - void setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings); - /** Whether to use PSSM support dynamic texture shadows, and if so the - settings to use (default 0). - */ - PSSMShadowCameraSetup* getReceiveDynamicShadowsPSSM() const { return mPSSM; } - /** Whether to use depth shadows (default false). - */ - void setReceiveDynamicShadowsDepth(bool enabled); - /** Whether to use depth shadows (default false). - */ - bool getReceiveDynamicShadowsDepth() const { return mDepthShadows; } - /** Whether to use shadows on low LOD material rendering (when using composite map) (default false). - */ - void setReceiveDynamicShadowsLowLod(bool enabled); - /** Whether to use shadows on low LOD material rendering (when using composite map) (default false). - */ - bool getReceiveDynamicShadowsLowLod() const { return mLowLodShadows; } + virtual void updateParamsForCompositeMap(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain); - int getNumberOfLightsSupported() const; + virtual void requestOptions(Ogre::Terrain* terrain); - /// Internal - bool _isSM3Available() const { return mSM3Available; } - - protected: - - enum TechniqueType - { - HIGH_LOD, - LOW_LOD, - RENDER_COMPOSITE_MAP - }; - void addTechnique(const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt); - - /// Interface definition for helper class to generate shaders - class ShaderHelper : public TerrainAlloc - { - public: - ShaderHelper() : mShadowSamplerStartHi(0), mShadowSamplerStartLo(0) {} - virtual ~ShaderHelper() {} - virtual HighLevelGpuProgramPtr generateVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - virtual HighLevelGpuProgramPtr generateFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - virtual void updateParams(const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap); - protected: - virtual String getVertexProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - virtual String getFragmentProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - virtual HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0; - virtual HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0; - virtual void generateVertexProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - virtual void generateFragmentProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - virtual void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; - virtual void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; - virtual void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0; - virtual void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0; - virtual void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; - virtual void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; - virtual void defaultVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog); - virtual void defaultFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog); - virtual void updateVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params); - virtual void updateFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params); - static String getChannel(uint idx); - - size_t mShadowSamplerStartHi; - size_t mShadowSamplerStartLo; - - }; - - /// Utility class to help with generating shaders for Cg / HLSL. - class ShaderHelperCg : public ShaderHelper - { - protected: - HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream); - void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream); - void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - uint generateVpDynamicShadowsParams(uint texCoordStart, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - void generateVpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - void generateFpDynamicShadowsHelpers(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - void generateFpDynamicShadowsParams(uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - void generateFpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); - }; - - class ShaderHelperHLSL : public ShaderHelperCg - { - protected: - HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - }; - - /// Utility class to help with generating shaders for GLSL. - class ShaderHelperGLSL : public ShaderHelper - { - protected: - HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); - void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} - void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} - void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {} - void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {} - void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} - void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} - }; - - ShaderHelper* mShaderGen; - bool mLayerNormalMappingEnabled; - bool mLayerParallaxMappingEnabled; - bool mLayerSpecularMappingEnabled; - bool mGlobalColourMapEnabled; - bool mLightmapEnabled; - bool mCompositeMapEnabled; - bool mReceiveDynamicShadows; - PSSMShadowCameraSetup* mPSSM; - bool mDepthShadows; - bool mLowLodShadows; - bool mSM3Available; - float mShadowFar; - float mShadowFadeStart; - - bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const; - - }; - - - - - }; - - - - /** @} */ - /** @} */ + }; + TerrainMaterial(); + }; } + #endif diff --git a/extern/shiny b/extern/shiny index 7485a15c2..ca0549ae6 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 7485a15c26f129084a1c264fa1a98dc2de86f298 +Subproject commit ca0549ae6db7aa9efb125335942c61fb3f583a8d diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 76e64112b..96c6f8422 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -6,9 +6,6 @@ material openmw_objects_base emissive 0.0 0.0 0.0 has_vertex_colour false diffuseMap black.png - fog true - mrt_output true - lighting true is_transparent false // real transparency, alpha rejection doesn't count here scene_blend default @@ -25,9 +22,6 @@ material openmw_objects_base shader_properties { - fog $fog - mrt_output $mrt_output - lighting $lighting has_vertex_colour $has_vertex_colour is_transparent $is_transparent } diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 3b4992f5b..c343a32b0 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -3,9 +3,9 @@ #include "shadows.h" -#define FOG @shPropertyBool(fog) -#define MRT @shPropertyNotBool(is_transparent) && @shPropertyBool(mrt_output) && @shGlobalSettingBool(mrt_output) -#define LIGHTING @shPropertyBool(lighting) +#define FOG @shGlobalSettingBool(fog) +#define MRT @shPropertyNotBool(is_transparent) && @shGlobalSettingBool(mrt_output) +#define LIGHTING @shGlobalSettingBool(lighting) #define SHADOWS_PSSM LIGHTING && @shGlobalSettingBool(shadows_pssm) #define SHADOWS LIGHTING && @shGlobalSettingBool(shadows) diff --git a/files/materials/openmw.configuration b/files/materials/openmw.configuration index 6122cd8cb..cddf49a03 100644 --- a/files/materials/openmw.configuration +++ b/files/materials/openmw.configuration @@ -1,15 +1,16 @@ configuration water_reflection { fog false - receives_shadows false + shadows false + shadows_pssm false mrt_output false } configuration local_map { fog false - receives_shadows false - simple_water true mrt_output false lighting false + shadows false + shadows_pssm false } diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader new file mode 100644 index 000000000..8cdcb339e --- /dev/null +++ b/files/materials/terrain.shader @@ -0,0 +1,57 @@ +#include "core.h" + +#define FOG @shPropertyBool(fog) +#define MRT @shGlobalSettingBool(mrt_output) + + +@shAllocatePassthrough(1, depth) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) + shUniform(float4x4, viewProjMatrix) @shAutoConstant(viewProjMatrix, viewproj_matrix) + + shInput(float2, uv0) + + @shPassthroughVertexOutputs + + SH_START_PROGRAM + { + + + float4 worldPos = shMatrixMult(worldMatrix, shInputPosition); + + + shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); + + @shPassthroughAssign(depth, shOutputPosition.z); + + } + +#else + + SH_BEGIN_PROGRAM + + + + @shPassthroughFragmentInputs + +#if MRT + shDeclareMrtOutput(1) +#endif + + + + SH_START_PROGRAM + { + + float depth = @shPassthroughReceive(depth); + + +#if MRT + //shOutputColour(1) = float4(1,1,1,1); +#endif + } + +#endif diff --git a/files/materials/terrain.shaderset b/files/materials/terrain.shaderset new file mode 100644 index 000000000..d31429dbb --- /dev/null +++ b/files/materials/terrain.shaderset @@ -0,0 +1,15 @@ +shader_set terrain_vertex +{ + source terrain.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set terrain_fragment +{ + source terrain.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} From 7a2d61304310abbdf081c34c12a300dffbf316bb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 13 Jul 2012 08:50:46 +0200 Subject: [PATCH 115/688] Issue #314: extended Class::apply function to cover the actor --- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index f51aa401c..2ae6cb695 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -34,7 +34,7 @@ namespace MWWorld } - bool Class::apply (const MWWorld::Ptr& ptr, const std::string& id) const + bool Class::apply (const MWWorld::Ptr& ptr, const std::string& id, const MWWorld::Ptr& actor) const { return false; } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 1a3113f7c..968ae7917 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -165,8 +165,10 @@ namespace MWWorld /// effects). Throws an exception, if the object can't hold other objects. /// (default implementation: throws an exception) - virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id) const; + virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id, + const MWWorld::Ptr& actor) const; ///< Apply \a id on \a ptr. + /// \param actor Actor that is resposible for the ID being applied to \a ptr. /// \return Any effect? /// /// (default implementation: ignore and return false) From 805b81bf6e61b65f096d7a127538fa46e384ef6a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 13 Jul 2012 09:03:17 +0200 Subject: [PATCH 116/688] Issue #314: added skillUsageSucceeded function to Class --- apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 2ae6cb695..356eef9a9 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -39,6 +39,11 @@ namespace MWWorld return false; } + void Class::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const + { + throw std::runtime_error ("class does not represent an actor"); + } + MWMechanics::CreatureStats& Class::getCreatureStats (const Ptr& ptr) const { throw std::runtime_error ("class does not have creature stats"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 968ae7917..352c7bda1 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -173,6 +173,11 @@ namespace MWWorld /// /// (default implementation: ignore and return false) + virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const; + ///< Inform actor \a ptr that a skill use has succeeded. + /// + /// (default implementations: throws an exception) + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. From 7731e9a6fac11623e9b24fd52efb95b8920c640f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jul 2012 09:13:12 +0200 Subject: [PATCH 117/688] finished cleaning up sky --- apps/openmw/mwrender/sky.cpp | 301 ++++++----------------- apps/openmw/mwrender/sky.hpp | 21 +- apps/openmw/mwrender/terrain.cpp | 2 +- apps/openmw/mwrender/terrainmaterial.cpp | 20 +- apps/openmw/mwrender/terrainmaterial.hpp | 12 + apps/openmw/mwworld/weather.cpp | 13 +- extern/shiny | 2 +- files/materials/moon.shader | 52 ++++ files/materials/moon.shaderset | 15 ++ files/materials/sky.mat | 11 +- files/materials/sun.shader | 6 +- files/materials/terrain.shader | 4 +- 12 files changed, 201 insertions(+), 258 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 65a9e8953..f84c12b4e 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -19,6 +19,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -31,9 +33,33 @@ using namespace Ogre; BillboardObject::BillboardObject( const String& textureName, const float initialSize, const Vector3& position, - SceneNode* rootNode) + SceneNode* rootNode, + const std::string& material) { - init(textureName, initialSize, position, rootNode); + SceneManager* sceneMgr = rootNode->getCreator(); + + Vector3 finalPosition = position.normalisedCopy() * 1000.f; + + static unsigned int bodyCount=0; + + /// \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->setBillboardType(BBT_PERPENDICULAR_COMMON); + mBBSet->setCommonDirection( -position.normalisedCopy() ); + mBBSet->setVisibilityFlags(RV_Sky); + mNode = rootNode->createChildSceneNode(); + mNode->setPosition(finalPosition); + mNode->attachObject(mBBSet); + mBBSet->createBillboard(0,0,0); + mBBSet->setCastShadows(false); + + mMaterial = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), material); + mMaterial->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + + mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); + + bodyCount++; } BillboardObject::BillboardObject() @@ -52,7 +78,13 @@ void BillboardObject::setSize(const float size) void BillboardObject::setVisibility(const float visibility) { - //mMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, visibility); + Ogre::MaterialPtr m = static_cast(mMaterial->getMaterial ())->getOgreMaterial (); + for (int i=0; igetNumTechniques(); ++i) + { + Ogre::Technique* t = m->getTechnique(i); + if (t->getNumPasses ()) + t->getPass(0)->setDiffuse (0,0,0, visibility); + } } void BillboardObject::setPosition(const Vector3& pPosition) @@ -78,6 +110,13 @@ void BillboardObject::setVisibilityFlags(int flags) void BillboardObject::setColour(const ColourValue& pColour) { + Ogre::MaterialPtr m = static_cast(mMaterial->getMaterial ())->getOgreMaterial (); + for (int i=0; igetNumTechniques(); ++i) + { + Ogre::Technique* t = m->getTechnique(i); + if (t->getNumPasses ()) + t->getPass(0)->setSelfIllumination (pColour); + } //mMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(pColour); } @@ -91,187 +130,13 @@ SceneNode* BillboardObject::getNode() return mNode; } -void BillboardObject::init(const String& textureName, - const float initialSize, - const Vector3& position, - SceneNode* rootNode) -{ - SceneManager* sceneMgr = rootNode->getCreator(); - - Vector3 finalPosition = position.normalisedCopy() * 1000.f; - - static unsigned int bodyCount=0; - - /// \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->setBillboardType(BBT_PERPENDICULAR_COMMON); - mBBSet->setCommonDirection( -position.normalisedCopy() ); - mBBSet->setVisibilityFlags(RV_Sky); - mNode = rootNode->createChildSceneNode(); - mNode->setPosition(finalPosition); - mNode->attachObject(mBBSet); - mBBSet->createBillboard(0,0,0); - mBBSet->setCastShadows(false); - - sh::MaterialInstance* m = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), "openmw_sun"); - m->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); - - mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); - - /* - mMaterial = MaterialManager::getSingleton().create("BillboardMaterial"+StringConverter::toString(bodyCount), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - mMaterial->removeAllTechniques(); - Pass* p = mMaterial->createTechnique()->createPass(); - p->setSceneBlending(SBT_TRANSPARENT_ALPHA); - p->setDepthCheckEnabled(false); - p->setDepthWriteEnabled(false); - p->setSelfIllumination(1.0,1.0,1.0); - p->setDiffuse(0.0,0.0,0.0,1.0); - p->setAmbient(0.0,0.0,0.0); - p->setPolygonModeOverrideable(false); - p->createTextureUnitState(textureName); - - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - HighLevelGpuProgramPtr vshader; - if (mgr.resourceExists("BBO_VP")) - vshader = mgr.getByName("BBO_VP"); - else - vshader = mgr.createProgram("BBO_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); - vshader->setParameter("profiles", "vs_2_x arbvp1"); - vshader->setParameter("entry_point", "main_vp"); - StringUtil::StrStreamType outStream; - outStream << - "void main_vp( \n" - " float4 position : POSITION, \n" - " in float2 uv : TEXCOORD0, \n" - " out float2 oUV : TEXCOORD0, \n" - " out float4 oPosition : POSITION, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" - " oUV = uv; \n" - " oPosition = mul( worldViewProj, position ); \n" - "}"; - vshader->setSource(outStream.str()); - vshader->load(); - vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - mMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); - - HighLevelGpuProgramPtr fshader; - if (mgr.resourceExists("BBO_FP")) - fshader = mgr.getByName("BBO_FP"); - else - fshader = mgr.createProgram("BBO_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); - - fshader->setParameter("profiles", "ps_2_x arbfp1"); - fshader->setParameter("entry_point", "main_fp"); - StringUtil::StrStreamType outStream2; - outStream2 << - "void main_fp( \n" - " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n"; - if (RenderingManager::useMRT()) outStream2 << - " out float4 oColor1 : COLOR1, \n"; - outStream2 << - " uniform sampler2D texture : TEXUNIT0, \n" - " uniform float4 diffuse, \n" - " uniform float4 emissive \n" - ") \n" - "{ \n" - " float4 tex = tex2D(texture, uv); \n" - " oColor = float4(emissive.xyz,1) * tex * float4(1,1,1,diffuse.a); \n"; - if (RenderingManager::useMRT()) outStream2 << - " oColor1 = float4(1, 0, 0, 1); \n"; - outStream2 << - "}"; - fshader->setSource(outStream2.str()); - fshader->load(); - fshader->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - mMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); -*/ - bodyCount++; -} - Moon::Moon( const String& textureName, const float initialSize, const Vector3& position, - SceneNode* rootNode) + SceneNode* rootNode, + const std::string& material) + : BillboardObject(textureName, initialSize, position, rootNode, material) { - init(textureName, initialSize, position, rootNode); - - - /* - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - HighLevelGpuProgramPtr vshader; - if (mgr.resourceExists("Moon_VP")) - vshader = mgr.getByName("Moon_VP"); - else - vshader = mgr.createProgram("Moon_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); - vshader->setParameter("profiles", "vs_2_x arbvp1"); - vshader->setParameter("entry_point", "main_vp"); - StringUtil::StrStreamType outStream; - outStream << - "void main_vp( \n" - " float4 position : POSITION, \n" - " in float2 uv : TEXCOORD0, \n" - " out float2 oUV : TEXCOORD0, \n" - " out float4 oPosition : POSITION, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" - " oUV = uv; \n" - " oPosition = mul( worldViewProj, position ); \n" - "}"; - vshader->setSource(outStream.str()); - vshader->load(); - vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - mMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); - - HighLevelGpuProgramPtr fshader; - if (mgr.resourceExists("Moon_FP")) - fshader = mgr.getByName("Moon_FP"); - else - fshader = mgr.createProgram("Moon_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); - - fshader->setParameter("profiles", "ps_2_x arbfp1"); - fshader->setParameter("entry_point", "main_fp"); - StringUtil::StrStreamType outStream2; - outStream2 << - "void main_fp( \n" - " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n"; - if (RenderingManager::useMRT()) outStream2 << - " out float4 oColor1 : COLOR1, \n"; - outStream2 << - " uniform sampler2D texture : TEXUNIT0, \n" - " uniform float4 skyColour, \n" - " uniform float4 diffuse, \n" - " uniform float4 emissive \n" - ") \n" - "{ \n" - " float4 tex = tex2D(texture, uv); \n" - " oColor = float4(emissive.xyz,1) * tex; \n"; - if (RenderingManager::useMRT()) outStream2 << - " oColor1 = float4(1, 0, 0, 1); \n"; - outStream2 << - // use a circle for the alpha (compute UV distance to center) - // looks a bit bad because its not filtered on the edges, - // but it's cheaper than a seperate alpha texture. - " float sqrUVdist = pow(uv.x-0.5,2) + pow(uv.y-0.5, 2); \n" - " oColor.a = diffuse.a * (sqrUVdist >= 0.24 ? 0 : 1); \n" - " oColor.rgb += (1-tex.a) * oColor.a * skyColour.rgb; \n"//fill dark side of moon with skycolour - " oColor.rgb += (1-diffuse.a) * skyColour.rgb; \n"//fade bump - "}"; - fshader->setSource(outStream2.str()); - fshader->load(); - fshader->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - mMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); - - */ - setVisibility(1.0); mPhase = Moon::Phase_Full; @@ -282,11 +147,6 @@ void Moon::setType(const Moon::Type& type) mType = type; } -void Moon::setSkyColour(const Ogre::ColourValue& colour) -{ - //mMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("skyColour", colour); -} - void Moon::setPhase(const Moon::Phase& phase) { // Colour texture @@ -306,7 +166,10 @@ void Moon::setPhase(const Moon::Phase& phase) textureName += ".dds"; - //mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(textureName); + if (mType == Moon::Type_Secunda) + sh::Factory::getInstance ().setTextureAlias ("secunda_texture", textureName); + else + sh::Factory::getInstance ().setTextureAlias ("masser_texture", textureName); mPhase = phase; } @@ -355,25 +218,12 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType) else if (i>= 33 && i <= 48) alpha = 64; // second bottom-most row else alpha = 255; } - + // NB we would have to swap R and B depending on rendersystem specific VertexElementType, but doesn't matter since they are both 1 uint8 tmpR = static_cast(255); uint8 tmpG = static_cast(255); uint8 tmpB = static_cast(255); uint8 tmpA = static_cast(alpha); - // This does not matter since R and B are always 1. - /*VertexElementType format = Root::getSingleton().getRenderSystem()->getColourVertexElementType(); - switch (format) - { - case VET_COLOUR_ARGB: - std::swap(tmpR, tmpB); - break; - case VET_COLOUR_ABGR: - break; - default: - break; - }*/ - // Modify *((uint32*)currentVertex) = tmpR | (tmpG << 8) | (tmpB << 16) | (tmpA << 24); @@ -405,8 +255,6 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mCloudOpacity(0.0f) , mCloudSpeed(0.0f) , mStarsOpacity(0.0f) - , mThunderOverlay(NULL) - , mThunderTextureUnit(NULL) , mRemainingTransitionTime(0.0f) , mGlareFade(0.0f) , mGlare(0.0f) @@ -416,6 +264,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mSecundaEnabled(true) , mCreated(false) , mCloudAnimationTimer(0.f) + , mMoonRed(false) { mSceneMgr = pMwRoot->getCreator(); mRootNode = mCamera->getParentSceneNode()->createChildSceneNode(); @@ -440,34 +289,23 @@ void SkyManager::create() sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", ""); // Create overlay used for thunderstorm - MaterialPtr material = MaterialManager::getSingleton().create( "ThunderMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); - Pass* pass = material->getTechnique(0)->getPass(0); - pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); - mThunderTextureUnit = pass->createTextureUnitState(); - mThunderTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(1.f, 1.f, 1.f)); - mThunderTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 0.5f); - OverlayManager& ovm = OverlayManager::getSingleton(); - mThunderOverlay = ovm.create( "ThunderOverlay" ); - OverlayContainer* overlay_panel; - overlay_panel = (OverlayContainer*)ovm.createOverlayElement("Panel", "ThunderPanel"); - overlay_panel->_setPosition(0, 0); - overlay_panel->_setDimensions(1, 1); - overlay_panel->setMaterialName( "ThunderMaterial" ); - overlay_panel->show(); - mThunderOverlay->add2D(overlay_panel); - mThunderOverlay->hide(); + mLightning = mSceneMgr->createLight(); + mLightning->setType (Ogre::Light::LT_DIRECTIONAL); + mLightning->setDirection (Ogre::Vector3(0.3, -0.7, 0.3)); + mLightning->setVisible (false); + mLightning->setDiffuseColour (ColourValue(3,3,3)); - mSecunda = new Moon("textures\\tx_secunda_full.dds", 0.5, Vector3(-0.4, 0.4, 0.5), mRootNode); + mSecunda = new Moon("secunda_texture", 0.5, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); mSecunda->setType(Moon::Type_Secunda); mSecunda->setRenderQueue(RQG_SkiesEarly+4); - mMasser = new Moon("textures\\tx_masser_full.dds", 0.75, Vector3(-0.4, 0.4, 0.5), mRootNode); + mMasser = new Moon("masser_texture", 0.75, Vector3(-0.4, 0.4, 0.5), mRootNode, "openmw_moon"); mMasser->setRenderQueue(RQG_SkiesEarly+3); mMasser->setType(Moon::Type_Masser); - mSun = new BillboardObject("textures\\tx_sun_05.dds", 1, Vector3(0.4, 0.4, 0.4), mRootNode); + mSun = new BillboardObject("textures\\tx_sun_05.dds", 1, Vector3(0.4, 0.4, 0.4), mRootNode, "openmw_sun"); mSun->setRenderQueue(RQG_SkiesEarly+4); - mSunGlare = new BillboardObject("textures\\tx_sun_flash_grey_05.dds", 3, Vector3(0.4, 0.4, 0.4), mRootNode); + mSunGlare = new BillboardObject("textures\\tx_sun_flash_grey_05.dds", 3, Vector3(0.4, 0.4, 0.4), mRootNode, "openmw_sun"); mSunGlare->setRenderQueue(RQG_SkiesLate); mSunGlare->setVisibilityFlags(RV_Glare); @@ -556,6 +394,8 @@ void SkyManager::update(float duration) mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); + mSecunda->setColour ( mMoonRed ? ColourValue(1.0, 0.0784, 0.0784) : ColourValue(1,1,1,1)); + mMasser->setColour (ColourValue(1,1,1,1)); if (mSunEnabled) { @@ -609,9 +449,7 @@ void SkyManager::disable() void SkyManager::setMoonColour (bool red) { - if (!mCreated) return; - mSecunda->setColour( red ? ColourValue(1.0, 0.0784, 0.0784) - : ColourValue(1.0, 1.0, 1.0)); + mMoonRed = red; } void SkyManager::setWeather(const MWWorld::WeatherResult& weather) @@ -755,16 +593,21 @@ void SkyManager::secundaDisable() mSecundaEnabled = false; } -void SkyManager::setThunder(const float factor) +void SkyManager::setLightningStrength(const float factor) { if (!mCreated) return; if (factor > 0.f) { - mThunderOverlay->show(); - mThunderTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, factor*0.6); + mLightning->setDiffuseColour (ColourValue(2*factor, 2*factor, 2*factor)); + mLightning->setVisible(true); } else - mThunderOverlay->hide(); + mLightning->setVisible(false); +} + +void SkyManager::setLightningDirection(const Ogre::Vector3& dir) +{ + mLightning->setDirection (dir); } void SkyManager::setMasserFade(const float fade) diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index f59583c04..4a0a7b36b 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -33,7 +33,8 @@ namespace MWRender BillboardObject( const Ogre::String& textureName, const float size, const Ogre::Vector3& position, - Ogre::SceneNode* rootNode + Ogre::SceneNode* rootNode, + const std::string& material ); BillboardObject(); @@ -52,13 +53,9 @@ namespace MWRender Ogre::SceneNode* getNode(); protected: - virtual void init(const Ogre::String& textureName, - const float size, - const Ogre::Vector3& position, - Ogre::SceneNode* rootNode); Ogre::SceneNode* mNode; - Ogre::MaterialPtr mMaterial; + sh::MaterialInstance* mMaterial; Ogre::BillboardSet* mBBSet; }; @@ -72,7 +69,8 @@ namespace MWRender Moon( const Ogre::String& textureName, const float size, const Ogre::Vector3& position, - Ogre::SceneNode* rootNode + Ogre::SceneNode* rootNode, + const std::string& material ); virtual ~Moon() {} @@ -97,7 +95,6 @@ namespace MWRender void setPhase(const Phase& phase); void setType(const Type& type); - void setSkyColour(const Ogre::ColourValue& colour); Phase getPhase() const; unsigned int getPhaseInt() const; @@ -163,7 +160,8 @@ namespace MWRender void secundaEnable(); void secundaDisable(); - void setThunder(const float factor); + void setLightningStrength(const float factor); + void setLightningDirection(const Ogre::Vector3& dir); void setGlare(const float glare); Ogre::Vector3 getRealSunPos(); @@ -175,6 +173,8 @@ namespace MWRender private: bool mCreated; + bool mMoonRed; + float mHour; int mDay; int mMonth; @@ -205,8 +205,7 @@ namespace MWRender Ogre::ColourValue mCloudColour; Ogre::ColourValue mSkyColour; - Ogre::Overlay* mThunderOverlay; - Ogre::TextureUnitState* mThunderTextureUnit; + Ogre::Light* mLightning; float mRemainingTransitionTime; diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 7610acadc..67cee435c 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -193,7 +193,7 @@ namespace MWRender //this is a hack to get around the fact that Ogre seems to //corrupt the global colour map leading to rendering errors - MaterialPtr mat = terrain->getMaterial(); + //MaterialPtr mat = terrain->getMaterial(); /// \todo //mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 577b346df..a066a9bed 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -47,12 +47,30 @@ namespace MWRender Ogre::MaterialManager::getSingleton().remove(matName); - sh::MaterialInstance* m = sh::Factory::getInstance().createMaterialInstance (matName); + mMaterial = sh::Factory::getInstance().createMaterialInstance (matName); + mMaterial->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); + + createPass(); return Ogre::MaterialManager::getSingleton().getByName(matName); } + int TerrainMaterial::Profile::getLayersPerPass () const + { + return 10; + } + + void TerrainMaterial::Profile::createPass (int index) + { + int layerOffset = index * getLayersPerPass(); + + sh::MaterialInstancePass* p = mMaterial->createPass (); + + p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); + p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); + } + Ogre::MaterialPtr TerrainMaterial::Profile::generateForCompositeMap(const Ogre::Terrain* terrain) { throw std::runtime_error ("composite map not supported"); diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index 7cafbb97a..73bbd7e07 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -33,6 +33,11 @@ THE SOFTWARE. #include "OgreTerrainMaterialGenerator.h" #include "OgreGpuProgramParams.h" +namespace sh +{ + class MaterialInstance; +} + namespace MWRender { @@ -64,6 +69,13 @@ namespace MWRender virtual void requestOptions(Ogre::Terrain* terrain); + private: + sh::MaterialInstance* mMaterial; + + void createPass (int index=0); + + int getLayersPerPass () const; + }; TerrainMaterial(); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 6c275ff80..b5f2e3a57 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -476,8 +476,7 @@ WeatherResult WeatherManager::transition(float factor) result.mSunDiscColor = lerp(current.mSunDiscColor, other.mSunDiscColor); result.mFogDepth = lerp(current.mFogDepth, other.mFogDepth); result.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed); - //result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed); - result.mCloudSpeed = current.mCloudSpeed; + result.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed); result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); result.mGlareView = lerp(current.mGlareView, other.mGlareView); result.mNightFade = lerp(current.mNightFade, other.mNightFade); @@ -689,13 +688,13 @@ void WeatherManager::update(float duration) mThunderFlash -= duration; if (mThunderFlash > 0) - mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold ); + mRendering->getSkyManager()->setLightningStrength( mThunderFlash / WeatherGlobals::mThunderThreshold ); else { srand(time(NULL)); mThunderChanceNeeded = rand() % 100; mThunderChance = 0; - mRendering->getSkyManager()->setThunder( 0.f ); + mRendering->getSkyManager()->setLightningStrength( 0.f ); } } else @@ -706,14 +705,14 @@ void WeatherManager::update(float duration) { mThunderFlash = WeatherGlobals::mThunderThreshold; - mRendering->getSkyManager()->setThunder( mThunderFlash / WeatherGlobals::mThunderThreshold ); + mRendering->getSkyManager()->setLightningStrength( mThunderFlash / WeatherGlobals::mThunderThreshold ); mThunderSoundDelay = WeatherGlobals::mThunderSoundDelay; } } } else - mRendering->getSkyManager()->setThunder(0.f); + mRendering->getSkyManager()->setLightningStrength(0.f); mRendering->setAmbientColour(result.mAmbientColor); mRendering->sunEnable(); @@ -725,7 +724,7 @@ void WeatherManager::update(float duration) { mRendering->sunDisable(); mRendering->skyDisable(); - mRendering->getSkyManager()->setThunder(0.f); + mRendering->getSkyManager()->setLightningStrength(0.f); } // play sounds diff --git a/extern/shiny b/extern/shiny index ca0549ae6..1c25aca08 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit ca0549ae6db7aa9efb125335942c61fb3f583a8d +Subproject commit 1c25aca082214beddd05fa9b8adf481c7299cf0e diff --git a/files/materials/moon.shader b/files/materials/moon.shader index e69de29bb..4a4eaf0b4 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -0,0 +1,52 @@ +#include "core.h" + +#define MRT @shGlobalSettingBool(mrt_output) + + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shInput(float2, uv0) + shOutput(float2, UV) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + } + +#else + + SH_BEGIN_PROGRAM + shSampler2D(diffuseMap) + shInput(float2, UV) +#if MRT + shDeclareMrtOutput(1) +#endif + shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) + shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + + shUniform(float4, atmosphereColour) @shSharedParameter(atmosphereColour) + + SH_START_PROGRAM + { + + float4 tex = shSample(diffuseMap, UV); + + shOutputColour(0) = float4(materialEmissive.xyz, 1) * tex; + + // use a circle for the alpha (compute UV distance to center) + // looks a bit bad because it's not filtered on the edges, + // but cheaper than a seperate alpha texture. + float sqrUVdist = pow(UV.x-0.5,2) + pow(UV.y-0.5, 2); + shOutputColour(0).a = materialDiffuse.a * (sqrUVdist >= 0.24 ? 0 : 1); + shOutputColour(0).rgb += (1-tex.a) * shOutputColour(0).a * atmosphereColour.rgb; //fill dark side of moon with atmosphereColour + shOutputColour(0).rgb += (1-materialDiffuse.a) * atmosphereColour.rgb; //fade bump + +#if MRT + shOutputColour(1) = float4(1,1,1,1); +#endif + } + +#endif diff --git a/files/materials/moon.shaderset b/files/materials/moon.shaderset index e69de29bb..659481a96 100644 --- a/files/materials/moon.shaderset +++ b/files/materials/moon.shaderset @@ -0,0 +1,15 @@ +shader_set moon_vertex +{ + source moon.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set moon_fragment +{ + source moon.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/materials/sky.mat b/files/materials/sky.mat index 3f52966e8..17da7fc13 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -7,11 +7,13 @@ material openmw_moon fragment_program moon_fragment polygon_mode_overrideable off + depth_write off + depth_check off + scene_blend alpha_blend texture_unit diffuseMap { - texture $diffuseMap - create_in_ffp true + texture_alias $texture } } } @@ -33,7 +35,6 @@ material openmw_clouds texture_unit diffuseMap1 { texture_alias cloud_texture_1 - create_in_ffp true } texture_unit diffuseMap2 @@ -90,6 +91,10 @@ material openmw_sun polygon_mode_overrideable off + depth_check off + depth_write off + scene_blend alpha_blend + texture_unit diffuseMap { direct_texture $texture diff --git a/files/materials/sun.shader b/files/materials/sun.shader index 4c7926756..ceab60565 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -25,13 +25,11 @@ shDeclareMrtOutput(1) #endif shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) - shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) + //shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) SH_START_PROGRAM { - shOutputColour(0) = float4(1,1,1,materialDiffuse.a) * float4(materialEmissive.xyz, 1) * shSample(diffuseMap, UV); - - shOutputColour(0) = shSample(diffuseMap, UV); + shOutputColour(0) = float4(1,1,1,materialDiffuse.a) * shSample(diffuseMap, UV); #if MRT shOutputColour(1) = float4(1,1,1,1); diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 8cdcb339e..8b186def3 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -1,6 +1,6 @@ #include "core.h" -#define FOG @shPropertyBool(fog) +#define FOG @shGlobalSettingBool(fog) #define MRT @shGlobalSettingBool(mrt_output) @@ -47,6 +47,8 @@ { float depth = @shPassthroughReceive(depth); + + shOutputColour(0) = float4(1,0,0,1); #if MRT From 84d846cf07c75dc13f3fdf488a1d4dee87ee4865 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 13 Jul 2012 09:16:27 +0200 Subject: [PATCH 118/688] Issue #314: implemented apply and skillUsageSucceeded functions for MWClass::Npc --- apps/openmw/mwclass/npc.cpp | 22 ++++++++++++++++++++++ apps/openmw/mwclass/npc.hpp | 9 +++++++++ 2 files changed, 31 insertions(+) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d54b9441d..2570408f7 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -343,4 +343,26 @@ namespace MWClass return weight; } + + bool Npc::apply (const MWWorld::Ptr& ptr, const std::string& id, + const MWWorld::Ptr& actor) const + { + MWMechanics::CreatureStats& stats = getCreatureStats (ptr); + + /// \todo consider instant effects + + return stats.mActiveSpells.addSpell (id); + } + + void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const + { + MWMechanics::NpcStats& stats = getNpcStats (ptr); + + MWWorld::LiveCellRef *ref = ptr.get(); + + const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().classes.find ( + ref->base->cls); + + stats.useSkill (skill, *class_, usageType); + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 4cb733977..74a80c085 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -76,6 +76,15 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. + virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id, + const MWWorld::Ptr& actor) const; + ///< Apply \a id on \a ptr. + /// \param actor Actor that is resposible for the ID being applied to \a ptr. + /// \return Any effect? + + virtual void skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const; + ///< Inform actor \a ptr that a skill use has succeeded. + static void registerSelf(); }; } From 29b4a5e5f7e1611b7854d06a7c205a7615e06e20 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 13 Jul 2012 09:41:38 +0200 Subject: [PATCH 119/688] Issue #314: added apply actions --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwworld/actionapply.cpp | 28 +++++++++++++++++++ apps/openmw/mwworld/actionapply.hpp | 42 +++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 apps/openmw/mwworld/actionapply.cpp create mode 100644 apps/openmw/mwworld/actionapply.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 36e59f6bd..bb3f76f3c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -50,7 +50,7 @@ add_openmw_dir (mwworld refdata worldimp physicssystem scene globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellfunctors cells localscripts customdata weather inventorystore ptr actionopen actionread - actionequip timestamp actionalchemy cellstore + actionequip timestamp actionalchemy cellstore actionapply ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp new file mode 100644 index 000000000..c5228d798 --- /dev/null +++ b/apps/openmw/mwworld/actionapply.cpp @@ -0,0 +1,28 @@ + +#include "actionapply.hpp" + +#include "class.hpp" + +namespace MWWorld +{ + ActionApply::ActionApply (const Ptr& target, const std::string& id, const Ptr& actor) + : mTarget (target), mId (id), mActor (actor) + {} + + void ActionApply::execute() + { + MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor); + } + + + ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id, + const Ptr& actor, int skillIndex, int usageType) + : mTarget (target), mId (id), mActor (actor), mSkillIndex (skillIndex), mUsageType (usageType) + {} + + void ActionApplyWithSkill::execute() + { + if (MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor)) + MWWorld::Class::get (mTarget).skillUsageSucceeded (mActor, mSkillIndex, mUsageType); + } +} diff --git a/apps/openmw/mwworld/actionapply.hpp b/apps/openmw/mwworld/actionapply.hpp new file mode 100644 index 000000000..972417e02 --- /dev/null +++ b/apps/openmw/mwworld/actionapply.hpp @@ -0,0 +1,42 @@ + +#ifndef GAME_MWWORLD_ACTIONAPPLY_H +#define GAME_MWWORLD_ACTIONAPPLY_H + +#include + +#include "action.hpp" +#include "ptr.hpp" + +namespace MWWorld +{ + class ActionApply : public Action + { + Ptr mTarget; + std::string mId; + Ptr mActor; + + public: + + ActionApply (const Ptr& target, const std::string& id, const Ptr& actor); + + virtual void execute(); + }; + + class ActionApplyWithSkill : public Action + { + Ptr mTarget; + std::string mId; + Ptr mActor; + int mSkillIndex; + int mUsageType; + + public: + + ActionApplyWithSkill (const Ptr& target, const std::string& id, const Ptr& actor, + int skillIndex, int usageType); + + virtual void execute(); + }; +} + +#endif From d2fbb114757e884dbeb6b6a0dd6557ac64464c1e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jul 2012 11:26:36 +0200 Subject: [PATCH 120/688] temp commit --- apps/openmw/mwrender/terrainmaterial.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index a066a9bed..ae04306f6 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -42,11 +42,13 @@ namespace MWRender Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain) { const Ogre::String& matName = terrain->getMaterialName(); + + sh::Factory::getInstance().destroyMaterialInstance (matName); + Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(matName); if (!mat.isNull()) Ogre::MaterialManager::getSingleton().remove(matName); - mMaterial = sh::Factory::getInstance().createMaterialInstance (matName); mMaterial->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); @@ -54,6 +56,11 @@ namespace MWRender createPass(); return Ogre::MaterialManager::getSingleton().getByName(matName); + + /* + Ogre::MaterialPtr m = Ogre::MaterialManager::getSingleton().create(matName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + return m; + */ } int TerrainMaterial::Profile::getLayersPerPass () const From fb109ec7e2bc57c89afdd46f3ae95e070c97c66c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jul 2012 11:30:47 +0200 Subject: [PATCH 121/688] use debug versions of ogre plugins in debug mode --- CMakeLists.txt | 6 ++++++ files/plugins.cfg.linux | 9 ++++----- files/plugins.cfg.mac | 8 ++++---- files/plugins.cfg.win32 | 10 +++++----- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84cef306e..b561815ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,12 @@ set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") +# Debug suffix for plugins +set(DEBUG_SUFFIX "") +if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(DEBUG_SUFFIX "_d") +endif() + # doxygen main page configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") diff --git a/files/plugins.cfg.linux b/files/plugins.cfg.linux index f34621a0f..bbce1f1b4 100644 --- a/files/plugins.cfg.linux +++ b/files/plugins.cfg.linux @@ -4,9 +4,8 @@ PluginFolder=${OGRE_PLUGIN_DIR_REL} # Define plugins -Plugin=RenderSystem_GL -Plugin=Plugin_ParticleFX -Plugin=Plugin_OctreeSceneManager -Plugin=Plugin_CgProgramManager - +Plugin=RenderSystem_GL${OGRE_RenderSystem_GL_LIBRARIES} +Plugin=Plugin_ParticleFX${DEBUG_SUFFIX} +Plugin=Plugin_OctreeSceneManager${DEBUG_SUFFIX} +Plugin=Plugin_CgProgramManager${DEBUG_SUFFIX} diff --git a/files/plugins.cfg.mac b/files/plugins.cfg.mac index 322070832..fac18dc8f 100644 --- a/files/plugins.cfg.mac +++ b/files/plugins.cfg.mac @@ -4,9 +4,9 @@ PluginFolder=${OGRE_PLUGIN_DIR} # Define plugins -Plugin=RenderSystem_GL.1.8.0 -Plugin=Plugin_ParticleFX.1.8.0 -Plugin=Plugin_OctreeSceneManager.1.8.0 -Plugin=Plugin_CgProgramManager.1.8.0 +Plugin=RenderSystem_GL${DEBUG_SUFFIX}.1.8.0 +Plugin=Plugin_ParticleFX${DEBUG_SUFFIX}.1.8.0 +Plugin=Plugin_OctreeSceneManager${DEBUG_SUFFIX}.1.8.0 +Plugin=Plugin_CgProgramManager${DEBUG_SUFFIX}.1.8.0 diff --git a/files/plugins.cfg.win32 b/files/plugins.cfg.win32 index ea12c0394..6b4e9ef9d 100644 --- a/files/plugins.cfg.win32 +++ b/files/plugins.cfg.win32 @@ -4,10 +4,10 @@ PluginFolder=.\ # Define plugins -Plugin=RenderSystem_Direct3D9 -Plugin=RenderSystem_GL -Plugin=Plugin_ParticleFX -Plugin=Plugin_OctreeSceneManager -Plugin=Plugin_CgProgramManager +Plugin=RenderSystem_Direct3D9${DEBUG_SUFFIX} +Plugin=RenderSystem_GL${DEBUG_SUFFIX} +Plugin=Plugin_ParticleFX${DEBUG_SUFFIX} +Plugin=Plugin_OctreeSceneManager${DEBUG_SUFFIX} +Plugin=Plugin_CgProgramManager${DEBUG_SUFFIX} From 1429c8d5cb088780d6cd969f2eb807f7f41b49e1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 13 Jul 2012 12:43:48 +0200 Subject: [PATCH 122/688] copy&paste mistake --- files/plugins.cfg.linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/plugins.cfg.linux b/files/plugins.cfg.linux index bbce1f1b4..7b8d99e8f 100644 --- a/files/plugins.cfg.linux +++ b/files/plugins.cfg.linux @@ -4,7 +4,7 @@ PluginFolder=${OGRE_PLUGIN_DIR_REL} # Define plugins -Plugin=RenderSystem_GL${OGRE_RenderSystem_GL_LIBRARIES} +Plugin=RenderSystem_GL${DEBUG_SUFFIX} Plugin=Plugin_ParticleFX${DEBUG_SUFFIX} Plugin=Plugin_OctreeSceneManager${DEBUG_SUFFIX} Plugin=Plugin_CgProgramManager${DEBUG_SUFFIX} From e4d046f69c79145d7ce46173184065a5877f12d2 Mon Sep 17 00:00:00 2001 From: Carl Maxwell Date: Fri, 13 Jul 2012 03:51:58 -0700 Subject: [PATCH 123/688] Prepending m to the name of every member variable. I made a bunch of changes in apps/openmw/mwrender/animation.cpp because the scope brackets didn't line up in a bunch of places npcanimations.cpp & creatureanimations.cpp were the same kind of thing --- apps/openmw/mwclass/misc.hpp | 2 +- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwclass/potion.hpp | 2 +- apps/openmw/mwclass/static.hpp | 2 +- apps/openmw/mwdialogue/dialoguemanager.cpp | 18 +- apps/openmw/mwdialogue/dialoguemanager.hpp | 4 +- apps/openmw/mwgui/birth.cpp | 60 +- apps/openmw/mwgui/birth.hpp | 12 +- apps/openmw/mwgui/class.cpp | 466 ++++---- apps/openmw/mwgui/class.hpp | 94 +- apps/openmw/mwgui/dialogue.cpp | 80 +- apps/openmw/mwgui/dialogue.hpp | 8 +- apps/openmw/mwgui/hud.cpp | 166 +-- apps/openmw/mwgui/hud.hpp | 20 +- apps/openmw/mwgui/journalwindow.cpp | 24 +- apps/openmw/mwgui/journalwindow.hpp | 12 +- apps/openmw/mwgui/messagebox.cpp | 4 +- apps/openmw/mwgui/messagebox.hpp | 2 +- apps/openmw/mwgui/race.cpp | 100 +- apps/openmw/mwgui/race.hpp | 26 +- apps/openmw/mwgui/review.cpp | 188 +-- apps/openmw/mwgui/review.hpp | 26 +- apps/openmw/mwgui/stats_window.cpp | 200 ++-- apps/openmw/mwgui/stats_window.hpp | 26 +- apps/openmw/mwgui/text_input.cpp | 8 +- apps/openmw/mwgui/text_input.hpp | 6 +- apps/openmw/mwgui/widgets.cpp | 166 +-- apps/openmw/mwgui/widgets.hpp | 66 +- apps/openmw/mwgui/window_manager.cpp | 206 ++-- apps/openmw/mwgui/window_manager.hpp | 52 +- apps/openmw/mwrender/animation.cpp | 797 +++++++------ apps/openmw/mwrender/animation.hpp | 43 +- apps/openmw/mwrender/creatureanimation.cpp | 81 +- apps/openmw/mwrender/creatureanimation.hpp | 3 +- apps/openmw/mwrender/npcanimation.cpp | 1211 +++++++++++--------- apps/openmw/mwrender/npcanimation.hpp | 101 +- 36 files changed, 2172 insertions(+), 2112 deletions(-) diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 46b5b9662..da5f0df96 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -9,7 +9,7 @@ namespace MWClass { public: - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 4cb733977..dcb9eaee0 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -48,7 +48,7 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr - virtual void setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const; + virtual void setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const; ///< Force or unforce a stance. virtual void setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const; diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 74779864a..97a4ab80a 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -9,7 +9,7 @@ namespace MWClass { public: - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index cd1626c19..c223df1ac 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -9,7 +9,7 @@ namespace MWClass { public: - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 41ffd1e93..f6a17baa5 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -591,7 +591,7 @@ namespace MWDialogue mIsInChoice = false; mCompilerContext.setExtensions (&extensions); mDialogueMap.clear(); - actorKnownTopics.clear(); + mActorKnownTopics.clear(); ESMS::RecListCaseT::MapType dialogueList = MWBase::Environment::get().getWorld()->getStore().dialogs.list; for(ESMS::RecListCaseT::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++) { @@ -601,24 +601,24 @@ namespace MWDialogue void DialogueManager::addTopic(std::string topic) { - knownTopics[toLower(topic)] = true; + mKnownTopics[toLower(topic)] = true; } void DialogueManager::parseText(std::string text) { std::list::iterator it; - for(it = actorKnownTopics.begin();it != actorKnownTopics.end();++it) + for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it) { size_t pos = find_str_ci(text,*it,0); if(pos !=std::string::npos) { if(pos==0) { - knownTopics[*it] = true; + mKnownTopics[*it] = true; } else if(text.substr(pos -1,1) == " ") { - knownTopics[*it] = true; + mKnownTopics[*it] = true; } } } @@ -632,7 +632,7 @@ namespace MWDialogue mActor = actor; - actorKnownTopics.clear(); + mActorKnownTopics.clear(); //initialise the GUI MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); @@ -742,7 +742,7 @@ namespace MWDialogue std::list keywordList; int choice = mChoice; mChoice = -1; - actorKnownTopics.clear(); + mActorKnownTopics.clear(); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); ESMS::RecListCaseT::MapType dialogueList = MWBase::Environment::get().getWorld()->getStore().dialogs.list; for(ESMS::RecListCaseT::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++) @@ -755,9 +755,9 @@ namespace MWDialogue { if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true)) { - actorKnownTopics.push_back(toLower(it->first)); + mActorKnownTopics.push_back(toLower(it->first)); //does the player know the topic? - if(knownTopics.find(toLower(it->first)) != knownTopics.end()) + if(mKnownTopics.find(toLower(it->first)) != mKnownTopics.end()) { keywordList.push_back(it->first); break; diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanager.hpp index 992175c0c..d139ddc01 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.hpp +++ b/apps/openmw/mwdialogue/dialoguemanager.hpp @@ -26,8 +26,8 @@ namespace MWDialogue void updateTopics(); std::map mDialogueMap; - std::map knownTopics;// Those are the topics the player knows. - std::list actorKnownTopics; + std::map mKnownTopics;// Those are the topics the player knows. + std::list mActorKnownTopics; MWScript::CompilerContext mCompilerContext; std::ostream mErrorStream; diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 69056759d..0f1a04c27 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -15,15 +15,15 @@ BirthDialog::BirthDialog(WindowManager& parWindowManager) // Centre dialog center(); - getWidget(spellArea, "SpellArea"); + getWidget(mSpellArea, "SpellArea"); - getWidget(birthImage, "BirthsignImage"); + getWidget(mBirthImage, "BirthsignImage"); - getWidget(birthList, "BirthsignList"); - birthList->setScrollVisible(true); - birthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - birthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); - birthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + getWidget(mBirthList, "BirthsignList"); + mBirthList->setScrollVisible(true); + mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); + mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); @@ -68,14 +68,14 @@ void BirthDialog::open() void BirthDialog::setBirthId(const std::string &birthId) { - currentBirthId = birthId; - birthList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = birthList->getItemCount(); + mCurrentBirthId = birthId; + mBirthList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mBirthList->getItemCount(); for (size_t i = 0; i < count; ++i) { - if (boost::iequals(*birthList->getItemDataAt(i), birthId)) + if (boost::iequals(*mBirthList->getItemDataAt(i), birthId)) { - birthList->setIndexSelected(i); + mBirthList->setIndexSelected(i); break; } } @@ -100,11 +100,11 @@ void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) if (_index == MyGUI::ITEM_NONE) return; - const std::string *birthId = birthList->getItemDataAt(_index); - if (boost::iequals(currentBirthId, *birthId)) + const std::string *birthId = mBirthList->getItemDataAt(_index); + if (boost::iequals(mCurrentBirthId, *birthId)) return; - currentBirthId = *birthId; + mCurrentBirthId = *birthId; updateSpells(); } @@ -112,7 +112,7 @@ void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index) void BirthDialog::updateBirths() { - birthList->removeAllItems(); + mBirthList->removeAllItems(); const ESMS::ESMStore &store = mWindowManager.getStore(); @@ -122,34 +122,34 @@ void BirthDialog::updateBirths() for (; it != end; ++it) { const ESM::BirthSign &birth = it->second; - birthList->addItem(birth.name, it->first); - if (boost::iequals(it->first, currentBirthId)) - birthList->setIndexSelected(index); + mBirthList->addItem(birth.name, it->first); + if (boost::iequals(it->first, mCurrentBirthId)) + mBirthList->setIndexSelected(index); ++index; } } void BirthDialog::updateSpells() { - for (std::vector::iterator it = spellItems.begin(); it != spellItems.end(); ++it) + for (std::vector::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } - spellItems.clear(); + mSpellItems.clear(); - if (currentBirthId.empty()) + if (mCurrentBirthId.empty()) return; MWSpellPtr spellWidget; const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, spellArea->getWidth(), 18); + MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); const ESMS::ESMStore &store = mWindowManager.getStore(); - const ESM::BirthSign *birth = store.birthSigns.find(currentBirthId); + const ESM::BirthSign *birth = store.birthSigns.find(mCurrentBirthId); std::string texturePath = std::string("textures\\") + birth->texture; fixTexturePath(texturePath); - birthImage->setImageTexture(texturePath); + mBirthImage->setImageTexture(texturePath); std::vector abilities, powers, spells; @@ -183,25 +183,25 @@ void BirthDialog::updateSpells() { if (!categories[category].spells.empty()) { - MyGUI::TextBox* label = spellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); + MyGUI::TextBox* label = mSpellArea->createWidget("SandBrightText", coord, MyGUI::Align::Default, std::string("Label")); label->setCaption(mWindowManager.getGameSettingString(categories[category].label, "")); - spellItems.push_back(label); + mSpellItems.push_back(label); coord.top += lineHeight; std::vector::const_iterator end = categories[category].spells.end(); for (std::vector::const_iterator it = categories[category].spells.begin(); it != end; ++it) { const std::string &spellId = *it; - spellWidget = spellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); + spellWidget = mSpellArea->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast(i)); spellWidget->setWindowManager(&mWindowManager); spellWidget->setSpellId(spellId); - spellItems.push_back(spellWidget); + mSpellItems.push_back(spellWidget); coord.top += lineHeight; MyGUI::IntCoord spellCoord = coord; spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template? - spellWidget->createEffectWidgets(spellItems, spellArea, spellCoord, (category == 0) ? MWEffectList::EF_Constant : 0); + spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? MWEffectList::EF_Constant : 0); coord.top = spellCoord.top; ++i; diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index e61be736a..770e4ba36 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -25,7 +25,7 @@ namespace MWGui GM_Female }; - const std::string &getBirthId() const { return currentBirthId; } + const std::string &getBirthId() const { return mCurrentBirthId; } void setBirthId(const std::string &raceId); void setNextButtonShow(bool shown); @@ -49,12 +49,12 @@ namespace MWGui void updateBirths(); void updateSpells(); - MyGUI::ListBox* birthList; - MyGUI::WidgetPtr spellArea; - MyGUI::ImageBox* birthImage; - std::vector spellItems; + MyGUI::ListBox* mBirthList; + MyGUI::WidgetPtr mSpellArea; + MyGUI::ImageBox* mBirthImage; + std::vector mSpellItems; - std::string currentBirthId; + std::string mCurrentBirthId; }; } #endif diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index d0f21f945..8ec1331ad 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -26,8 +26,8 @@ GenerateClassResultDialog::GenerateClassResultDialog(WindowManager& parWindowMan setText("ReflectT", mWindowManager.getGameSettingString("sMessageQuestionAnswer1", "")); - getWidget(classImage, "ClassImage"); - getWidget(className, "ClassName"); + getWidget(mClassImage, "ClassImage"); + getWidget(mClassName, "ClassName"); MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); @@ -51,15 +51,15 @@ void GenerateClassResultDialog::open() std::string GenerateClassResultDialog::getClassId() const { - return className->getCaption(); + return mClassName->getCaption(); } void GenerateClassResultDialog::setClassId(const std::string &classId) { - currentClassId = classId; - classImage->setImageTexture(std::string("textures\\levelup\\") + currentClassId + ".dds"); + mCurrentClassId = classId; + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); const ESMS::ESMStore &store = mWindowManager.getStore(); - className->setCaption(store.classes.find(currentClassId)->name); + mClassName->setCaption(store.classes.find(mCurrentClassId)->name); } // widget controls @@ -82,29 +82,29 @@ PickClassDialog::PickClassDialog(WindowManager& parWindowManager) // Centre dialog center(); - getWidget(specializationName, "SpecializationName"); + getWidget(mSpecializationName, "SpecializationName"); - getWidget(favoriteAttribute[0], "FavoriteAttribute0"); - getWidget(favoriteAttribute[1], "FavoriteAttribute1"); - favoriteAttribute[0]->setWindowManager(&mWindowManager); - favoriteAttribute[1]->setWindowManager(&mWindowManager); + getWidget(mFavoriteAttribute[0], "FavoriteAttribute0"); + getWidget(mFavoriteAttribute[1], "FavoriteAttribute1"); + mFavoriteAttribute[0]->setWindowManager(&mWindowManager); + mFavoriteAttribute[1]->setWindowManager(&mWindowManager); for(int i = 0; i < 5; i++) { char theIndex = '0'+i; - getWidget(majorSkill[i], std::string("MajorSkill").append(1, theIndex)); - getWidget(minorSkill[i], std::string("MinorSkill").append(1, theIndex)); - majorSkill[i]->setWindowManager(&mWindowManager); - minorSkill[i]->setWindowManager(&mWindowManager); + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); + mMajorSkill[i]->setWindowManager(&mWindowManager); + mMinorSkill[i]->setWindowManager(&mWindowManager); } - getWidget(classList, "ClassList"); - classList->setScrollVisible(true); - classList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - classList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - classList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + getWidget(mClassList, "ClassList"); + mClassList->setScrollVisible(true); + mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); + mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); - getWidget(classImage, "ClassImage"); + getWidget(mClassImage, "ClassImage"); MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); @@ -148,14 +148,14 @@ void PickClassDialog::open() void PickClassDialog::setClassId(const std::string &classId) { - currentClassId = classId; - classList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = classList->getItemCount(); + mCurrentClassId = classId; + mClassList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mClassList->getItemCount(); for (size_t i = 0; i < count; ++i) { - if (boost::iequals(*classList->getItemDataAt(i), classId)) + if (boost::iequals(*mClassList->getItemDataAt(i), classId)) { - classList->setIndexSelected(i); + mClassList->setIndexSelected(i); break; } } @@ -180,11 +180,11 @@ void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) if (_index == MyGUI::ITEM_NONE) return; - const std::string *classId = classList->getItemDataAt(_index); - if (boost::iequals(currentClassId, *classId)) + const std::string *classId = mClassList->getItemDataAt(_index); + if (boost::iequals(mCurrentClassId, *classId)) return; - currentClassId = *classId; + mCurrentClassId = *classId; updateStats(); } @@ -192,7 +192,7 @@ void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) void PickClassDialog::updateClasses() { - classList->removeAllItems(); + mClassList->removeAllItems(); const ESMS::ESMStore &store = mWindowManager.getStore(); @@ -207,19 +207,19 @@ void PickClassDialog::updateClasses() continue; const std::string &id = it->first; - classList->addItem(klass.name, id); - if (boost::iequals(id, currentClassId)) - classList->setIndexSelected(index); + mClassList->addItem(klass.name, id); + if (boost::iequals(id, mCurrentClassId)) + mClassList->setIndexSelected(index); ++index; } } void PickClassDialog::updateStats() { - if (currentClassId.empty()) + if (mCurrentClassId.empty()) return; const ESMS::ESMStore &store = mWindowManager.getStore(); - const ESM::Class *klass = store.classes.search(currentClassId); + const ESM::Class *klass = store.classes.search(mCurrentClassId); if (!klass) return; @@ -231,23 +231,23 @@ void PickClassDialog::updateStats() "sSpecializationStealth" }; std::string specName = mWindowManager.getGameSettingString(specIds[specialization], specIds[specialization]); - specializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(specializationName, specName, specialization); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); - favoriteAttribute[0]->setAttributeId(klass->data.attribute[0]); - favoriteAttribute[1]->setAttributeId(klass->data.attribute[1]); - ToolTips::createAttributeToolTip(favoriteAttribute[0], favoriteAttribute[0]->getAttributeId()); - ToolTips::createAttributeToolTip(favoriteAttribute[1], favoriteAttribute[1]->getAttributeId()); + mFavoriteAttribute[0]->setAttributeId(klass->data.attribute[0]); + mFavoriteAttribute[1]->setAttributeId(klass->data.attribute[1]); + ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); for (int i = 0; i < 5; ++i) { - minorSkill[i]->setSkillNumber(klass->data.skills[i][0]); - majorSkill[i]->setSkillNumber(klass->data.skills[i][1]); - ToolTips::createSkillToolTip(minorSkill[i], klass->data.skills[i][0]); - ToolTips::createSkillToolTip(majorSkill[i], klass->data.skills[i][1]); + mMinorSkill[i]->setSkillNumber(klass->data.skills[i][0]); + mMajorSkill[i]->setSkillNumber(klass->data.skills[i][1]); + ToolTips::createSkillToolTip(mMinorSkill[i], klass->data.skills[i][0]); + ToolTips::createSkillToolTip(mMajorSkill[i], klass->data.skills[i][1]); } - classImage->setImageTexture(std::string("textures\\levelup\\") + currentClassId + ".dds"); + mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); } /* InfoBoxDialog */ @@ -284,59 +284,59 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin) InfoBoxDialog::InfoBoxDialog(WindowManager& parWindowManager) : WindowBase("openmw_infobox.layout", parWindowManager) - , currentButton(-1) + , mCurrentButton(-1) { - getWidget(textBox, "TextBox"); - getWidget(text, "Text"); - text->getSubWidgetText()->setWordWrap(true); - getWidget(buttonBar, "ButtonBar"); + getWidget(mTextBox, "TextBox"); + getWidget(mText, "Text"); + mText->getSubWidgetText()->setWordWrap(true); + getWidget(mButtonBar, "ButtonBar"); center(); } void InfoBoxDialog::setText(const std::string &str) { - text->setCaption(str); - textBox->setVisible(!str.empty()); - fitToText(text); + mText->setCaption(str); + mTextBox->setVisible(!str.empty()); + fitToText(mText); } std::string InfoBoxDialog::getText() const { - return text->getCaption(); + return mText->getCaption(); } void InfoBoxDialog::setButtons(ButtonList &buttons) { - for (std::vector::iterator it = this->buttons.begin(); it != this->buttons.end(); ++it) + for (std::vector::iterator it = this->mButtons.begin(); it != this->mButtons.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } - this->buttons.clear(); - currentButton = -1; + this->mButtons.clear(); + mCurrentButton = -1; // TODO: The buttons should be generated from a template in the layout file, ie. cloning an existing widget MyGUI::ButtonPtr button; - MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, buttonBar->getWidth(), 10); + MyGUI::IntCoord coord = MyGUI::IntCoord(0, 0, mButtonBar->getWidth(), 10); ButtonList::const_iterator end = buttons.end(); for (ButtonList::const_iterator it = buttons.begin(); it != end; ++it) { const std::string &text = *it; - button = buttonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); + button = mButtonBar->createWidget("MW_Button", coord, MyGUI::Align::Top | MyGUI::Align::HCenter, ""); button->getSubWidgetText()->setWordWrap(true); button->setCaption(text); fitToText(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked); coord.top += button->getHeight(); - this->buttons.push_back(button); + this->mButtons.push_back(button); } } void InfoBoxDialog::open() { // Fix layout - layoutVertically(textBox, 4); - layoutVertically(buttonBar, 6); + layoutVertically(mTextBox, 4); + layoutVertically(mButtonBar, 6); layoutVertically(mMainWidget, 4 + 6); center(); @@ -345,18 +345,18 @@ void InfoBoxDialog::open() int InfoBoxDialog::getChosenButton() const { - return currentButton; + return mCurrentButton; } void InfoBoxDialog::onButtonClicked(MyGUI::WidgetPtr _sender) { - std::vector::const_iterator end = buttons.end(); + std::vector::const_iterator end = mButtons.end(); int i = 0; - for (std::vector::const_iterator it = buttons.begin(); it != end; ++it) + for (std::vector::const_iterator it = mButtons.begin(); it != end; ++it) { if (*it == _sender) { - currentButton = i; + mCurrentButton = i; eventButtonSelected(i); return; } @@ -382,49 +382,49 @@ ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) : WindowBase("openmw_chargen_create_class.layout", parWindowManager) - , specDialog(nullptr) - , attribDialog(nullptr) - , skillDialog(nullptr) - , descDialog(nullptr) + , mSpecDialog(nullptr) + , mAttribDialog(nullptr) + , mSkillDialog(nullptr) + , mDescDialog(nullptr) { // Centre dialog center(); setText("SpecializationT", mWindowManager.getGameSettingString("sChooseClassMenu1", "Specialization")); - getWidget(specializationName, "SpecializationName"); - specializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); + getWidget(mSpecializationName, "SpecializationName"); + mSpecializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked); setText("FavoriteAttributesT", mWindowManager.getGameSettingString("sChooseClassMenu2", "Favorite Attributes:")); - getWidget(favoriteAttribute0, "FavoriteAttribute0"); - getWidget(favoriteAttribute1, "FavoriteAttribute1"); - favoriteAttribute0->setWindowManager(&mWindowManager); - favoriteAttribute1->setWindowManager(&mWindowManager); - favoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); - favoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + getWidget(mFavoriteAttribute0, "FavoriteAttribute0"); + getWidget(mFavoriteAttribute1, "FavoriteAttribute1"); + mFavoriteAttribute0->setWindowManager(&mWindowManager); + mFavoriteAttribute1->setWindowManager(&mWindowManager); + mFavoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); + mFavoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked); setText("MajorSkillT", mWindowManager.getGameSettingString("sSkillClassMajor", "")); setText("MinorSkillT", mWindowManager.getGameSettingString("sSkillClassMinor", "")); for(int i = 0; i < 5; i++) { char theIndex = '0'+i; - getWidget(majorSkill[i], std::string("MajorSkill").append(1, theIndex)); - getWidget(minorSkill[i], std::string("MinorSkill").append(1, theIndex)); - skills.push_back(majorSkill[i]); - skills.push_back(minorSkill[i]); + getWidget(mMajorSkill[i], std::string("MajorSkill").append(1, theIndex)); + getWidget(mMinorSkill[i], std::string("MinorSkill").append(1, theIndex)); + mSkills.push_back(mMajorSkill[i]); + mSkills.push_back(mMinorSkill[i]); } - std::vector::const_iterator end = skills.end(); - for (std::vector::const_iterator it = skills.begin(); it != end; ++it) + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) { (*it)->setWindowManager(&mWindowManager); (*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked); } setText("LabelT", mWindowManager.getGameSettingString("sName", "")); - getWidget(editName, "EditName"); + getWidget(mEditName, "EditName"); // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(editName); + MyGUI::InputManager::getInstance().setKeyFocusWidget(mEditName); MyGUI::ButtonPtr descriptionButton; getWidget(descriptionButton, "DescriptionButton"); @@ -441,20 +441,20 @@ CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) // Set default skills, attributes - favoriteAttribute0->setAttributeId(ESM::Attribute::Strength); - favoriteAttribute1->setAttributeId(ESM::Attribute::Agility); + mFavoriteAttribute0->setAttributeId(ESM::Attribute::Strength); + mFavoriteAttribute1->setAttributeId(ESM::Attribute::Agility); - majorSkill[0]->setSkillId(ESM::Skill::Block); - majorSkill[1]->setSkillId(ESM::Skill::Armorer); - majorSkill[2]->setSkillId(ESM::Skill::MediumArmor); - majorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); - majorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); + mMajorSkill[0]->setSkillId(ESM::Skill::Block); + mMajorSkill[1]->setSkillId(ESM::Skill::Armorer); + mMajorSkill[2]->setSkillId(ESM::Skill::MediumArmor); + mMajorSkill[3]->setSkillId(ESM::Skill::HeavyArmor); + mMajorSkill[4]->setSkillId(ESM::Skill::BluntWeapon); - minorSkill[0]->setSkillId(ESM::Skill::LongBlade); - minorSkill[1]->setSkillId(ESM::Skill::Axe); - minorSkill[2]->setSkillId(ESM::Skill::Spear); - minorSkill[3]->setSkillId(ESM::Skill::Athletics); - minorSkill[4]->setSkillId(ESM::Skill::Enchant); + mMinorSkill[0]->setSkillId(ESM::Skill::LongBlade); + mMinorSkill[1]->setSkillId(ESM::Skill::Axe); + mMinorSkill[2]->setSkillId(ESM::Skill::Spear); + mMinorSkill[3]->setSkillId(ESM::Skill::Athletics); + mMinorSkill[4]->setSkillId(ESM::Skill::Enchant); setSpecialization(0); update(); @@ -462,44 +462,44 @@ CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) CreateClassDialog::~CreateClassDialog() { - delete specDialog; - delete attribDialog; - delete skillDialog; - delete descDialog; + delete mSpecDialog; + delete mAttribDialog; + delete mSkillDialog; + delete mDescDialog; } void CreateClassDialog::update() { for (int i = 0; i < 5; ++i) { - ToolTips::createSkillToolTip(majorSkill[i], majorSkill[i]->getSkillId()); - ToolTips::createSkillToolTip(minorSkill[i], minorSkill[i]->getSkillId()); + ToolTips::createSkillToolTip(mMajorSkill[i], mMajorSkill[i]->getSkillId()); + ToolTips::createSkillToolTip(mMinorSkill[i], mMinorSkill[i]->getSkillId()); } - ToolTips::createAttributeToolTip(favoriteAttribute0, favoriteAttribute0->getAttributeId()); - ToolTips::createAttributeToolTip(favoriteAttribute1, favoriteAttribute1->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute0, mFavoriteAttribute0->getAttributeId()); + ToolTips::createAttributeToolTip(mFavoriteAttribute1, mFavoriteAttribute1->getAttributeId()); } std::string CreateClassDialog::getName() const { - return editName->getOnlyText(); + return mEditName->getOnlyText(); } std::string CreateClassDialog::getDescription() const { - return description; + return mDescription; } ESM::Class::Specialization CreateClassDialog::getSpecializationId() const { - return specializationId; + return mSpecializationId; } std::vector CreateClassDialog::getFavoriteAttributes() const { std::vector v; - v.push_back(favoriteAttribute0->getAttributeId()); - v.push_back(favoriteAttribute1->getAttributeId()); + v.push_back(mFavoriteAttribute0->getAttributeId()); + v.push_back(mFavoriteAttribute1->getAttributeId()); return v; } @@ -508,7 +508,7 @@ std::vector CreateClassDialog::getMajorSkills() const std::vector v; for(int i = 0; i < 5; i++) { - v.push_back(majorSkill[i]->getSkillId()); + v.push_back(mMajorSkill[i]->getSkillId()); } return v; } @@ -518,7 +518,7 @@ std::vector CreateClassDialog::getMinorSkills() const std::vector v; for(int i=0; i < 5; i++) { - v.push_back(minorSkill[i]->getSkillId()); + v.push_back(mMinorSkill[i]->getSkillId()); } return v; } @@ -557,108 +557,108 @@ void CreateClassDialog::open() void CreateClassDialog::onDialogCancel() { - if (specDialog) + if (mSpecDialog) { - mWindowManager.removeDialog(specDialog); - specDialog = 0; + mWindowManager.removeDialog(mSpecDialog); + mSpecDialog = 0; } - if (attribDialog) + if (mAttribDialog) { - mWindowManager.removeDialog(attribDialog); - attribDialog = 0; + mWindowManager.removeDialog(mAttribDialog); + mAttribDialog = 0; } - if (skillDialog) + if (mSkillDialog) { - mWindowManager.removeDialog(skillDialog); - skillDialog = 0; + mWindowManager.removeDialog(mSkillDialog); + mSkillDialog = 0; } - if (descDialog) + if (mDescDialog) { - mWindowManager.removeDialog(descDialog); - descDialog = 0; + mWindowManager.removeDialog(mDescDialog); + mDescDialog = 0; } } void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) { - delete specDialog; - specDialog = new SelectSpecializationDialog(mWindowManager); - specDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - specDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); - specDialog->setVisible(true); + delete mSpecDialog; + mSpecDialog = new SelectSpecializationDialog(mWindowManager); + mSpecDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSpecDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected); + mSpecDialog->setVisible(true); } void CreateClassDialog::onSpecializationSelected() { - specializationId = specDialog->getSpecializationId(); - setSpecialization(specializationId); + mSpecializationId = mSpecDialog->getSpecializationId(); + setSpecialization(mSpecializationId); - mWindowManager.removeDialog(specDialog); - specDialog = 0; + mWindowManager.removeDialog(mSpecDialog); + mSpecDialog = 0; } void CreateClassDialog::setSpecialization(int id) { - specializationId = (ESM::Class::Specialization) id; + mSpecializationId = (ESM::Class::Specialization) id; static const char *specIds[3] = { "sSpecializationCombat", "sSpecializationMagic", "sSpecializationStealth" }; - std::string specName = mWindowManager.getGameSettingString(specIds[specializationId], specIds[specializationId]); - specializationName->setCaption(specName); - ToolTips::createSpecializationToolTip(specializationName, specName, specializationId); + std::string specName = mWindowManager.getGameSettingString(specIds[mSpecializationId], specIds[mSpecializationId]); + mSpecializationName->setCaption(specName); + ToolTips::createSpecializationToolTip(mSpecializationName, specName, mSpecializationId); } void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { - delete attribDialog; - attribDialog = new SelectAttributeDialog(mWindowManager); - attribDialog->setAffectedWidget(_sender); - attribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - attribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); - attribDialog->setVisible(true); + delete mAttribDialog; + mAttribDialog = new SelectAttributeDialog(mWindowManager); + mAttribDialog->setAffectedWidget(_sender); + mAttribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mAttribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected); + mAttribDialog->setVisible(true); } void CreateClassDialog::onAttributeSelected() { - ESM::Attribute::AttributeID id = attribDialog->getAttributeId(); - Widgets::MWAttributePtr attribute = attribDialog->getAffectedWidget(); - if (attribute == favoriteAttribute0) + ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); + Widgets::MWAttributePtr attribute = mAttribDialog->getAffectedWidget(); + if (attribute == mFavoriteAttribute0) { - if (favoriteAttribute1->getAttributeId() == id) - favoriteAttribute1->setAttributeId(favoriteAttribute0->getAttributeId()); + if (mFavoriteAttribute1->getAttributeId() == id) + mFavoriteAttribute1->setAttributeId(mFavoriteAttribute0->getAttributeId()); } - else if (attribute == favoriteAttribute1) + else if (attribute == mFavoriteAttribute1) { - if (favoriteAttribute0->getAttributeId() == id) - favoriteAttribute0->setAttributeId(favoriteAttribute1->getAttributeId()); + if (mFavoriteAttribute0->getAttributeId() == id) + mFavoriteAttribute0->setAttributeId(mFavoriteAttribute1->getAttributeId()); } attribute->setAttributeId(id); - mWindowManager.removeDialog(attribDialog); - attribDialog = 0; + mWindowManager.removeDialog(mAttribDialog); + mAttribDialog = 0; update(); } void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender) { - delete skillDialog; - skillDialog = new SelectSkillDialog(mWindowManager); - skillDialog->setAffectedWidget(_sender); - skillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); - skillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); - skillDialog->setVisible(true); + delete mSkillDialog; + mSkillDialog = new SelectSkillDialog(mWindowManager); + mSkillDialog->setAffectedWidget(_sender); + mSkillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel); + mSkillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected); + mSkillDialog->setVisible(true); } void CreateClassDialog::onSkillSelected() { - ESM::Skill::SkillEnum id = skillDialog->getSkillId(); - Widgets::MWSkillPtr skill = skillDialog->getAffectedWidget(); + ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); + Widgets::MWSkillPtr skill = mSkillDialog->getAffectedWidget(); // Avoid duplicate skills by swapping any skill field that matches the selected one - std::vector::const_iterator end = skills.end(); - for (std::vector::const_iterator it = skills.begin(); it != end; ++it) + std::vector::const_iterator end = mSkills.end(); + for (std::vector::const_iterator it = mSkills.begin(); it != end; ++it) { if (*it == skill) continue; @@ -669,25 +669,25 @@ void CreateClassDialog::onSkillSelected() } } - skill->setSkillId(skillDialog->getSkillId()); - mWindowManager.removeDialog(skillDialog); - skillDialog = 0; + skill->setSkillId(mSkillDialog->getSkillId()); + mWindowManager.removeDialog(mSkillDialog); + mSkillDialog = 0; update(); } void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender) { - descDialog = new DescriptionDialog(mWindowManager); - descDialog->setTextInput(description); - descDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); - descDialog->setVisible(true); + mDescDialog = new DescriptionDialog(mWindowManager); + mDescDialog->setTextInput(mDescription); + mDescDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered); + mDescDialog->setVisible(true); } void CreateClassDialog::onDescriptionEntered(WindowBase* parWindow) { - description = descDialog->getTextInput(); - mWindowManager.removeDialog(descDialog); - descDialog = 0; + mDescription = mDescDialog->getTextInput(); + mWindowManager.removeDialog(mDescDialog); + mDescDialog = 0; } void CreateClassDialog::onOkClicked(MyGUI::Widget* _sender) @@ -710,24 +710,24 @@ SelectSpecializationDialog::SelectSpecializationDialog(WindowManager& parWindowM setText("LabelT", mWindowManager.getGameSettingString("sSpecializationMenu1", "")); - getWidget(specialization0, "Specialization0"); - getWidget(specialization1, "Specialization1"); - getWidget(specialization2, "Specialization2"); + getWidget(mSpecialization0, "Specialization0"); + getWidget(mSpecialization1, "Specialization1"); + getWidget(mSpecialization2, "Specialization2"); std::string combat = mWindowManager.getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Combat], ""); std::string magic = mWindowManager.getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Magic], ""); std::string stealth = mWindowManager.getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Stealth], ""); - specialization0->setCaption(combat); - specialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - specialization1->setCaption(magic); - specialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - specialization2->setCaption(stealth); - specialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); - specializationId = ESM::Class::Combat; + mSpecialization0->setCaption(combat); + mSpecialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecialization1->setCaption(magic); + mSpecialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecialization2->setCaption(stealth); + mSpecialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked); + mSpecializationId = ESM::Class::Combat; - ToolTips::createSpecializationToolTip(specialization0, combat, ESM::Class::Combat); - ToolTips::createSpecializationToolTip(specialization1, magic, ESM::Class::Magic); - ToolTips::createSpecializationToolTip(specialization2, stealth, ESM::Class::Stealth); + ToolTips::createSpecializationToolTip(mSpecialization0, combat, ESM::Class::Combat); + ToolTips::createSpecializationToolTip(mSpecialization1, magic, ESM::Class::Magic); + ToolTips::createSpecializationToolTip(mSpecialization2, stealth, ESM::Class::Stealth); MyGUI::ButtonPtr cancelButton; getWidget(cancelButton, "CancelButton"); @@ -748,12 +748,12 @@ SelectSpecializationDialog::~SelectSpecializationDialog() void SelectSpecializationDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) { - if (_sender == specialization0) - specializationId = ESM::Class::Combat; - else if (_sender == specialization1) - specializationId = ESM::Class::Magic; - else if (_sender == specialization2) - specializationId = ESM::Class::Stealth; + if (_sender == mSpecialization0) + mSpecializationId = ESM::Class::Combat; + else if (_sender == mSpecialization1) + mSpecializationId = ESM::Class::Magic; + else if (_sender == mSpecialization2) + mSpecializationId = ESM::Class::Stealth; else return; @@ -807,7 +807,7 @@ SelectAttributeDialog::~SelectAttributeDialog() void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) { // TODO: Change MWAttribute to set and get AttributeID enum instead of int - attributeId = static_cast(_sender->getAttributeId()); + mAttributeId = static_cast(_sender->getAttributeId()); eventItemSelected(); } @@ -833,44 +833,44 @@ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager) for(int i = 0; i < 9; i++) { char theIndex = '0'+i; - getWidget(combatSkill[i], std::string("CombatSkill").append(1, theIndex)); - getWidget(magicSkill[i], std::string("MagicSkill").append(1, theIndex)); - getWidget(stealthSkill[i], std::string("StealthSkill").append(1, theIndex)); + getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); + getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); + getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); } - struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} skills[3][9] = { + struct {Widgets::MWSkillPtr widget; ESM::Skill::SkillEnum skillId;} mSkills[3][9] = { { - {combatSkill[0], ESM::Skill::Block}, - {combatSkill[1], ESM::Skill::Armorer}, - {combatSkill[2], ESM::Skill::MediumArmor}, - {combatSkill[3], ESM::Skill::HeavyArmor}, - {combatSkill[4], ESM::Skill::BluntWeapon}, - {combatSkill[5], ESM::Skill::LongBlade}, - {combatSkill[6], ESM::Skill::Axe}, - {combatSkill[7], ESM::Skill::Spear}, - {combatSkill[8], ESM::Skill::Athletics} + {mCombatSkill[0], ESM::Skill::Block}, + {mCombatSkill[1], ESM::Skill::Armorer}, + {mCombatSkill[2], ESM::Skill::MediumArmor}, + {mCombatSkill[3], ESM::Skill::HeavyArmor}, + {mCombatSkill[4], ESM::Skill::BluntWeapon}, + {mCombatSkill[5], ESM::Skill::LongBlade}, + {mCombatSkill[6], ESM::Skill::Axe}, + {mCombatSkill[7], ESM::Skill::Spear}, + {mCombatSkill[8], ESM::Skill::Athletics} }, { - {magicSkill[0], ESM::Skill::Enchant}, - {magicSkill[1], ESM::Skill::Destruction}, - {magicSkill[2], ESM::Skill::Alteration}, - {magicSkill[3], ESM::Skill::Illusion}, - {magicSkill[4], ESM::Skill::Conjuration}, - {magicSkill[5], ESM::Skill::Mysticism}, - {magicSkill[6], ESM::Skill::Restoration}, - {magicSkill[7], ESM::Skill::Alchemy}, - {magicSkill[8], ESM::Skill::Unarmored} + {mMagicSkill[0], ESM::Skill::Enchant}, + {mMagicSkill[1], ESM::Skill::Destruction}, + {mMagicSkill[2], ESM::Skill::Alteration}, + {mMagicSkill[3], ESM::Skill::Illusion}, + {mMagicSkill[4], ESM::Skill::Conjuration}, + {mMagicSkill[5], ESM::Skill::Mysticism}, + {mMagicSkill[6], ESM::Skill::Restoration}, + {mMagicSkill[7], ESM::Skill::Alchemy}, + {mMagicSkill[8], ESM::Skill::Unarmored} }, { - {stealthSkill[0], ESM::Skill::Security}, - {stealthSkill[1], ESM::Skill::Sneak}, - {stealthSkill[2], ESM::Skill::Acrobatics}, - {stealthSkill[3], ESM::Skill::LightArmor}, - {stealthSkill[4], ESM::Skill::ShortBlade}, - {stealthSkill[5] ,ESM::Skill::Marksman}, - {stealthSkill[6] ,ESM::Skill::Mercantile}, - {stealthSkill[7] ,ESM::Skill::Speechcraft}, - {stealthSkill[8] ,ESM::Skill::HandToHand} + {mStealthSkill[0], ESM::Skill::Security}, + {mStealthSkill[1], ESM::Skill::Sneak}, + {mStealthSkill[2], ESM::Skill::Acrobatics}, + {mStealthSkill[3], ESM::Skill::LightArmor}, + {mStealthSkill[4], ESM::Skill::ShortBlade}, + {mStealthSkill[5] ,ESM::Skill::Marksman}, + {mStealthSkill[6] ,ESM::Skill::Mercantile}, + {mStealthSkill[7] ,ESM::Skill::Speechcraft}, + {mStealthSkill[8] ,ESM::Skill::HandToHand} } }; @@ -878,10 +878,10 @@ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager) { for (int i = 0; i < 9; ++i) { - skills[spec][i].widget->setWindowManager(&mWindowManager); - skills[spec][i].widget->setSkillId(skills[spec][i].skillId); - skills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); - ToolTips::createSkillToolTip(skills[spec][i].widget, skills[spec][i].widget->getSkillId()); + mSkills[spec][i].widget->setWindowManager(&mWindowManager); + mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); + mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); + ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); } } @@ -904,7 +904,7 @@ SelectSkillDialog::~SelectSkillDialog() void SelectSkillDialog::onSkillClicked(Widgets::MWSkillPtr _sender) { - skillId = _sender->getSkillId(); + mSkillId = _sender->getSkillId(); eventItemSelected(); } @@ -921,7 +921,7 @@ DescriptionDialog::DescriptionDialog(WindowManager& parWindowManager) // Centre dialog center(); - getWidget(textEdit, "TextEdit"); + getWidget(mTextEdit, "TextEdit"); MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); @@ -931,7 +931,7 @@ DescriptionDialog::DescriptionDialog(WindowManager& parWindowManager) okButton->setCoord(234 - buttonWidth, 214, buttonWidth, 24); // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(textEdit); + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index c9e7aef7b..4d8d9fa23 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -45,11 +45,11 @@ namespace MWGui void fitToText(MyGUI::TextBox* widget); void layoutVertically(MyGUI::WidgetPtr widget, int margin); - int currentButton; - MyGUI::WidgetPtr textBox; - MyGUI::TextBox* text; - MyGUI::WidgetPtr buttonBar; - std::vector buttons; + int mCurrentButton; + MyGUI::WidgetPtr mTextBox; + MyGUI::TextBox* mText; + MyGUI::WidgetPtr mButtonBar; + std::vector mButtons; }; // Lets the player choose between 3 ways of creating a class @@ -90,10 +90,10 @@ namespace MWGui void onBackClicked(MyGUI::Widget* _sender); private: - MyGUI::ImageBox* classImage; - MyGUI::TextBox* className; + MyGUI::ImageBox* mClassImage; + MyGUI::TextBox* mClassName; - std::string currentClassId; + std::string mCurrentClassId; }; class PickClassDialog : public WindowBase @@ -101,7 +101,7 @@ namespace MWGui public: PickClassDialog(WindowManager& parWindowManager); - const std::string &getClassId() const { return currentClassId; } + const std::string &getClassId() const { return mCurrentClassId; } void setClassId(const std::string &classId); void setNextButtonShow(bool shown); @@ -125,14 +125,14 @@ namespace MWGui void updateClasses(); void updateStats(); - MyGUI::ImageBox* classImage; - MyGUI::ListBox* classList; - MyGUI::TextBox* specializationName; - Widgets::MWAttributePtr favoriteAttribute[2]; - Widgets::MWSkillPtr majorSkill[5]; - Widgets::MWSkillPtr minorSkill[5]; + MyGUI::ImageBox* mClassImage; + MyGUI::ListBox* mClassList; + MyGUI::TextBox* mSpecializationName; + Widgets::MWAttributePtr mFavoriteAttribute[2]; + Widgets::MWSkillPtr mMajorSkill[5]; + Widgets::MWSkillPtr mMinorSkill[5]; - std::string currentClassId; + std::string mCurrentClassId; }; class SelectSpecializationDialog : public WindowBase @@ -141,7 +141,7 @@ namespace MWGui SelectSpecializationDialog(WindowManager& parWindowManager); ~SelectSpecializationDialog(); - ESM::Class::Specialization getSpecializationId() const { return specializationId; } + ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } // Events typedef delegates::CMultiDelegate0 EventHandle_Void; @@ -161,9 +161,9 @@ namespace MWGui void onCancelClicked(MyGUI::Widget* _sender); private: - MyGUI::TextBox *specialization0, *specialization1, *specialization2; + MyGUI::TextBox *mSpecialization0, *mSpecialization1, *mSpecialization2; - ESM::Class::Specialization specializationId; + ESM::Class::Specialization mSpecializationId; }; class SelectAttributeDialog : public WindowBase @@ -172,9 +172,9 @@ namespace MWGui SelectAttributeDialog(WindowManager& parWindowManager); ~SelectAttributeDialog(); - ESM::Attribute::AttributeID getAttributeId() const { return attributeId; } - Widgets::MWAttributePtr getAffectedWidget() const { return affectedWidget; } - void setAffectedWidget(Widgets::MWAttributePtr widget) { affectedWidget = widget; } + ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } + Widgets::MWAttributePtr getAffectedWidget() const { return mAffectedWidget; } + void setAffectedWidget(Widgets::MWAttributePtr widget) { mAffectedWidget = widget; } // Events typedef delegates::CMultiDelegate0 EventHandle_Void; @@ -194,9 +194,9 @@ namespace MWGui void onCancelClicked(MyGUI::Widget* _sender); private: - Widgets::MWAttributePtr affectedWidget; + Widgets::MWAttributePtr mAffectedWidget; - ESM::Attribute::AttributeID attributeId; + ESM::Attribute::AttributeID mAttributeId; }; class SelectSkillDialog : public WindowBase @@ -205,9 +205,9 @@ namespace MWGui SelectSkillDialog(WindowManager& parWindowManager); ~SelectSkillDialog(); - ESM::Skill::SkillEnum getSkillId() const { return skillId; } - Widgets::MWSkillPtr getAffectedWidget() const { return affectedWidget; } - void setAffectedWidget(Widgets::MWSkillPtr widget) { affectedWidget = widget; } + ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } + Widgets::MWSkillPtr getAffectedWidget() const { return mAffectedWidget; } + void setAffectedWidget(Widgets::MWSkillPtr widget) { mAffectedWidget = widget; } // Events typedef delegates::CMultiDelegate0 EventHandle_Void; @@ -227,12 +227,12 @@ namespace MWGui void onCancelClicked(MyGUI::Widget* _sender); private: - Widgets::MWSkillPtr combatSkill[9]; - Widgets::MWSkillPtr magicSkill[9]; - Widgets::MWSkillPtr stealthSkill[9]; - Widgets::MWSkillPtr affectedWidget; + Widgets::MWSkillPtr mCombatSkill[9]; + Widgets::MWSkillPtr mMagicSkill[9]; + Widgets::MWSkillPtr mStealthSkill[9]; + Widgets::MWSkillPtr mAffectedWidget; - ESM::Skill::SkillEnum skillId; + ESM::Skill::SkillEnum mSkillId; }; class DescriptionDialog : public WindowBase @@ -241,14 +241,14 @@ namespace MWGui DescriptionDialog(WindowManager& parWindowManager); ~DescriptionDialog(); - std::string getTextInput() const { return textEdit ? textEdit->getOnlyText() : ""; } - void setTextInput(const std::string &text) { if (textEdit) textEdit->setOnlyText(text); } + std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } + void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } protected: void onOkClicked(MyGUI::Widget* _sender); private: - MyGUI::EditPtr textEdit; + MyGUI::EditPtr mTextEdit; }; class CreateClassDialog : public WindowBase @@ -294,20 +294,20 @@ namespace MWGui void update(); private: - MyGUI::EditPtr editName; - MyGUI::TextBox* specializationName; - Widgets::MWAttributePtr favoriteAttribute0, favoriteAttribute1; - Widgets::MWSkillPtr majorSkill[5]; - Widgets::MWSkillPtr minorSkill[5]; - std::vector skills; - std::string description; + MyGUI::EditPtr mEditName; + MyGUI::TextBox* mSpecializationName; + Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1; + Widgets::MWSkillPtr mMajorSkill[5]; + Widgets::MWSkillPtr mMinorSkill[5]; + std::vector mSkills; + std::string mDescription; - SelectSpecializationDialog *specDialog; - SelectAttributeDialog *attribDialog; - SelectSkillDialog *skillDialog; - DescriptionDialog *descDialog; + SelectSpecializationDialog *mSpecDialog; + SelectAttributeDialog *mAttribDialog; + SelectSkillDialog *mSkillDialog; + DescriptionDialog *mDescDialog; - ESM::Class::Specialization specializationId; + ESM::Class::Specialization mSpecializationId; }; } #endif diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index acf0bf130..efb6dc036 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -51,9 +51,9 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager) center(); //History view - getWidget(history, "History"); - history->setOverflowToTheLeft(true); - history->setMaxTextLength(1000000); + getWidget(mHistory, "History"); + mHistory->setOverflowToTheLeft(true); + mHistory->setMaxTextLength(1000000); Widget* eventbox; //An EditBox cannot receive mouse click events, so we use an @@ -63,36 +63,36 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager) eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel); //Topics list - getWidget(topicsList, "TopicsList"); - topicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); + getWidget(mTopicsList, "TopicsList"); + mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic); MyGUI::ButtonPtr byeButton; getWidget(byeButton, "ByeButton"); byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked); - getWidget(pDispositionBar, "Disposition"); - getWidget(pDispositionText,"DispositionText"); + getWidget(mDispositionBar, "Disposition"); + getWidget(mDispositionText,"DispositionText"); static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize); } void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) { - ISubWidgetText* t = history->getClient()->getSubWidgetText(); + ISubWidgetText* t = mHistory->getClient()->getSubWidgetText(); if(t == nullptr) return; const IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); size_t cursorPosition = t->getCursorPosition(lastPressed); - MyGUI::UString color = history->getColorAtPos(cursorPosition); + MyGUI::UString color = mHistory->getColorAtPos(cursorPosition); if (!mEnabled && color == "#572D21") MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); if(color != "#B29154") { - UString key = history->getColorTextAt(cursorPosition); + UString key = mHistory->getColorTextAt(cursorPosition); if(color == "#686EBA") MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); if(color == "#572D21") MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); @@ -101,15 +101,15 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) void DialogueWindow::onWindowResize(MyGUI::Window* _sender) { - topicsList->adjustSize(); + mTopicsList->adjustSize(); } void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - if (history->getVScrollPosition() - _rel*0.3 < 0) - history->setVScrollPosition(0); + if (mHistory->getVScrollPosition() - _rel*0.3 < 0) + mHistory->setVScrollPosition(0); else - history->setVScrollPosition(history->getVScrollPosition() - _rel*0.3); + mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3); } void DialogueWindow::onByeClicked(MyGUI::Widget* _sender) @@ -136,40 +136,40 @@ void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) { mEnabled = true; mPtr = actor; - topicsList->setEnabled(true); + mTopicsList->setEnabled(true); setTitle(npcName); - topicsList->clear(); - history->eraseText(0,history->getTextLength()); + mTopicsList->clear(); + mHistory->eraseText(0,mHistory->getTextLength()); updateOptions(); } void DialogueWindow::setKeywords(std::list keyWords) { - topicsList->clear(); + mTopicsList->clear(); bool anyService = mShowTrade; if (mShowTrade) - topicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarter")->str); + mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarter")->str); if (anyService) - topicsList->addSeparator(); + mTopicsList->addSeparator(); for(std::list::iterator it = keyWords.begin(); it != keyWords.end(); ++it) { - topicsList->addItem(*it); + mTopicsList->addItem(*it); } - topicsList->adjustSize(); + mTopicsList->adjustSize(); } void DialogueWindow::removeKeyword(std::string keyWord) { - if(topicsList->hasItem(keyWord)) + if(mTopicsList->hasItem(keyWord)) { - topicsList->removeItem(keyWord); + mTopicsList->removeItem(keyWord); } - topicsList->adjustSize(); + mTopicsList->adjustSize(); } void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2) @@ -206,9 +206,9 @@ void addColorInString(std::string& str, const std::string& keyword,std::string c std::string DialogueWindow::parseText(std::string text) { bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored) - for(unsigned int i = 0;igetItemCount();i++) + for(unsigned int i = 0;igetItemCount();i++) { - std::string keyWord = topicsList->getItemNameAt(i); + std::string keyWord = mTopicsList->getItemNameAt(i); if (separatorReached && keyWord != "") addColorInString(text,keyWord,"#686EBA","#B29154"); else @@ -219,7 +219,7 @@ std::string DialogueWindow::parseText(std::string text) void DialogueWindow::addText(std::string text) { - history->addDialogText("#B29154"+parseText(text)+"#B29154"); + mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); } void DialogueWindow::addTitle(std::string text) @@ -227,37 +227,37 @@ void DialogueWindow::addTitle(std::string text) // This is called from the dialogue manager, so text is // case-smashed - thus we have to retrieve the correct case // of the text through the topic list. - for (size_t i=0; igetItemCount(); ++i) + for (size_t i=0; igetItemCount(); ++i) { - std::string item = topicsList->getItemNameAt(i); + std::string item = mTopicsList->getItemNameAt(i); if (lower_string(item) == text) text = item; } - history->addDialogHeading(text); + mHistory->addDialogHeading(text); } void DialogueWindow::askQuestion(std::string question) { - history->addDialogText("#572D21"+question+"#B29154"+" "); + mHistory->addDialogText("#572D21"+question+"#B29154"+" "); } void DialogueWindow::updateOptions() { //Clear the list of topics - topicsList->clear(); - history->eraseText(0,history->getTextLength()); + mTopicsList->clear(); + mHistory->eraseText(0, mHistory->getTextLength()); - pDispositionBar->setProgressRange(100); - pDispositionBar->setProgressPosition(40); - pDispositionText->eraseText(0,pDispositionText->getTextLength()); - pDispositionText->addText("#B29154"+std::string("40/100")+"#B29154"); + mDispositionBar->setProgressRange(100); + mDispositionBar->setProgressPosition(40); + mDispositionText->eraseText(0, mDispositionText->getTextLength()); + mDispositionText->addText("#B29154"+std::string("40/100")+"#B29154"); } void DialogueWindow::goodbye() { - history->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGoodbye")->str); - topicsList->setEnabled(false); + mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGoodbye")->str); + mTopicsList->setEnabled(false); mEnabled = false; } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 5d808b5a7..e2824bead 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -73,10 +73,10 @@ namespace MWGui bool mEnabled; - DialogueHistory* history; - Widgets::MWList* topicsList; - MyGUI::ProgressPtr pDispositionBar; - MyGUI::EditPtr pDispositionText; + DialogueHistory* mHistory; + Widgets::MWList* mTopicsList; + MyGUI::ProgressPtr mDispositionBar; + MyGUI::EditPtr mDispositionText; }; } #endif diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 78eefa338..d2eef26ac 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -28,24 +28,24 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) , health(NULL) , magicka(NULL) , stamina(NULL) - , weapImage(NULL) - , spellImage(NULL) - , weapStatus(NULL) - , spellStatus(NULL) - , effectBox(NULL) - , effect1(NULL) - , minimap(NULL) - , compass(NULL) - , crosshair(NULL) + , mWeapImage(NULL) + , mSpellImage(NULL) + , mWeapStatus(NULL) + , mSpellStatus(NULL) + , mEffectBox(NULL) + , mEffect1(NULL) + , mMinimap(NULL) + , mCompass(NULL) + , mCrosshair(NULL) , fpsbox(NULL) , fpscounter(NULL) , trianglecounter(NULL) , batchcounter(NULL) - , hmsBaseLeft(0) - , weapBoxBaseLeft(0) - , spellBoxBaseLeft(0) - , effectBoxBaseRight(0) - , minimapBoxBaseRight(0) + , mHealthManaStaminaBaseLeft(0) + , mWeapBoxBaseLeft(0) + , mSpellBoxBaseLeft(0) + , mEffectBoxBaseRight(0) + , mMinimapBoxBaseRight(0) , mDragAndDrop(dragAndDrop) , mCellNameTimer(0.0f) , mCellNameBox(NULL) @@ -62,7 +62,7 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) getWidget(magicka, "Magicka"); getWidget(stamina, "Stamina"); - hmsBaseLeft = mHealthFrame->getLeft(); + mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; getWidget(healthFrame, "HealthFrame"); @@ -75,33 +75,33 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); // Item and spell images and status bars - getWidget(weapBox, "WeapBox"); - getWidget(weapImage, "WeapImage"); - getWidget(weapStatus, "WeapStatus"); - weapBoxBaseLeft = weapBox->getLeft(); - weapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); + getWidget(mWeapBox, "WeapBox"); + getWidget(mWeapImage, "WeapImage"); + getWidget(mWeapStatus, "WeapStatus"); + mWeapBoxBaseLeft = mWeapBox->getLeft(); + mWeapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWeaponClicked); - getWidget(spellBox, "SpellBox"); - getWidget(spellImage, "SpellImage"); - getWidget(spellStatus, "SpellStatus"); - spellBoxBaseLeft = spellBox->getLeft(); - spellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); + getWidget(mSpellBox, "SpellBox"); + getWidget(mSpellImage, "SpellImage"); + getWidget(mSpellStatus, "SpellStatus"); + mSpellBoxBaseLeft = mSpellBox->getLeft(); + mSpellBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); - getWidget(effectBox, "EffectBox"); - getWidget(effect1, "Effect1"); - effectBoxBaseRight = viewSize.width - effectBox->getRight(); - effectBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); + getWidget(mEffectBox, "EffectBox"); + getWidget(mEffect1, "Effect1"); + mEffectBoxBaseRight = viewSize.width - mEffectBox->getRight(); + mEffectBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMagicClicked); - getWidget(minimapBox, "MiniMapBox"); - minimapBoxBaseRight = viewSize.width - minimapBox->getRight(); - minimapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); - getWidget(minimap, "MiniMap"); - getWidget(compass, "Compass"); + getWidget(mMinimapBox, "MiniMapBox"); + mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); + mMinimapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); + getWidget(mMinimap, "MiniMap"); + getWidget(mCompass, "Compass"); getWidget(mCellNameBox, "CellName"); getWidget(mWeaponSpellBox, "WeaponSpellName"); - getWidget(crosshair, "Crosshair"); + getWidget(mCrosshair, "Crosshair"); setFpsLevel(fpsLevel); @@ -110,7 +110,7 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) setEffect("icons\\s\\tx_s_chameleon.dds"); - LocalMapBase::init(minimap, compass, this); + LocalMapBase::init(mMinimap, mCompass, this); mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked); mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver); @@ -159,7 +159,7 @@ void HUD::setBatchCount(unsigned int count) void HUD::setEffect(const char *img) { - effect1->setImageTexture(img); + mEffect1->setImageTexture(img); } void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) @@ -202,10 +202,10 @@ void HUD::setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellV { int weapDx = 0, spellDx = 0; if (!hmsVisible) - spellDx = weapDx = weapBoxBaseLeft - hmsBaseLeft; + spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; if (!weapVisible) - spellDx += spellBoxBaseLeft - weapBoxBaseLeft; + spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; mWeaponVisible = weapVisible; mSpellVisible = spellVisible; @@ -215,10 +215,10 @@ void HUD::setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellV health->setVisible(hmsVisible); stamina->setVisible(hmsVisible); magicka->setVisible(hmsVisible); - weapBox->setPosition(weapBoxBaseLeft - weapDx, weapBox->getTop()); - weapBox->setVisible(weapVisible); - spellBox->setPosition(spellBoxBaseLeft - spellDx, spellBox->getTop()); - spellBox->setVisible(spellVisible); + mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); + mWeapBox->setVisible(weapVisible); + mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + mSpellBox->setVisible(spellVisible); } void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible) @@ -228,12 +228,12 @@ void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible // effect box can have variable width -> variable left coordinate int effectsDx = 0; if (!minimapBoxVisible) - effectsDx = (viewSize.width - minimapBoxBaseRight) - (viewSize.width - effectBoxBaseRight); + effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); mMapVisible = minimapBoxVisible; - minimapBox->setVisible(minimapBoxVisible); - effectBox->setPosition((viewSize.width - effectBoxBaseRight) - effectBox->getWidth() + effectsDx, effectBox->getTop()); - effectBox->setVisible(effectBoxVisible); + mMinimapBox->setVisible(minimapBoxVisible); + mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); + mEffectBox->setVisible(effectBoxVisible); } void HUD::onWorldClicked(MyGUI::Widget* _sender) @@ -395,14 +395,14 @@ void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) mWeaponSpellBox->setVisible(true); } - spellStatus->setProgressRange(100); - spellStatus->setProgressPosition(successChancePercent); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(successChancePercent); - if (spellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(spellImage->getChildAt(0)); + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - spellBox->setUserString("ToolTipType", "Spell"); - spellBox->setUserString("Spell", spellId); + mSpellBox->setUserString("ToolTipType", "Spell"); + mSpellBox->setUserString("Spell", spellId); // use the icon of the first effect const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().magicEffects.find(spell->effects.list.front().effectID); @@ -411,7 +411,7 @@ void HUD::setSelectedSpell(const std::string& spellId, int successChancePercent) icon.insert(slashPos+1, "b_"); icon = std::string("icons\\") + icon; Widgets::fixTexturePath(icon); - spellImage->setImageTexture(icon); + mSpellImage->setImageTexture(icon); } void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) @@ -425,17 +425,17 @@ void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) mWeaponSpellBox->setVisible(true); } - spellStatus->setProgressRange(100); - spellStatus->setProgressPosition(chargePercent); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(chargePercent); - if (spellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(spellImage->getChildAt(0)); + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); - spellBox->setUserString("ToolTipType", "ItemPtr"); - spellBox->setUserData(item); + mSpellBox->setUserString("ToolTipType", "ItemPtr"); + mSpellBox->setUserData(item); - spellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = spellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) , MyGUI::Align::Stretch); std::string path = std::string("icons\\"); @@ -456,14 +456,14 @@ void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) mWeaponSpellBox->setVisible(true); } - weapBox->setUserString("ToolTipType", "ItemPtr"); - weapBox->setUserData(item); + mWeapBox->setUserString("ToolTipType", "ItemPtr"); + mWeapBox->setUserData(item); - weapStatus->setProgressRange(100); - weapStatus->setProgressPosition(durabilityPercent); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(durabilityPercent); - if (weapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(weapImage->getChildAt(0)); + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); std::string path = std::string("icons\\"); path+=MWWorld::Class::get(item).getInventoryIcon(item); @@ -471,14 +471,14 @@ void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) if (MWWorld::Class::get(item).getEnchantment(item) != "") { - weapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); - MyGUI::ImageBox* itemBox = weapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) + mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); + MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal("ImageBox", MyGUI::FloatCoord(0,0,1,1) , MyGUI::Align::Stretch); itemBox->setImageTexture(path); itemBox->setNeedMouseFocus(false); } else - weapImage->setImageTexture(path); + mWeapImage->setImageTexture(path); } void HUD::unsetSelectedSpell() @@ -492,12 +492,12 @@ void HUD::unsetSelectedSpell() mWeaponSpellBox->setVisible(true); } - if (spellImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(spellImage->getChildAt(0)); - spellStatus->setProgressRange(100); - spellStatus->setProgressPosition(0); - spellImage->setImageTexture(""); - spellBox->clearUserStrings(); + if (mSpellImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0)); + mSpellStatus->setProgressRange(100); + mSpellStatus->setProgressPosition(0); + mSpellImage->setImageTexture(""); + mSpellBox->clearUserStrings(); } void HUD::unsetSelectedWeapon() @@ -511,10 +511,10 @@ void HUD::unsetSelectedWeapon() mWeaponSpellBox->setVisible(true); } - if (weapImage->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(weapImage->getChildAt(0)); - weapStatus->setProgressRange(100); - weapStatus->setProgressPosition(0); - weapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); - weapBox->clearUserStrings(); + if (mWeapImage->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0)); + mWeapStatus->setProgressRange(100); + mWeapStatus->setProgressPosition(0); + mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); + mWeapBox->clearUserStrings(); } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index c6bcdd4e9..485c788fc 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -37,14 +37,14 @@ namespace MWGui MyGUI::ProgressPtr health, magicka, stamina; MyGUI::Widget* mHealthFrame; - MyGUI::Widget *weapBox, *spellBox; - MyGUI::ImageBox *weapImage, *spellImage; - MyGUI::ProgressPtr weapStatus, spellStatus; - MyGUI::Widget *effectBox, *minimapBox; - MyGUI::ImageBox* effect1; - MyGUI::ScrollView* minimap; - MyGUI::ImageBox* compass; - MyGUI::ImageBox* crosshair; + MyGUI::Widget *mWeapBox, *mSpellBox; + MyGUI::ImageBox *mWeapImage, *mSpellImage; + MyGUI::ProgressPtr mWeapStatus, mSpellStatus; + MyGUI::Widget *mEffectBox, *mMinimapBox; + MyGUI::ImageBox* mEffect1; + MyGUI::ScrollView* mMinimap; + MyGUI::ImageBox* mCompass; + MyGUI::ImageBox* mCrosshair; MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; @@ -55,9 +55,9 @@ namespace MWGui private: // bottom left elements - int hmsBaseLeft, weapBoxBaseLeft, spellBoxBaseLeft; + int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft; // bottom right elements - int minimapBoxBaseRight, effectBoxBaseRight; + int mMinimapBoxBaseRight, mEffectBoxBaseRight; DragAndDrop* mDragAndDrop; diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 75b546c7b..8b50c9ef1 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -84,7 +84,7 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) : WindowBase("openmw_journal.layout", parWindowManager) - , lastPos(0) + , mLastPos(0) , mVisible(false) { //setCoord(0,0,498, 342); @@ -148,19 +148,19 @@ void MWGui::JournalWindow::open() { if(left) { - leftPages.push_back(*it); + mLeftPages.push_back(*it); } else { - rightPages.push_back(*it); + mRightPages.push_back(*it); } left = !left; } - if(!left) rightPages.push_back(""); + if(!left) mRightPages.push_back(""); - mPageNumber = leftPages.size()-1; - displayLeftText(leftPages[mPageNumber]); - displayRightText(rightPages[mPageNumber]); + mPageNumber = mLeftPages.size()-1; + displayLeftText(mLeftPages[mPageNumber]); + displayRightText(mRightPages[mPageNumber]); } else @@ -184,13 +184,13 @@ void MWGui::JournalWindow::displayRightText(std::string text) void MWGui::JournalWindow::notifyNextPage(MyGUI::WidgetPtr _sender) { - if(mPageNumber < int(leftPages.size())-1) + if(mPageNumber < int(mLeftPages.size())-1) { std::string nextSound = "book page2"; MWBase::Environment::get().getSoundManager()->playSound (nextSound, 1.0, 1.0); mPageNumber = mPageNumber + 1; - displayLeftText(leftPages[mPageNumber]); - displayRightText(rightPages[mPageNumber]); + displayLeftText(mLeftPages[mPageNumber]); + displayRightText(mRightPages[mPageNumber]); } } @@ -201,7 +201,7 @@ void MWGui::JournalWindow::notifyPrevPage(MyGUI::WidgetPtr _sender) std::string prevSound = "book page"; MWBase::Environment::get().getSoundManager()->playSound (prevSound, 1.0, 1.0); mPageNumber = mPageNumber - 1; - displayLeftText(leftPages[mPageNumber]); - displayRightText(rightPages[mPageNumber]); + displayLeftText(mLeftPages[mPageNumber]); + displayRightText(mRightPages[mPageNumber]); } } diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index cacdb9414..0c85ebf08 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -31,17 +31,17 @@ namespace MWGui void notifyNextPage(MyGUI::WidgetPtr _sender); void notifyPrevPage(MyGUI::WidgetPtr _sender); - static const int lineHeight; + static const int sLineHeight; - MyGUI::WidgetPtr skillAreaWidget, skillClientWidget; - MyGUI::ScrollBar* skillScrollerWidget; - int lastPos, clientHeight; + MyGUI::WidgetPtr mSkillAreaWidget, mSkillClientWidget; + MyGUI::ScrollBar* mSkillScrollerWidget; + int mLastPos, mClientHeight; MyGUI::EditPtr mLeftTextWidget; MyGUI::EditPtr mRightTextWidget; MyGUI::ButtonPtr mPrevBtn; MyGUI::ButtonPtr mNextBtn; - std::vector leftPages; - std::vector rightPages; + std::vector mLeftPages; + std::vector mRightPages; int mPageNumber; //store the number of the current left page bool mVisible; }; diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 68326a3c3..2b00ca05c 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -149,7 +149,7 @@ int MessageBoxManager::readPressedButton () MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message) : Layout("openmw_messagebox.layout") , mMessageBoxManager(parMessageBoxManager) - , cMessage(message) + , mMessage(message) { // defines mFixedWidth = 300; @@ -160,7 +160,7 @@ MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::strin getWidget(mMessageWidget, "message"); mMessageWidget->setOverflowToTheLeft(true); - mMessageWidget->setCaptionWithReplacing(cMessage); + mMessageWidget->setCaptionWithReplacing(mMessage); MyGUI::IntSize size; size.width = mFixedWidth; diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 33155b2a0..75393ec94 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -59,7 +59,7 @@ namespace MWGui protected: MessageBoxManager& mMessageBoxManager; int mHeight; - const std::string& cMessage; + const std::string& mMessage; MyGUI::EditPtr mMessageWidget; int mFixedWidth; int mBottomPadding; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 9ae453016..dd11ec011 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -18,11 +18,11 @@ using namespace Widgets; RaceDialog::RaceDialog(WindowManager& parWindowManager) : WindowBase("openmw_chargen_race.layout", parWindowManager) - , genderIndex(0) - , faceIndex(0) - , hairIndex(0) - , faceCount(10) - , hairCount(14) + , mGenderIndex(0) + , mFaceIndex(0) + , mHairIndex(0) + , mFaceCount(10) + , mHairCount(14) { // Centre dialog center(); @@ -31,13 +31,13 @@ RaceDialog::RaceDialog(WindowManager& parWindowManager) // real calls from outside the class later. setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); - getWidget(appearanceBox, "AppearanceBox"); + getWidget(mAppearanceBox, "AppearanceBox"); - getWidget(headRotate, "HeadRotate"); - headRotate->setScrollRange(50); - headRotate->setScrollPosition(20); - headRotate->setScrollViewPage(10); - headRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); + getWidget(mHeadRotate, "HeadRotate"); + mHeadRotate->setScrollRange(50); + mHeadRotate->setScrollPosition(20); + mHeadRotate->setScrollViewPage(10); + mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); // Set up next/previous buttons MyGUI::ButtonPtr prevButton, nextButton; @@ -61,16 +61,16 @@ RaceDialog::RaceDialog(WindowManager& parWindowManager) nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu4", "Race")); - getWidget(raceList, "RaceList"); - raceList->setScrollVisible(true); - raceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - raceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); - raceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + getWidget(mRaceList, "RaceList"); + mRaceList->setScrollVisible(true); + mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); + mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); setText("SkillsT", mWindowManager.getGameSettingString("sBonusSkillTitle", "Skill Bonus")); - getWidget(skillList, "SkillList"); + getWidget(mSkillList, "SkillList"); setText("SpellPowerT", mWindowManager.getGameSettingString("sRaceMenu7", "Specials")); - getWidget(spellPowerList, "SpellPowerList"); + getWidget(mSpellPowerList, "SpellPowerList"); MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); @@ -117,14 +117,14 @@ void RaceDialog::open() void RaceDialog::setRaceId(const std::string &raceId) { - currentRaceId = raceId; - raceList->setIndexSelected(MyGUI::ITEM_NONE); - size_t count = raceList->getItemCount(); + mCurrentRaceId = raceId; + mRaceList->setIndexSelected(MyGUI::ITEM_NONE); + size_t count = mRaceList->getItemCount(); for (size_t i = 0; i < count; ++i) { - if (boost::iequals(*raceList->getItemDataAt(i), raceId)) + if (boost::iequals(*mRaceList->getItemDataAt(i), raceId)) { - raceList->setIndexSelected(i); + mRaceList->setIndexSelected(i); break; } } @@ -162,32 +162,32 @@ void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) { - genderIndex = wrap(genderIndex - 1, 2); + mGenderIndex = wrap(mGenderIndex - 1, 2); } void RaceDialog::onSelectNextGender(MyGUI::Widget*) { - genderIndex = wrap(genderIndex + 1, 2); + mGenderIndex = wrap(mGenderIndex + 1, 2); } void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) { - faceIndex = wrap(faceIndex - 1, faceCount); + mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); } void RaceDialog::onSelectNextFace(MyGUI::Widget*) { - faceIndex = wrap(faceIndex + 1, faceCount); + mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); } void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) { - hairIndex = wrap(hairIndex - 1, hairCount); + mHairIndex = wrap(mHairIndex - 1, mHairCount); } void RaceDialog::onSelectNextHair(MyGUI::Widget*) { - hairIndex = wrap(hairIndex - 1, hairCount); + mHairIndex = wrap(mHairIndex - 1, mHairCount); } void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) @@ -195,11 +195,11 @@ void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) if (_index == MyGUI::ITEM_NONE) return; - const std::string *raceId = raceList->getItemDataAt(_index); - if (boost::iequals(currentRaceId, *raceId)) + const std::string *raceId = mRaceList->getItemDataAt(_index); + if (boost::iequals(mCurrentRaceId, *raceId)) return; - currentRaceId = *raceId; + mCurrentRaceId = *raceId; updateSkills(); updateSpellPowers(); } @@ -208,7 +208,7 @@ void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) void RaceDialog::updateRaces() { - raceList->removeAllItems(); + mRaceList->removeAllItems(); const ESMS::ESMStore &store = mWindowManager.getStore(); @@ -222,30 +222,30 @@ void RaceDialog::updateRaces() if (!playable) // Only display playable races continue; - raceList->addItem(race.name, it->first); - if (boost::iequals(it->first, currentRaceId)) - raceList->setIndexSelected(index); + mRaceList->addItem(race.name, it->first); + if (boost::iequals(it->first, mCurrentRaceId)) + mRaceList->setIndexSelected(index); ++index; } } void RaceDialog::updateSkills() { - for (std::vector::iterator it = skillItems.begin(); it != skillItems.end(); ++it) + for (std::vector::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } - skillItems.clear(); + mSkillItems.clear(); - if (currentRaceId.empty()) + if (mCurrentRaceId.empty()) return; MWSkillPtr skillWidget; const int lineHeight = 18; - MyGUI::IntCoord coord1(0, 0, skillList->getWidth(), 18); + MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); const ESMS::ESMStore &store = mWindowManager.getStore(); - const ESM::Race *race = store.races.find(currentRaceId); + const ESM::Race *race = store.races.find(mCurrentRaceId); int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? for (int i = 0; i < count; ++i) { @@ -253,7 +253,7 @@ void RaceDialog::updateSkills() if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes continue; - skillWidget = skillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, + skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default, std::string("Skill") + boost::lexical_cast(i)); skillWidget->setWindowManager(&mWindowManager); skillWidget->setSkillNumber(skillId); @@ -261,7 +261,7 @@ void RaceDialog::updateSkills() ToolTips::createSkillToolTip(skillWidget, skillId); - skillItems.push_back(skillWidget); + mSkillItems.push_back(skillWidget); coord1.top += lineHeight; } @@ -269,34 +269,34 @@ void RaceDialog::updateSkills() void RaceDialog::updateSpellPowers() { - for (std::vector::iterator it = spellPowerItems.begin(); it != spellPowerItems.end(); ++it) + for (std::vector::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } - spellPowerItems.clear(); + mSpellPowerItems.clear(); - if (currentRaceId.empty()) + if (mCurrentRaceId.empty()) return; MWSpellPtr spellPowerWidget; const int lineHeight = 18; - MyGUI::IntCoord coord(0, 0, spellPowerList->getWidth(), 18); + MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); const ESMS::ESMStore &store = mWindowManager.getStore(); - const ESM::Race *race = store.races.find(currentRaceId); + const ESM::Race *race = store.races.find(mCurrentRaceId); std::vector::const_iterator it = race->powers.list.begin(); std::vector::const_iterator end = race->powers.list.end(); for (int i = 0; it != end; ++it) { const std::string &spellpower = *it; - spellPowerWidget = spellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); + spellPowerWidget = mSpellPowerList->createWidget("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast(i)); spellPowerWidget->setWindowManager(&mWindowManager); spellPowerWidget->setSpellId(spellpower); spellPowerWidget->setUserString("ToolTipType", "Spell"); spellPowerWidget->setUserString("Spell", spellpower); - spellPowerItems.push_back(spellPowerWidget); + mSpellPowerItems.push_back(spellPowerWidget); coord.top += lineHeight; ++i; diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 217ce15aa..b523b8690 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -32,13 +32,13 @@ namespace MWGui GM_Female }; - const std::string &getRaceId() const { return currentRaceId; } - Gender getGender() const { return genderIndex == 0 ? GM_Male : GM_Female; } + const std::string &getRaceId() const { return mCurrentRaceId; } + Gender getGender() const { return mGenderIndex == 0 ? GM_Male : GM_Female; } // getFace() // getHair() void setRaceId(const std::string &raceId); - void setGender(Gender gender) { genderIndex = gender == GM_Male ? 0 : 1; } + void setGender(Gender gender) { mGenderIndex = gender == GM_Male ? 0 : 1; } // setFace() // setHair() @@ -75,20 +75,20 @@ namespace MWGui void updateSkills(); void updateSpellPowers(); - MyGUI::CanvasPtr appearanceBox; - MyGUI::ListBox* raceList; - MyGUI::ScrollBar* headRotate; + MyGUI::CanvasPtr mAppearanceBox; + MyGUI::ListBox* mRaceList; + MyGUI::ScrollBar* mHeadRotate; - MyGUI::WidgetPtr skillList; - std::vector skillItems; + MyGUI::WidgetPtr mSkillList; + std::vector mSkillItems; - MyGUI::WidgetPtr spellPowerList; - std::vector spellPowerItems; + MyGUI::WidgetPtr mSpellPowerList; + std::vector mSpellPowerItems; - int genderIndex, faceIndex, hairIndex; - int faceCount, hairCount; + int mGenderIndex, mFaceIndex, mHairIndex; + int mFaceCount, mHairCount; - std::string currentRaceId; + std::string mCurrentRaceId; }; } #endif diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index f9792ea34..7061f65c1 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -17,49 +17,49 @@ using namespace MWGui; using namespace Widgets; -const int ReviewDialog::lineHeight = 18; +const int ReviewDialog::sLineHeight = 18; ReviewDialog::ReviewDialog(WindowManager& parWindowManager) : WindowBase("openmw_chargen_review.layout", parWindowManager) - , lastPos(0) + , mLastPos(0) { // Centre dialog center(); // Setup static stats ButtonPtr button; - getWidget(nameWidget, "NameText"); + getWidget(mNameWidget, "NameText"); getWidget(button, "NameButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);; - getWidget(raceWidget, "RaceText"); + getWidget(mRaceWidget, "RaceText"); getWidget(button, "RaceButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);; - getWidget(classWidget, "ClassText"); + getWidget(mClassWidget, "ClassText"); getWidget(button, "ClassButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);; - getWidget(birthSignWidget, "SignText"); + getWidget(mBirthSignWidget, "SignText"); getWidget(button, "SignButton"); adjustButtonSize(button); button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);; // Setup dynamic stats - getWidget(health, "Health"); - health->setTitle(mWindowManager.getGameSettingString("sHealth", "")); - health->setValue(45, 45); + getWidget(mHealth, "Health"); + mHealth->setTitle(mWindowManager.getGameSettingString("sHealth", "")); + mHealth->setValue(45, 45); - getWidget(magicka, "Magicka"); - magicka->setTitle(mWindowManager.getGameSettingString("sMagic", "")); - magicka->setValue(50, 50); + getWidget(mMagicka, "Magicka"); + mMagicka->setTitle(mWindowManager.getGameSettingString("sMagic", "")); + mMagicka->setValue(50, 50); - getWidget(fatigue, "Fatigue"); - fatigue->setTitle(mWindowManager.getGameSettingString("sFatigue", "")); - fatigue->setValue(160, 160); + getWidget(mFatigue, "Fatigue"); + mFatigue->setTitle(mWindowManager.getGameSettingString("sFatigue", "")); + mFatigue->setValue(160, 160); // Setup attributes @@ -67,24 +67,24 @@ ReviewDialog::ReviewDialog(WindowManager& parWindowManager) for (int idx = 0; idx < ESM::Attribute::Length; ++idx) { getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); - attributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::attributeIds[idx]), attribute)); + mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::attributeIds[idx]), attribute)); attribute->setWindowManager(&mWindowManager); attribute->setAttributeId(ESM::Attribute::attributeIds[idx]); attribute->setAttributeValue(MWAttribute::AttributeValue(0, 0)); } // Setup skills - getWidget(skillAreaWidget, "Skills"); - getWidget(skillClientWidget, "SkillClient"); - getWidget(skillScrollerWidget, "SkillScroller"); - skillClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - skillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &ReviewDialog::onScrollChangePosition); + getWidget(mSkillAreaWidget, "Skills"); + getWidget(mSkillClientWidget, "SkillClient"); + getWidget(mSkillScrollerWidget, "SkillScroller"); + mSkillClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); + mSkillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &ReviewDialog::onScrollChangePosition); updateScroller(); for (int i = 0; i < ESM::Skill::Length; ++i) { - skillValues.insert(std::make_pair(i, MWMechanics::Stat())); - skillWidgetMap.insert(std::make_pair(i, static_cast (0))); + mSkillValues.insert(std::make_pair(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); } static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ReviewDialog::onWindowResize); @@ -112,14 +112,14 @@ void ReviewDialog::open() void ReviewDialog::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) { - int diff = lastPos - pos; + int diff = mLastPos - pos; // Adjust position of all widget according to difference if (diff == 0) return; - lastPos = pos; + mLastPos = pos; - std::vector::const_iterator end = skillWidgets.end(); - for (std::vector::const_iterator it = skillWidgets.begin(); it != end; ++it) + std::vector::const_iterator end = mSkillWidgets.end(); + for (std::vector::const_iterator it = mSkillWidgets.begin(); it != end; ++it) { (*it)->setCoord((*it)->getCoord() + MyGUI::IntPoint(0, diff)); } @@ -132,63 +132,63 @@ void ReviewDialog::onWindowResize(MyGUI::Window* window) void ReviewDialog::setPlayerName(const std::string &name) { - nameWidget->setCaption(name); + mNameWidget->setCaption(name); } -void ReviewDialog::setRace(const std::string &raceId_) +void ReviewDialog::setRace(const std::string &raceId) { - raceId = raceId_; - const ESM::Race *race = mWindowManager.getStore().races.search(raceId); + mRaceId = raceId; + const ESM::Race *race = mWindowManager.getStore().races.search(mRaceId); if (race) { - ToolTips::createRaceToolTip(raceWidget, race); - raceWidget->setCaption(race->name); + ToolTips::createRaceToolTip(mRaceWidget, race); + mRaceWidget->setCaption(race->name); } } void ReviewDialog::setClass(const ESM::Class& class_) { - klass = class_; - classWidget->setCaption(klass.name); - ToolTips::createClassToolTip(classWidget, klass); + mKlass = class_; + mClassWidget->setCaption(mKlass.name); + ToolTips::createClassToolTip(mClassWidget, mKlass); } void ReviewDialog::setBirthSign(const std::string& signId) { - birthSignId = signId; - const ESM::BirthSign *sign = mWindowManager.getStore().birthSigns.search(birthSignId); + mBirthSignId = signId; + const ESM::BirthSign *sign = mWindowManager.getStore().birthSigns.search(mBirthSignId); if (sign) { - birthSignWidget->setCaption(sign->name); - ToolTips::createBirthsignToolTip(birthSignWidget, birthSignId); + mBirthSignWidget->setCaption(sign->name); + ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId); } } void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) { - health->setValue(value.getCurrent(), value.getModified()); + mHealth->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - health->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); + mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); } void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) { - magicka->setValue(value.getCurrent(), value.getModified()); + mMagicka->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - magicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); + mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); } void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) { - fatigue->setValue(value.getCurrent(), value.getModified()); + mFatigue->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); - fatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); + mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); } void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value) { - std::map::iterator attr = attributeWidgets.find(static_cast(attributeId)); - if (attr == attributeWidgets.end()) + std::map::iterator attr = mAttributeWidgets.find(static_cast(attributeId)); + if (attr == mAttributeWidgets.end()) return; attr->second->setAttributeValue(value); @@ -196,8 +196,8 @@ void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const M void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value) { - skillValues[skillId] = value; - MyGUI::TextBox* widget = skillWidgetMap[skillId]; + mSkillValues[skillId] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; if (widget) { float modified = value.getModified(), base = value.getBase(); @@ -216,20 +216,20 @@ void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanic void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) { - majorSkills = major; - minorSkills = minor; + mMajorSkills = major; + mMinorSkills = minor; // Update misc skills with the remaining skills not in major or minor std::set skillSet; std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); boost::array::const_iterator end = ESM::Skill::skillIds.end(); - miscSkills.clear(); + mMiscSkills.clear(); for (boost::array::const_iterator it = ESM::Skill::skillIds.begin(); it != end; ++it) { int skill = *it; if (skillSet.find(skill) == skillSet.end()) - miscSkills.push_back(skill); + mMiscSkills.push_back(skill); } updateSkillArea(); @@ -237,10 +237,10 @@ void ReviewDialog::configureSkills(const std::vector& major, const std::vec void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::ImageBox* separator = skillClientWidget->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); + MyGUI::ImageBox* separator = mSkillClientWidget->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - skillWidgets.push_back(separator); + mSkillWidgets.push_back(separator); coord1.top += separator->getHeight(); coord2.top += separator->getHeight(); @@ -248,13 +248,13 @@ void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2 void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::TextBox* groupWidget = skillClientWidget->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); + MyGUI::TextBox* groupWidget = mSkillClientWidget->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); groupWidget->setCaption(label); - skillWidgets.push_back(groupWidget); + mSkillWidgets.push_back(groupWidget); - coord1.top += lineHeight; - coord2.top += lineHeight; + coord1.top += sLineHeight; + coord2.top += sLineHeight; } MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) @@ -262,20 +262,20 @@ MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::s MyGUI::TextBox* skillNameWidget; MyGUI::TextBox* skillValueWidget; - skillNameWidget = skillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - skillValueWidget = skillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); + skillValueWidget = mSkillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); skillValueWidget->setCaption(value); skillValueWidget->_setWidgetState(state); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - skillWidgets.push_back(skillNameWidget); - skillWidgets.push_back(skillValueWidget); + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); - coord1.top += lineHeight; - coord2.top += lineHeight; + coord1.top += sLineHeight; + coord2.top += sLineHeight; return skillValueWidget; } @@ -284,20 +284,20 @@ void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyG { MyGUI::TextBox* skillNameWidget; - skillNameWidget = skillClientWidget->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - skillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillNameWidget); - coord1.top += lineHeight; - coord2.top += lineHeight; + coord1.top += sLineHeight; + coord2.top += sLineHeight; } void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { // Add a line separator if there are items above - if (!skillWidgets.empty()) + if (!mSkillWidgets.empty()) { addSeparator(coord1, coord2); } @@ -312,7 +312,7 @@ void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId continue; assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESMS::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = skillValues.find(skillId)->second; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; float base = stat.getBase(); float modified = stat.getModified(); @@ -325,44 +325,44 @@ void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId for (int i=0; i<2; ++i) { - ToolTips::createSkillToolTip(skillWidgets[skillWidgets.size()-1-i], skillId); + ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId); } - skillWidgetMap[skillId] = widget; + mSkillWidgetMap[skillId] = widget; } } void ReviewDialog::updateSkillArea() { - for (std::vector::iterator it = skillWidgets.begin(); it != skillWidgets.end(); ++it) + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } - skillWidgets.clear(); + mSkillWidgets.clear(); const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, skillClientWidget->getWidth() - (10 + valueSize), 18); + MyGUI::IntCoord coord1(10, 0, mSkillClientWidget->getWidth() - (10 + valueSize), 18); MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); - if (!majorSkills.empty()) - addSkills(majorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); - if (!minorSkills.empty()) - addSkills(minorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); - if (!miscSkills.empty()) - addSkills(miscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - clientHeight = coord1.top; + mClientHeight = coord1.top; updateScroller(); } void ReviewDialog::updateScroller() { - skillScrollerWidget->setScrollRange(std::max(clientHeight - skillClientWidget->getHeight(), 0)); - skillScrollerWidget->setScrollPage(std::max(skillClientWidget->getHeight() - lineHeight, 0)); - if (clientHeight != 0) - skillScrollerWidget->setTrackSize( (skillAreaWidget->getHeight() / float(clientHeight)) * skillScrollerWidget->getLineSize() ); + mSkillScrollerWidget->setScrollRange(std::max(mClientHeight - mSkillClientWidget->getHeight(), 0)); + mSkillScrollerWidget->setScrollPage(std::max(mSkillClientWidget->getHeight() - sLineHeight, 0)); + if (mClientHeight != 0) + mSkillScrollerWidget->setTrackSize( (mSkillAreaWidget->getHeight() / float(mClientHeight)) * mSkillScrollerWidget->getLineSize() ); } // widget controls @@ -399,12 +399,12 @@ void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - if (skillScrollerWidget->getScrollPosition() - _rel*0.3 < 0) - skillScrollerWidget->setScrollPosition(0); - else if (skillScrollerWidget->getScrollPosition() - _rel*0.3 > skillScrollerWidget->getScrollRange()-1) - skillScrollerWidget->setScrollPosition(skillScrollerWidget->getScrollRange()-1); + if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 < 0) + mSkillScrollerWidget->setScrollPosition(0); + else if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 > mSkillScrollerWidget->getScrollRange()-1) + mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollRange()-1); else - skillScrollerWidget->setScrollPosition(skillScrollerWidget->getScrollPosition() - _rel*0.3); + mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollPosition() - _rel*0.3); - onScrollChangePosition(skillScrollerWidget, skillScrollerWidget->getScrollPosition()); + onScrollChangePosition(mSkillScrollerWidget, mSkillScrollerWidget->getScrollPosition()); } diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 454a6c5a5..27b167033 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -82,23 +82,23 @@ namespace MWGui void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos); void onWindowResize(MyGUI::Window* window); - static const int lineHeight; + static const int sLineHeight; - MyGUI::TextBox *nameWidget, *raceWidget, *classWidget, *birthSignWidget; - MyGUI::WidgetPtr skillAreaWidget, skillClientWidget; - MyGUI::ScrollBar* skillScrollerWidget; - int lastPos, clientHeight; + MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget; + MyGUI::WidgetPtr mSkillAreaWidget, mSkillClientWidget; + MyGUI::ScrollBar* mSkillScrollerWidget; + int mLastPos, mClientHeight; - Widgets::MWDynamicStatPtr health, magicka, fatigue; + Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; - std::map attributeWidgets; + std::map mAttributeWidgets; - SkillList majorSkills, minorSkills, miscSkills; - std::map > skillValues; - std::map skillWidgetMap; - std::string name, raceId, birthSignId; - ESM::Class klass; - std::vector skillWidgets; //< Skills and other information + SkillList mMajorSkills, mMinorSkills, mMiscSkills; + std::map > mSkillValues; + std::map mSkillWidgetMap; + std::string mName, mRaceId, mBirthSignId; + ESM::Class mKlass; + std::vector mSkillWidgets; //< Skills and other information }; } #endif diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 0f8b41b28..e7a74a9e1 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -19,26 +19,26 @@ using namespace MWGui; -const int StatsWindow::lineHeight = 18; +const int StatsWindow::sLineHeight = 18; StatsWindow::StatsWindow (WindowManager& parWindowManager) : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) - , skillAreaWidget(NULL) - , skillClientWidget(NULL) - , skillScrollerWidget(NULL) - , lastPos(0) - , clientHeight(0) - , majorSkills() - , minorSkills() - , miscSkills() - , skillValues() - , skillWidgetMap() - , factionWidgetMap() + , mSkillAreaWidget(NULL) + , mSkillClientWidget(NULL) + , mSkillScrollerWidget(NULL) + , mLastPos(0) + , mClientHeight(0) + , mMajorSkills() + , mMinorSkills() + , mMiscSkills() + , mSkillValues() + , mSkillWidgetMap() + , mFactionWidgetMap() , mFactions() - , birthSignId() - , reputation(0) - , bounty(0) - , skillWidgets() + , mBirthSignId() + , mReputation(0) + , mBounty(0) + , mSkillWidgets() , mChanged(true) { setCoord(0,0,498, 342); @@ -62,21 +62,21 @@ StatsWindow::StatsWindow (WindowManager& parWindowManager) setText (names[i][0], store.gameSettings.find (names[i][1])->str); } - getWidget(skillAreaWidget, "Skills"); - getWidget(skillClientWidget, "SkillClient"); - getWidget(skillScrollerWidget, "SkillScroller"); + getWidget(mSkillAreaWidget, "Skills"); + getWidget(mSkillClientWidget, "SkillClient"); + getWidget(mSkillScrollerWidget, "SkillScroller"); getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); - skillClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); + mSkillClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - skillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &StatsWindow::onScrollChangePosition); + mSkillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &StatsWindow::onScrollChangePosition); updateScroller(); for (int i = 0; i < ESM::Skill::Length; ++i) { - skillValues.insert(std::pair >(i, MWMechanics::Stat())); - skillWidgetMap.insert(std::pair(i, nullptr)); + mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); + mSkillWidgetMap.insert(std::pair(i, nullptr)); } MyGUI::WindowPtr t = static_cast(mMainWidget); @@ -85,14 +85,14 @@ StatsWindow::StatsWindow (WindowManager& parWindowManager) void StatsWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) { - int diff = lastPos - pos; + int diff = mLastPos - pos; // Adjust position of all widget according to difference if (diff == 0) return; - lastPos = pos; + mLastPos = pos; - std::vector::const_iterator end = skillWidgets.end(); - for (std::vector::const_iterator it = skillWidgets.begin(); it != end; ++it) + std::vector::const_iterator end = mSkillWidgets.end(); + for (std::vector::const_iterator it = mSkillWidgets.begin(); it != end; ++it) { (*it)->setCoord((*it)->getCoord() + MyGUI::IntPoint(0, diff)); } @@ -100,14 +100,14 @@ void StatsWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - if (skillScrollerWidget->getScrollPosition() - _rel*0.3 < 0) - skillScrollerWidget->setScrollPosition(0); - else if (skillScrollerWidget->getScrollPosition() - _rel*0.3 > skillScrollerWidget->getScrollRange()-1) - skillScrollerWidget->setScrollPosition(skillScrollerWidget->getScrollRange()-1); + if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 < 0) + mSkillScrollerWidget->setScrollPosition(0); + else if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 > mSkillScrollerWidget->getScrollRange()-1) + mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollRange()-1); else - skillScrollerWidget->setScrollPosition(skillScrollerWidget->getScrollPosition() - _rel*0.3); + mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollPosition() - _rel*0.3); - onScrollChangePosition(skillScrollerWidget, skillScrollerWidget->getScrollPosition()); + onScrollChangePosition(mSkillScrollerWidget, mSkillScrollerWidget->getScrollPosition()); } void StatsWindow::onWindowResize(MyGUI::Window* window) @@ -224,8 +224,8 @@ void StatsWindow::setValue (const std::string& id, int value) void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value) { - skillValues[parSkill] = value; - MyGUI::TextBox* widget = skillWidgetMap[(int)parSkill]; + mSkillValues[parSkill] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[(int)parSkill]; if (widget) { float modified = value.getModified(), base = value.getBase(); @@ -243,20 +243,20 @@ void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechani void StatsWindow::configureSkills (const std::vector& major, const std::vector& minor) { - majorSkills = major; - minorSkills = minor; + mMajorSkills = major; + mMinorSkills = minor; // Update misc skills with the remaining skills not in major or minor std::set skillSet; std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); boost::array::const_iterator end = ESM::Skill::skillIds.end(); - miscSkills.clear(); + mMiscSkills.clear(); for (boost::array::const_iterator it = ESM::Skill::skillIds.begin(); it != end; ++it) { int skill = *it; if (skillSet.find(skill) == skillSet.end()) - miscSkills.push_back(skill); + mMiscSkills.push_back(skill); } updateSkillArea(); @@ -289,20 +289,20 @@ void StatsWindow::setFactions (const FactionList& factions) void StatsWindow::setBirthSign (const std::string& signId) { - if (signId != birthSignId) + if (signId != mBirthSignId) { - birthSignId = signId; + mBirthSignId = signId; mChanged = true; } } void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::ImageBox* separator = skillClientWidget->createWidget("MW_HLine", + MyGUI::ImageBox* separator = mSkillClientWidget->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - skillWidgets.push_back(separator); + mSkillWidgets.push_back(separator); coord1.top += separator->getHeight(); coord2.top += separator->getHeight(); @@ -310,35 +310,35 @@ void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::TextBox* groupWidget = skillClientWidget->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget = mSkillClientWidget->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); groupWidget->setCaption(label); groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - skillWidgets.push_back(groupWidget); + mSkillWidgets.push_back(groupWidget); - coord1.top += lineHeight; - coord2.top += lineHeight; + coord1.top += sLineHeight; + coord2.top += sLineHeight; } MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { MyGUI::TextBox *skillNameWidget, *skillValueWidget; - skillNameWidget = skillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - skillValueWidget = skillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); + skillValueWidget = mSkillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); skillValueWidget->setCaption(value); skillValueWidget->_setWidgetState(state); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - skillWidgets.push_back(skillNameWidget); - skillWidgets.push_back(skillValueWidget); + mSkillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillValueWidget); - coord1.top += lineHeight; - coord2.top += lineHeight; + coord1.top += sLineHeight; + coord2.top += sLineHeight; return skillValueWidget; } @@ -347,14 +347,14 @@ MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &co { MyGUI::TextBox* skillNameWidget; - skillNameWidget = skillClientWidget->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - skillWidgets.push_back(skillNameWidget); + mSkillWidgets.push_back(skillNameWidget); - coord1.top += lineHeight; - coord2.top += lineHeight; + coord1.top += sLineHeight; + coord2.top += sLineHeight; return skillNameWidget; } @@ -362,7 +362,7 @@ MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &co void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { // Add a line separator if there are items above - if (!skillWidgets.empty()) + if (!mSkillWidgets.empty()) { addSeparator(coord1, coord2); } @@ -377,7 +377,7 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, continue; assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESMS::Skill::sSkillNameIds[skillId]; - const MWMechanics::Stat &stat = skillValues.find(skillId)->second; + const MWMechanics::Stat &stat = mSkillValues.find(skillId)->second; float base = stat.getBase(); float modified = stat.getModified(); int progressPercent = (modified - float(static_cast(modified))) * 100; @@ -400,18 +400,18 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, for (int i=0; i<2; ++i) { - skillWidgets[skillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->description); - skillWidgets[skillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->name + "}"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); - skillWidgets[skillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "SkillToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillName", "#{"+skillNameId+"}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillDescription", skill->description); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillAttribute", "#{sGoverningAttribute}: #{" + attr->name + "}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ImageTexture_SkillImage", icon); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_SkillProgressText", boost::lexical_cast(progressPercent)+"/100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Range_SkillProgress", "100"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("RangePosition_SkillProgress", boost::lexical_cast(progressPercent)); } - skillWidgetMap[skillId] = widget; + mSkillWidgetMap[skillId] = widget; } } @@ -419,28 +419,28 @@ void StatsWindow::updateSkillArea() { mChanged = false; - for (std::vector::iterator it = skillWidgets.begin(); it != skillWidgets.end(); ++it) + for (std::vector::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it) { MyGUI::Gui::getInstance().destroyWidget(*it); } - skillWidgets.clear(); + mSkillWidgets.clear(); - skillScrollerWidget->setScrollPosition(0); - onScrollChangePosition(skillScrollerWidget, 0); - clientHeight = 0; + mSkillScrollerWidget->setScrollPosition(0); + onScrollChangePosition(mSkillScrollerWidget, 0); + mClientHeight = 0; const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, skillClientWidget->getWidth() - (10 + valueSize), 18); + MyGUI::IntCoord coord1(10, 0, mSkillClientWidget->getWidth() - (10 + valueSize), 18); MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); - if (!majorSkills.empty()) - addSkills(majorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); + if (!mMajorSkills.empty()) + addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2); - if (!minorSkills.empty()) - addSkills(minorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); + if (!mMinorSkills.empty()) + addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2); - if (!miscSkills.empty()) - addSkills(miscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); + if (!mMiscSkills.empty()) + addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); const ESMS::ESMStore &store = mWindowManager.getStore(); @@ -463,7 +463,7 @@ void StatsWindow::updateSkillArea() if (!mFactions.empty()) { // Add a line separator if there are items above - if (!skillWidgets.empty()) + if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); addGroup(mWindowManager.getGameSettingString("sFaction", "Faction"), coord1, coord2); @@ -516,53 +516,53 @@ void StatsWindow::updateSkillArea() } } - if (!birthSignId.empty()) + if (!mBirthSignId.empty()) { // Add a line separator if there are items above - if (!skillWidgets.empty()) + if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); addGroup(mWindowManager.getGameSettingString("sBirthSign", "Sign"), coord1, coord2); - const ESM::BirthSign *sign = store.birthSigns.find(birthSignId); + const ESM::BirthSign *sign = store.birthSigns.find(mBirthSignId); MyGUI::Widget* w = addItem(sign->name, coord1, coord2); - ToolTips::createBirthsignToolTip(w, birthSignId); + ToolTips::createBirthsignToolTip(w, mBirthSignId); } // Add a line separator if there are items above - if (!skillWidgets.empty()) + if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); addValueItem(mWindowManager.getGameSettingString("sReputation", "Reputation"), - boost::lexical_cast(static_cast(reputation)), "normal", coord1, coord2); + boost::lexical_cast(static_cast(mReputation)), "normal", coord1, coord2); for (int i=0; i<2; ++i) { - skillWidgets[skillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sSkillsMenuReputationHelp}"); } addValueItem(mWindowManager.getGameSettingString("sBounty", "Bounty"), - boost::lexical_cast(static_cast(bounty)), "normal", coord1, coord2); + boost::lexical_cast(static_cast(mBounty)), "normal", coord1, coord2); for (int i=0; i<2; ++i) { - skillWidgets[skillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); - skillWidgets[skillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipType", "Layout"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("ToolTipLayout", "TextToolTip"); + mSkillWidgets[mSkillWidgets.size()-1-i]->setUserString("Caption_Text", "#{sCrimeHelp}"); } - clientHeight = coord1.top; + mClientHeight = coord1.top; updateScroller(); } void StatsWindow::updateScroller() { - skillScrollerWidget->setScrollRange(std::max(clientHeight - skillClientWidget->getHeight(), 0)); - skillScrollerWidget->setScrollPage(std::max(skillClientWidget->getHeight() - lineHeight, 0)); - if (clientHeight != 0) - skillScrollerWidget->setTrackSize( (skillAreaWidget->getHeight() / float(clientHeight)) * skillScrollerWidget->getLineSize() ); + mSkillScrollerWidget->setScrollRange(std::max(mClientHeight - mSkillClientWidget->getHeight(), 0)); + mSkillScrollerWidget->setScrollPage(std::max(mSkillClientWidget->getHeight() - sLineHeight, 0)); + if (mClientHeight != 0) + mSkillScrollerWidget->setTrackSize( (mSkillAreaWidget->getHeight() / float(mClientHeight)) * mSkillScrollerWidget->getLineSize() ); } void StatsWindow::onPinToggled() diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index 10b794cc0..2469c12e9 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -38,8 +38,8 @@ namespace MWGui void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); void configureSkills (const SkillList& major, const SkillList& minor); - void setReputation (int reputation) { this->reputation = reputation; } - void setBounty (int bounty) { this->bounty = bounty; } + void setReputation (int reputation) { this->mReputation = reputation; } + void setBounty (int bounty) { this->mBounty = bounty; } void updateSkillArea(); private: @@ -57,23 +57,23 @@ namespace MWGui void onWindowResize(MyGUI::Window* window); void onMouseWheel(MyGUI::Widget* _sender, int _rel); - static const int lineHeight; + static const int sLineHeight; MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; - MyGUI::WidgetPtr skillAreaWidget, skillClientWidget; - MyGUI::ScrollBar* skillScrollerWidget; - int lastPos, clientHeight; + MyGUI::WidgetPtr mSkillAreaWidget, mSkillClientWidget; + MyGUI::ScrollBar* mSkillScrollerWidget; + int mLastPos, mClientHeight; - SkillList majorSkills, minorSkills, miscSkills; - std::map > skillValues; - std::map skillWidgetMap; - std::map factionWidgetMap; + SkillList mMajorSkills, mMinorSkills, mMiscSkills; + std::map > mSkillValues; + std::map mSkillWidgetMap; + std::map mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank - std::string birthSignId; - int reputation, bounty; - std::vector skillWidgets; //< Skills and other information + std::string mBirthSignId; + int mReputation, mBounty; + std::vector mSkillWidgets; //< Skills and other information bool mChanged; diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 17852ba5a..7d5b0cc6d 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -9,15 +9,15 @@ TextInputDialog::TextInputDialog(WindowManager& parWindowManager) // Centre dialog center(); - getWidget(textEdit, "TextEdit"); - textEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); + getWidget(mTextEdit, "TextEdit"); + mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted); MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked); // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(textEdit); + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); } void TextInputDialog::setNextButtonShow(bool shown) @@ -43,7 +43,7 @@ void TextInputDialog::setTextLabel(const std::string &label) void TextInputDialog::open() { // Make sure the edit box has focus - MyGUI::InputManager::getInstance().setKeyFocusWidget(textEdit); + MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); setVisible(true); } diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index fe355655f..835d5deaa 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -20,8 +20,8 @@ namespace MWGui public: TextInputDialog(WindowManager& parWindowManager); - std::string getTextInput() const { return textEdit ? textEdit->getOnlyText() : ""; } - void setTextInput(const std::string &text) { if (textEdit) textEdit->setOnlyText(text); } + std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } + void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); @@ -32,7 +32,7 @@ namespace MWGui void onTextAccepted(MyGUI::Edit* _sender); private: - MyGUI::EditPtr textEdit; + MyGUI::EditPtr mTextEdit; }; } #endif diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 65de7ce0a..c54ac6e38 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -27,16 +27,16 @@ void MWGui::Widgets::fixTexturePath(std::string &path) /* MWSkill */ MWSkill::MWSkill() - : manager(nullptr) - , skillId(ESM::Skill::Length) - , skillNameWidget(nullptr) - , skillValueWidget(nullptr) + : mManager(nullptr) + , mSkillId(ESM::Skill::Length) + , mSkillNameWidget(nullptr) + , mSkillValueWidget(nullptr) { } void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) { - skillId = skill; + mSkillId = skill; updateWidgets(); } @@ -50,36 +50,36 @@ void MWSkill::setSkillNumber(int skill) throw new std::runtime_error("Skill number out of range"); } -void MWSkill::setSkillValue(const SkillValue& value_) +void MWSkill::setSkillValue(const SkillValue& value) { - value = value_; + mValue = value; updateWidgets(); } void MWSkill::updateWidgets() { - if (skillNameWidget && manager) + if (mSkillNameWidget && mManager) { - if (skillId == ESM::Skill::Length) + if (mSkillId == ESM::Skill::Length) { - static_cast(skillNameWidget)->setCaption(""); + static_cast(mSkillNameWidget)->setCaption(""); } else { - const std::string &name = manager->getGameSettingString(ESM::Skill::sSkillNameIds[skillId], ""); - static_cast(skillNameWidget)->setCaption(name); + const std::string &name = mManager->getGameSettingString(ESM::Skill::sSkillNameIds[mSkillId], ""); + static_cast(mSkillNameWidget)->setCaption(name); } } - if (skillValueWidget) + if (mSkillValueWidget) { - SkillValue::Type modified = value.getModified(), base = value.getBase(); - static_cast(skillValueWidget)->setCaption(boost::lexical_cast(modified)); + SkillValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mSkillValueWidget)->setCaption(boost::lexical_cast(modified)); if (modified > base) - skillValueWidget->_setWidgetState("increased"); + mSkillValueWidget->_setWidgetState("increased"); else if (modified < base) - skillValueWidget->_setWidgetState("decreased"); + mSkillValueWidget->_setWidgetState("decreased"); else - skillValueWidget->_setWidgetState("normal"); + mSkillValueWidget->_setWidgetState("normal"); } } @@ -96,14 +96,14 @@ void MWSkill::initialiseOverride() { Base::initialiseOverride(); - assignWidget(skillNameWidget, "StatName"); - assignWidget(skillValueWidget, "StatValue"); + assignWidget(mSkillNameWidget, "StatName"); + assignWidget(mSkillValueWidget, "StatValue"); MyGUI::ButtonPtr button; assignWidget(button, "StatNameButton"); if (button) { - skillNameWidget = button; + mSkillNameWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); } @@ -111,7 +111,7 @@ void MWSkill::initialiseOverride() assignWidget(button, "StatValueButton"); if (button) { - skillNameWidget = button; + mSkillNameWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked); } } @@ -119,22 +119,22 @@ void MWSkill::initialiseOverride() /* MWAttribute */ MWAttribute::MWAttribute() - : manager(nullptr) - , id(-1) - , attributeNameWidget(nullptr) - , attributeValueWidget(nullptr) + : mManager(nullptr) + , mId(-1) + , mAttributeNameWidget(nullptr) + , mAttributeValueWidget(nullptr) { } void MWAttribute::setAttributeId(int attributeId) { - id = attributeId; + mId = attributeId; updateWidgets(); } -void MWAttribute::setAttributeValue(const AttributeValue& value_) +void MWAttribute::setAttributeValue(const AttributeValue& value) { - value = value_; + mValue = value; updateWidgets(); } @@ -145,11 +145,11 @@ void MWAttribute::onClicked(MyGUI::Widget* _sender) void MWAttribute::updateWidgets() { - if (attributeNameWidget && manager) + if (mAttributeNameWidget && mManager) { - if (id < 0 || id >= 8) + if (mId < 0 || mId >= 8) { - static_cast(attributeNameWidget)->setCaption(""); + static_cast(mAttributeNameWidget)->setCaption(""); } else { @@ -163,20 +163,20 @@ void MWAttribute::updateWidgets() "sAttributePersonality", "sAttributeLuck" }; - const std::string &name = manager->getGameSettingString(attributes[id], ""); - static_cast(attributeNameWidget)->setCaption(name); + const std::string &name = mManager->getGameSettingString(attributes[mId], ""); + static_cast(mAttributeNameWidget)->setCaption(name); } } - if (attributeValueWidget) + if (mAttributeValueWidget) { - AttributeValue::Type modified = value.getModified(), base = value.getBase(); - static_cast(attributeValueWidget)->setCaption(boost::lexical_cast(modified)); + AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); + static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); if (modified > base) - attributeValueWidget->_setWidgetState("increased"); + mAttributeValueWidget->_setWidgetState("increased"); else if (modified < base) - attributeValueWidget->_setWidgetState("decreased"); + mAttributeValueWidget->_setWidgetState("decreased"); else - attributeValueWidget->_setWidgetState("normal"); + mAttributeValueWidget->_setWidgetState("normal"); } } @@ -188,14 +188,14 @@ void MWAttribute::initialiseOverride() { Base::initialiseOverride(); - assignWidget(attributeNameWidget, "StatName"); - assignWidget(attributeValueWidget, "StatValue"); + assignWidget(mAttributeNameWidget, "StatName"); + assignWidget(mAttributeValueWidget, "StatValue"); MyGUI::ButtonPtr button; assignWidget(button, "StatNameButton"); if (button) { - attributeNameWidget = button; + mAttributeNameWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); } @@ -203,7 +203,7 @@ void MWAttribute::initialiseOverride() assignWidget(button, "StatValueButton"); if (button) { - attributeValueWidget = button; + mAttributeValueWidget = button; button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked); } } @@ -212,21 +212,21 @@ void MWAttribute::initialiseOverride() MWSpell::MWSpell() : mWindowManager(nullptr) - , spellNameWidget(nullptr) + , mSpellNameWidget(nullptr) { } void MWSpell::setSpellId(const std::string &spellId) { - id = spellId; + mId = spellId; updateWidgets(); } void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, int flags) { const ESMS::ESMStore &store = mWindowManager->getStore(); - const ESM::Spell *spell = store.spells.search(id); - MYGUI_ASSERT(spell, "spell with id '" << id << "' not found"); + const ESM::Spell *spell = store.spells.search(mId); + MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); MWSpellEffectPtr effect = nullptr; std::vector::const_iterator end = spell->effects.list.end(); @@ -253,14 +253,14 @@ void MWSpell::createEffectWidgets(std::vector &effects, MyGUI: void MWSpell::updateWidgets() { - if (spellNameWidget && mWindowManager) + if (mSpellNameWidget && mWindowManager) { const ESMS::ESMStore &store = mWindowManager->getStore(); - const ESM::Spell *spell = store.spells.search(id); + const ESM::Spell *spell = store.spells.search(mId); if (spell) - static_cast(spellNameWidget)->setCaption(spell->name); + static_cast(mSpellNameWidget)->setCaption(spell->name); else - static_cast(spellNameWidget)->setCaption(""); + static_cast(mSpellNameWidget)->setCaption(""); } } @@ -268,7 +268,7 @@ void MWSpell::initialiseOverride() { Base::initialiseOverride(); - assignWidget(spellNameWidget, "StatName"); + assignWidget(mSpellNameWidget, "StatName"); } MWSpell::~MWSpell() @@ -367,8 +367,8 @@ SpellEffectList MWEffectList::effectListFromESM(const ESM::EffectList* effects) MWSpellEffect::MWSpellEffect() : mWindowManager(nullptr) - , imageWidget(nullptr) - , textWidget(nullptr) + , mImageWidget(nullptr) + , mTextWidget(nullptr) , mRequestedWidth(0) { } @@ -388,7 +388,7 @@ void MWSpellEffect::updateWidgets() const ESM::MagicEffect *magicEffect = store.magicEffects.search(mEffectParams.mEffectID); if (!magicEffect) return; - if (textWidget) + if (mTextWidget) { std::string pt = mWindowManager->getGameSettingString("spoint", ""); std::string pts = mWindowManager->getGameSettingString("spoints", ""); @@ -448,14 +448,14 @@ void MWSpellEffect::updateWidgets() } } - static_cast(textWidget)->setCaption(spellLine); - mRequestedWidth = textWidget->getTextSize().width + 24; + static_cast(mTextWidget)->setCaption(spellLine); + mRequestedWidth = mTextWidget->getTextSize().width + 24; } - if (imageWidget) + if (mImageWidget) { std::string path = std::string("icons\\") + magicEffect->icon; fixTexturePath(path); - imageWidget->setImageTexture(path); + mImageWidget->setImageTexture(path); } } @@ -728,49 +728,49 @@ void MWSpellEffect::initialiseOverride() { Base::initialiseOverride(); - assignWidget(textWidget, "Text"); - assignWidget(imageWidget, "Image"); + assignWidget(mTextWidget, "Text"); + assignWidget(mImageWidget, "Image"); } /* MWDynamicStat */ MWDynamicStat::MWDynamicStat() -: value(0) -, max(1) -, textWidget(nullptr) -, barWidget(nullptr) -, barTextWidget(nullptr) +: mValue(0) +, mMax(1) +, mTextWidget(nullptr) +, mBarWidget(nullptr) +, mBarTextWidget(nullptr) { } -void MWDynamicStat::setValue(int cur, int max_) +void MWDynamicStat::setValue(int cur, int max) { - value = cur; - max = max_; + mValue = cur; + mMax = max; - if (barWidget) + if (mBarWidget) { - barWidget->setProgressRange(max); - barWidget->setProgressPosition(value); + mBarWidget->setProgressRange(mMax); + mBarWidget->setProgressPosition(mValue); } - if (barTextWidget) + if (mBarTextWidget) { - if (value >= 0 && max > 0) + if (mValue >= 0 && mMax > 0) { std::stringstream out; - out << value << "/" << max; - static_cast(barTextWidget)->setCaption(out.str().c_str()); + out << mValue << "/" << mMax; + static_cast(mBarTextWidget)->setCaption(out.str().c_str()); } else - static_cast(barTextWidget)->setCaption(""); + static_cast(mBarTextWidget)->setCaption(""); } } void MWDynamicStat::setTitle(const std::string& text) { - if (textWidget) - static_cast(textWidget)->setCaption(text); + if (mTextWidget) + static_cast(mTextWidget)->setCaption(text); } MWDynamicStat::~MWDynamicStat() @@ -781,7 +781,7 @@ void MWDynamicStat::initialiseOverride() { Base::initialiseOverride(); - assignWidget(textWidget, "Text"); - assignWidget(barWidget, "Bar"); - assignWidget(barTextWidget, "BarText"); + assignWidget(mTextWidget, "Text"); + assignWidget(mBarWidget, "Bar"); + assignWidget(mBarTextWidget, "BarText"); } diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 5d00baf87..d4947895b 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -73,7 +73,7 @@ namespace MWGui typedef std::vector SpellEffectList; - class MYGUI_EXPORT MWSkill : public Widget + class MYGUI_EXPORT MWSkill : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSkill ); public: @@ -81,14 +81,14 @@ namespace MWGui typedef MWMechanics::Stat SkillValue; - void setWindowManager(WindowManager *m) { manager = m; } + void setWindowManager(WindowManager *m) { mManager = m; } void setSkillId(ESM::Skill::SkillEnum skillId); void setSkillNumber(int skillId); void setSkillValue(const SkillValue& value); - WindowManager *getWindowManager() const { return manager; } - ESM::Skill::SkillEnum getSkillId() const { return skillId; } - const SkillValue& getSkillValue() const { return value; } + WindowManager *getWindowManager() const { return mManager; } + ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } + const SkillValue& getSkillValue() const { return mValue; } // Events typedef delegates::CMultiDelegate1 EventHandle_SkillVoid; @@ -109,14 +109,14 @@ namespace MWGui void updateWidgets(); - WindowManager *manager; - ESM::Skill::SkillEnum skillId; - SkillValue value; - MyGUI::WidgetPtr skillNameWidget, skillValueWidget; + WindowManager *mManager; + ESM::Skill::SkillEnum mSkillId; + SkillValue mValue; + MyGUI::WidgetPtr mSkillNameWidget, mSkillValueWidget; }; typedef MWSkill* MWSkillPtr; - class MYGUI_EXPORT MWAttribute : public Widget + class MYGUI_EXPORT MWAttribute : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWAttribute ); public: @@ -124,13 +124,13 @@ namespace MWGui typedef MWMechanics::Stat AttributeValue; - void setWindowManager(WindowManager *m) { manager = m; } + void setWindowManager(WindowManager *m) { mManager = m; } void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); - WindowManager *getWindowManager() const { return manager; } - int getAttributeId() const { return id; } - const AttributeValue& getAttributeValue() const { return value; } + WindowManager *getWindowManager() const { return mManager; } + int getAttributeId() const { return mId; } + const AttributeValue& getAttributeValue() const { return mValue; } // Events typedef delegates::CMultiDelegate1 EventHandle_AttributeVoid; @@ -151,10 +151,10 @@ namespace MWGui void updateWidgets(); - WindowManager *manager; - int id; - AttributeValue value; - MyGUI::WidgetPtr attributeNameWidget, attributeValueWidget; + WindowManager *mManager; + int mId; + AttributeValue mValue; + MyGUI::WidgetPtr mAttributeNameWidget, mAttributeValueWidget; }; typedef MWAttribute* MWAttributePtr; @@ -162,7 +162,7 @@ namespace MWGui * @todo remove this class and use MWEffectList instead */ class MWSpellEffect; - class MYGUI_EXPORT MWSpell : public Widget + class MYGUI_EXPORT MWSpell : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSpell ); public: @@ -182,7 +182,7 @@ namespace MWGui */ void createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, int flags); - const std::string &getSpellId() const { return id; } + const std::string &getSpellId() const { return mId; } protected: virtual ~MWSpell(); @@ -193,12 +193,12 @@ namespace MWGui void updateWidgets(); WindowManager* mWindowManager; - std::string id; - MyGUI::TextBox* spellNameWidget; + std::string mId; + MyGUI::TextBox* mSpellNameWidget; }; typedef MWSpell* MWSpellPtr; - class MYGUI_EXPORT MWEffectList : public Widget + class MYGUI_EXPORT MWEffectList : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWEffectList ); public: @@ -239,7 +239,7 @@ namespace MWGui }; typedef MWEffectList* MWEffectListPtr; - class MYGUI_EXPORT MWSpellEffect : public Widget + class MYGUI_EXPORT MWSpellEffect : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWSpellEffect ); public: @@ -269,13 +269,13 @@ namespace MWGui WindowManager* mWindowManager; SpellEffectParams mEffectParams; - MyGUI::ImageBox* imageWidget; - MyGUI::TextBox* textWidget; + MyGUI::ImageBox* mImageWidget; + MyGUI::TextBox* mTextWidget; int mRequestedWidth; }; typedef MWSpellEffect* MWSpellEffectPtr; - class MYGUI_EXPORT MWDynamicStat : public Widget + class MYGUI_EXPORT MWDynamicStat : public MyGUI::Widget { MYGUI_RTTI_DERIVED( MWDynamicStat ); public: @@ -284,8 +284,8 @@ namespace MWGui void setValue(int value, int max); void setTitle(const std::string& text); - int getValue() const { return value; } - int getMax() const { return max; } + int getValue() const { return mValue; } + int getMax() const { return mMax; } protected: virtual ~MWDynamicStat(); @@ -294,10 +294,10 @@ namespace MWGui private: - int value, max; - MyGUI::TextBox* textWidget; - MyGUI::ProgressPtr barWidget; - MyGUI::TextBox* barTextWidget; + int mValue, mMax; + MyGUI::TextBox* mTextWidget; + MyGUI::ProgressPtr mBarWidget; + MyGUI::TextBox* mBarTextWidget; }; typedef MWDynamicStat* MWDynamicStatPtr; } diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 62207d7d7..aa0595b85 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -43,13 +43,13 @@ using namespace MWGui; WindowManager::WindowManager( const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath) : mGuiManager(NULL) - , hud(NULL) - , map(NULL) - , menu(NULL) + , mHud(NULL) + , mMap(NULL) + , mMenu(NULL) , mStatsWindow(NULL) , mToolTips(NULL) , mMessageBoxManager(NULL) - , console(NULL) + , mConsole(NULL) , mJournal(NULL) , mDialogueWindow(NULL) , mBookWindow(NULL) @@ -61,21 +61,21 @@ WindowManager::WindowManager( , mAlchemyWindow(NULL) , mSpellWindow(NULL) , mCharGen(NULL) - , playerClass() - , playerName() - , playerRaceId() - , playerAttributes() - , playerMajorSkills() - , playerMinorSkills() - , playerSkillValues() - , playerHealth() - , playerMagicka() - , playerFatigue() - , gui(NULL) - , garbageDialogs() - , shown(GW_ALL) - , allowed(newGame ? GW_None : GW_ALL) - , showFPSLevel(fpsLevel) + , mPlayerClass() + , mPlayerName() + , mPlayerRaceId() + , mPlayerAttributes() + , mPlayerMajorSkills() + , mPlayerMinorSkills() + , mPlayerSkillValues() + , mPlayerHealth() + , mPlayerMagicka() + , mPlayerFatigue() + , mGui(NULL) + , mGarbageDialogs() + , mShown(GW_ALL) + , mAllowed(newGame ? GW_None : GW_ALL) + , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) , mBatchCount(0) @@ -83,7 +83,7 @@ WindowManager::WindowManager( // Set up the GUI system mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath); - gui = mGuiManager->getGui(); + mGui = mGuiManager->getGui(); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -98,11 +98,11 @@ WindowManager::WindowManager( MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Get size info from the Gui object - assert(gui); + assert(mGui); int w = MyGUI::RenderManager::getInstance().getViewSize().width; int h = MyGUI::RenderManager::getInstance().getViewSize().height; - MyGUI::Widget* dragAndDropWidget = gui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); + MyGUI::Widget* dragAndDropWidget = mGui->createWidgetT("Widget","",0,0,w,h,MyGUI::Align::Default,"DragAndDrop","DragAndDropWidget"); dragAndDropWidget->setVisible(false); mDragAndDrop = new DragAndDrop(); @@ -110,17 +110,17 @@ WindowManager::WindowManager( mDragAndDrop->mDraggedWidget = 0; mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - menu = new MainMenu(w,h); - map = new MapWindow(*this); + mMenu = new MainMenu(w,h); + mMap = new MapWindow(*this); mStatsWindow = new StatsWindow(*this); - console = new Console(w,h, extensions); + mConsole = new Console(w,h, extensions); mJournal = new JournalWindow(*this); mMessageBoxManager = new MessageBoxManager(this); mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); mTradeWindow = new TradeWindow(*this); mDialogueWindow = new DialogueWindow(*this); mContainerWindow = new ContainerWindow(*this,mDragAndDrop); - hud = new HUD(w,h, showFPSLevel, mDragAndDrop); + mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); mToolTips = new ToolTips(this); mScrollWindow = new ScrollWindow(*this); mBookWindow = new BookWindow(*this); @@ -131,19 +131,19 @@ WindowManager::WindowManager( mSpellWindow = new SpellWindow(*this); // The HUD is always on - hud->setVisible(true); + mHud->setVisible(true); mCharGen = new CharacterCreation(this); // Setup player stats for (int i = 0; i < ESM::Attribute::Length; ++i) { - playerAttributes.insert(std::make_pair(ESM::Attribute::attributeIds[i], MWMechanics::Stat())); + mPlayerAttributes.insert(std::make_pair(ESM::Attribute::attributeIds[i], MWMechanics::Stat())); } for (int i = 0; i < ESM::Skill::Length; ++i) { - playerSkillValues.insert(std::make_pair(ESM::Skill::skillIds[i], MWMechanics::Stat())); + mPlayerSkillValues.insert(std::make_pair(ESM::Skill::skillIds[i], MWMechanics::Stat())); } unsetSelectedSpell(); @@ -156,11 +156,11 @@ WindowManager::WindowManager( WindowManager::~WindowManager() { delete mGuiManager; - delete console; + delete mConsole; delete mMessageBoxManager; - delete hud; - delete map; - delete menu; + delete mHud; + delete mMap; + delete mMenu; delete mStatsWindow; delete mJournal; delete mDialogueWindow; @@ -183,13 +183,13 @@ WindowManager::~WindowManager() void WindowManager::cleanupGarbage() { // Delete any dialogs which are no longer in use - if (!garbageDialogs.empty()) + if (!mGarbageDialogs.empty()) { - for (std::vector::iterator it = garbageDialogs.begin(); it != garbageDialogs.end(); ++it) + for (std::vector::iterator it = mGarbageDialogs.begin(); it != mGarbageDialogs.end(); ++it) { delete *it; } - garbageDialogs.clear(); + mGarbageDialogs.clear(); } } @@ -197,18 +197,18 @@ void WindowManager::update() { cleanupGarbage(); - hud->setFPS(mFPS); - hud->setTriangleCount(mTriangleCount); - hud->setBatchCount(mBatchCount); + mHud->setFPS(mFPS); + mHud->setTriangleCount(mTriangleCount); + mHud->setBatchCount(mBatchCount); } void WindowManager::updateVisible() { // Start out by hiding everything except the HUD - map->setVisible(false); - menu->setVisible(false); + mMap->setVisible(false); + mMenu->setVisible(false); mStatsWindow->setVisible(false); - console->disable(); + mConsole->disable(); mJournal->setVisible(false); mDialogueWindow->setVisible(false); mContainerWindow->setVisible(false); @@ -230,10 +230,10 @@ void WindowManager::updateVisible() else mToolTips->enterGuiMode(); - setMinimapVisibility((allowed & GW_Map) && !map->pinned()); - setWeaponVisibility((allowed & GW_Inventory) && !mInventoryWindow->pinned()); - setSpellVisibility((allowed & GW_Magic) && !mSpellWindow->pinned()); - setHMSVisibility((allowed & GW_Stats) && !mStatsWindow->pinned()); + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); + setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); + setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); + setHMSVisibility((mAllowed & GW_Stats) && !mStatsWindow->pinned()); // If in game mode, don't show anything. if (gameMode) @@ -243,13 +243,13 @@ void WindowManager::updateVisible() switch(mode) { case GM_MainMenu: - menu->setVisible(true); + mMenu->setVisible(true); break; case GM_Settings: mSettingsWindow->setVisible(true); break; case GM_Console: - console->enable(); + mConsole->enable(); break; case GM_Scroll: mScrollWindow->setVisible(true); @@ -276,13 +276,13 @@ void WindowManager::updateVisible() // 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; + int eff = mShown & mAllowed; // Show the windows we want - map -> setVisible(eff & GW_Map); - mStatsWindow -> setVisible(eff & GW_Stats); + mMap ->setVisible(eff & GW_Map); + mStatsWindow ->setVisible(eff & GW_Stats); mInventoryWindow->setVisible(eff & GW_Inventory); - mSpellWindow->setVisible(eff & GW_Magic); + mSpellWindow ->setVisible(eff & GW_Magic); break; } case GM_Container: @@ -333,7 +333,7 @@ void WindowManager::setValue (const std::string& id, const MWMechanics::StatsetValue(parSkill, value); mCharGen->setValue(parSkill, value); - playerSkillValues[parSkill] = value; + mPlayerSkillValues[parSkill] = value; } void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { mStatsWindow->setValue (id, value); - hud->setValue (id, value); + mHud->setValue (id, value); mCharGen->setValue(id, value); if (id == "HBar") { - playerHealth = value; + mPlayerHealth = value; mCharGen->setPlayerHealth (value); } else if (id == "MBar") { - playerMagicka = value; + mPlayerMagicka = value; mCharGen->setPlayerMagicka (value); } else if (id == "FBar") { - playerFatigue = value; + mPlayerFatigue = value; mCharGen->setPlayerFatigue (value); } } @@ -372,11 +372,11 @@ void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicS MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) { if(id == "HBar") - return playerHealth; + return layerHealth; else if (id == "MBar") - return playerMagicka; + return mPlayerMagicka; else if (id == "FBar") - return playerFatigue; + return mPlayerFatigue; } #endif @@ -384,9 +384,9 @@ void WindowManager::setValue (const std::string& id, const std::string& value) { mStatsWindow->setValue (id, value); if (id=="name") - playerName = value; + mPlayerName = value; else if (id=="race") - playerRaceId = value; + mPlayerRaceId = value; } void WindowManager::setValue (const std::string& id, int value) @@ -396,16 +396,16 @@ void WindowManager::setValue (const std::string& id, int value) void WindowManager::setPlayerClass (const ESM::Class &class_) { - playerClass = class_; - mStatsWindow->setValue("class", playerClass.name); + mPlayerClass = class_; + mStatsWindow->setValue("class", mPlayerClass.name); } void WindowManager::configureSkills (const SkillList& major, const SkillList& minor) { mStatsWindow->configureSkills (major, minor); mCharGen->configureSkills(major, minor); - playerMajorSkills = major; - playerMinorSkills = minor; + mPlayerMajorSkills = major; + mPlayerMinorSkills = minor; } void WindowManager::setReputation (int reputation) @@ -429,7 +429,7 @@ void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) if (!dialog) return; dialog->setVisible(false); - garbageDialogs.push_back(dialog); + mGarbageDialogs.push_back(dialog); } void WindowManager::messageBox (const std::string& message, const std::vector& buttons) @@ -484,12 +484,12 @@ void WindowManager::onFrame (float frameDuration) mStatsWindow->onFrame(); - hud->onFrame(frameDuration); + mHud->onFrame(frameDuration); mDialogueWindow->checkReferenceAvailable(); mTradeWindow->checkReferenceAvailable(); mContainerWindow->checkReferenceAvailable(); - console->checkReferenceAvailable(); + mConsole->checkReferenceAvailable(); } const ESMS::ESMStore& WindowManager::getStore() const @@ -513,56 +513,56 @@ void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) name = getGameSettingString("sDefaultCellname", "Wilderness"); } - map->setCellName( name ); - hud->setCellName( name ); + mMap->setCellName( name ); + mHud->setCellName( name ); - map->setCellPrefix("Cell"); - hud->setCellPrefix("Cell"); - map->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY ); - hud->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY ); + mMap->setCellPrefix("Cell"); + mHud->setCellPrefix("Cell"); + mMap->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY ); + mHud->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY ); } else { - map->setCellName( cell->cell->name ); - hud->setCellName( cell->cell->name ); - map->setCellPrefix( cell->cell->name ); - hud->setCellPrefix( cell->cell->name ); + mMap->setCellName( cell->cell->name ); + mHud->setCellName( cell->cell->name ); + mMap->setCellPrefix( cell->cell->name ); + mHud->setCellPrefix( cell->cell->name ); } } void WindowManager::setInteriorMapTexture(const int x, const int y) { - map->setActiveCell(x,y, true); - hud->setActiveCell(x,y, true); + mMap->setActiveCell(x,y, true); + mHud->setActiveCell(x,y, true); } void WindowManager::setPlayerPos(const float x, const float y) { - map->setPlayerPos(x,y); - hud->setPlayerPos(x,y); + mMap->setPlayerPos(x,y); + mHud->setPlayerPos(x,y); } void WindowManager::setPlayerDir(const float x, const float y) { - map->setPlayerDir(x,y); - hud->setPlayerDir(x,y); + mMap->setPlayerDir(x,y); + mHud->setPlayerDir(x,y); } void WindowManager::setHMSVisibility(bool visible) { - hud->setBottomLeftVisibility(visible, hud->weapBox->getVisible(), hud->spellBox->getVisible()); + mHud->setBottomLeftVisibility(visible, mHud->mWeapBox->getVisible(), mHud->mSpellBox->getVisible()); } void WindowManager::setMinimapVisibility(bool visible) { - hud->setBottomRightVisibility(hud->effectBox->getVisible(), visible); + mHud->setBottomRightVisibility(mHud->mEffectBox->getVisible(), visible); } void WindowManager::toggleFogOfWar() { - map->toggleFogOfWar(); - hud->toggleFogOfWar(); + mMap->toggleFogOfWar(); + mHud->toggleFogOfWar(); } void WindowManager::setFocusObject(const MWWorld::Ptr& focus) @@ -587,13 +587,13 @@ bool WindowManager::getFullHelp() const void WindowManager::setWeaponVisibility(bool visible) { - hud->setBottomLeftVisibility(hud->health->getVisible(), visible, hud->spellBox->getVisible()); + mHud->setBottomLeftVisibility(mHud->health->getVisible(), visible, mHud->mSpellBox->getVisible()); } void WindowManager::setSpellVisibility(bool visible) { - hud->setBottomLeftVisibility(hud->health->getVisible(), hud->weapBox->getVisible(), visible); - hud->setBottomRightVisibility(visible, hud->minimapBox->getVisible()); + mHud->setBottomLeftVisibility(mHud->health->getVisible(), mHud->mWeapBox->getVisible(), visible); + mHud->setBottomRightVisibility(visible, mHud->mMinimapBox->getVisible()); } void WindowManager::setMouseVisible(bool visible) @@ -618,7 +618,7 @@ void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _r void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - hud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); + mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); bool changeRes = false; @@ -637,8 +637,8 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector { int x = Settings::Manager::getInt("resolution x", "Video"); int y = Settings::Manager::getInt("resolution y", "Video"); - hud->onResChange(x, y); - console->onResChange(x, y); + mHud->onResChange(x, y); + mConsole->onResChange(x, y); mSettingsWindow->center(); mAlchemyWindow->center(); mScrollWindow->center(); @@ -649,7 +649,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector void WindowManager::pushGuiMode(GuiMode mode) { - if (mode==GM_Inventory && allowed==GW_None) + if (mode==GM_Inventory && mAllowed==GW_None) return; mGuiModes.push_back(mode); @@ -690,32 +690,32 @@ void WindowManager::removeGuiMode(GuiMode mode) void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) { - hud->setSelectedSpell(spellId, successChancePercent); + mHud->setSelectedSpell(spellId, successChancePercent); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); mSpellWindow->setTitle(spell->name); } void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) { - hud->setSelectedEnchantItem(item, chargePercent); + mHud->setSelectedEnchantItem(item, chargePercent); mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); } void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) { - hud->setSelectedWeapon(item, durabilityPercent); + mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); } void WindowManager::unsetSelectedSpell() { - hud->unsetSelectedSpell(); + mHud->unsetSelectedSpell(); mSpellWindow->setTitle("#{sNone}"); } void WindowManager::unsetSelectedWeapon() { - hud->unsetSelectedWeapon(); + mHud->unsetSelectedWeapon(); mInventoryWindow->setTitle("#{sSkillHandtohand}"); } @@ -738,5 +738,5 @@ void WindowManager::getMousePosition(float &x, float &y) bool WindowManager::getWorldMouseOver() { - return hud->getWorldMouseOver(); + return mHud->getWorldMouseOver(); } diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 03ffa6b59..db9505493 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -123,27 +123,27 @@ namespace MWGui void toggleVisible(GuiWindow wnd) { - shown = (shown & wnd) ? (GuiWindow) (shown & ~wnd) : (GuiWindow) (shown | wnd); + mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); updateVisible(); } // Disallow all inventory mode windows void disallowAll() { - allowed = GW_None; + mAllowed = GW_None; updateVisible(); } // Allow one or more windows void allow(GuiWindow wnd) { - allowed = (GuiWindow)(allowed | wnd); + mAllowed = (GuiWindow)(mAllowed | wnd); updateVisible(); } bool isAllowed(GuiWindow wnd) const { - return allowed & wnd; + return mAllowed & wnd; } MWGui::DialogueWindow* getDialogueWindow() {return mDialogueWindow;} @@ -155,9 +155,9 @@ namespace MWGui MWGui::ConfirmationDialog* getConfirmationDialog() {return mConfirmationDialog;} MWGui::TradeWindow* getTradeWindow() {return mTradeWindow;} MWGui::SpellWindow* getSpellWindow() {return mSpellWindow;} - MWGui::Console* getConsole() {return console;} + MWGui::Console* getConsole() {return mConsole;} - MyGUI::Gui* getGui() const { return gui; } + MyGUI::Gui* getGui() const { return mGui; } void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) { @@ -223,10 +223,10 @@ namespace MWGui void onFrame (float frameDuration); - std::map > getPlayerSkillValues() { return playerSkillValues; } - std::map > getPlayerAttributeValues() { return playerAttributes; } - SkillList getPlayerMinorSkills() { return playerMinorSkills; } - SkillList getPlayerMajorSkills() { return playerMajorSkills; } + std::map > getPlayerSkillValues() { return mPlayerSkillValues; } + std::map > getPlayerAttributeValues() { return mPlayerAttributes; } + SkillList getPlayerMinorSkills() { return mPlayerMinorSkills; } + SkillList getPlayerMajorSkills() { return mPlayerMajorSkills; } /** * Fetches a GMST string from the store, if there is no setting with the given @@ -243,13 +243,13 @@ namespace MWGui private: OEngine::GUI::MyGUIManager *mGuiManager; - HUD *hud; - MapWindow *map; - MainMenu *menu; + HUD *mHud; + MapWindow *mMap; + MainMenu *mMenu; ToolTips *mToolTips; StatsWindow *mStatsWindow; MessageBoxManager *mMessageBoxManager; - Console *console; + Console *mConsole; JournalWindow* mJournal; DialogueWindow *mDialogueWindow; ContainerWindow *mContainerWindow; @@ -267,33 +267,33 @@ namespace MWGui CharacterCreation* mCharGen; // Various stats about player as needed by window manager - ESM::Class playerClass; - std::string playerName; - std::string playerRaceId; - std::map > playerAttributes; - SkillList playerMajorSkills, playerMinorSkills; - std::map > playerSkillValues; - MWMechanics::DynamicStat playerHealth, playerMagicka, playerFatigue; + ESM::Class mPlayerClass; + std::string mPlayerName; + std::string mPlayerRaceId; + std::map > mPlayerAttributes; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map > mPlayerSkillValues; + MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; - MyGUI::Gui *gui; // Gui + MyGUI::Gui *mGui; // Gui std::vector mGuiModes; - std::vector garbageDialogs; + std::vector mGarbageDialogs; void cleanupGarbage(); - GuiWindow shown; // Currently shown windows in inventory mode + GuiWindow mShown; // Currently shown windows in inventory mode /* Currently ALLOWED windows in inventory mode. This is used at the start of the game, when windows are enabled one by one through script commands. You can manipulate this through using allow() and disableAll(). */ - GuiWindow allowed; + GuiWindow mAllowed; void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings - int showFPSLevel; + int mShowFPSLevel; float mFPS; unsigned int mTriangleCount; unsigned int mBatchCount; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f0a6ab683..549dbf6b2 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -6,25 +6,26 @@ #include #include -namespace MWRender{ - std::map Animation::mUniqueIDs; +namespace MWRender +{ + std::map Animation::sUniqueIDs; Animation::Animation(OEngine::Render::OgreRenderer& _rend) - : insert(NULL) + : mInsert(NULL) , mRend(_rend) - , vecRotPos() - , time(0.0f) - , startTime(0.0f) - , stopTime(0.0f) - , animate(0) - , rindexI() - , tindexI() - , shapeNumber(0) - , shapeIndexI() - , shapes(NULL) - , transformations(NULL) - , textmappings(NULL) - , base(NULL) + , mVecRotPos() + , mTime(0.0f) + , mStartTime(0.0f) + , mStopTime(0.0f) + , mAnimate(0) + , mRindexI() + , mTindexI() + , mShapeNumber(0) + , mShapeIndexI() + , mShapes(NULL) + , mTransformations(NULL) + , mTextmappings(NULL) + , mBase(NULL) { } @@ -32,178 +33,191 @@ namespace MWRender{ { } - std::string Animation::getUniqueID(std::string mesh){ - int counter; - std::string copy = mesh; - std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower); - if(mUniqueIDs.find(copy) == mUniqueIDs.end()){ - counter = mUniqueIDs[copy] = 0; - } - else{ - mUniqueIDs[copy] = mUniqueIDs[copy] + 1; - counter = mUniqueIDs[copy]; - } - - std::stringstream out; - if(counter > 99 && counter < 1000) - out << "0"; - else if(counter > 9) - out << "00"; - else - out << "000"; - out << counter; - return out.str(); -} - void Animation::startScript(std::string groupname, int mode, int loops){ - //If groupname is recognized set animate to true - //Set the start time and stop time - //How many times to loop - if(groupname == "all"){ - animate = loops; - time = startTime; + std::string Animation::getUniqueID(std::string mesh) + { + int counter; + std::string copy = mesh; + std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower); + + if(sUniqueIDs.find(copy) == sUniqueIDs.end()) + { + counter = sUniqueIDs[copy] = 0; } - else if(textmappings){ + else + { + sUniqueIDs[copy] = sUniqueIDs[copy] + 1; + counter = sUniqueIDs[copy]; + } + + std::stringstream out; + + if(counter > 99 && counter < 1000) + out << "0"; + else if(counter > 9) + out << "00"; + else + out << "000"; + out << counter; + + return out.str(); + } + + void Animation::startScript(std::string groupname, int mode, int loops) + { + //If groupname is recognized set animate to true + //Set the start time and stop time + //How many times to loop + if(groupname == "all") + { + mAnimate = loops; + mTime = mStartTime; + } + else if(mTextmappings) + { std::string startName = groupname + ": loop start"; std::string stopName = groupname + ": loop stop"; bool first = false; - if(loops > 1){ + if(loops > 1) + { startName = groupname + ": loop start"; stopName = groupname + ": loop stop"; - for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++){ + for(std::map::iterator iter = mTextmappings->begin(); iter != mTextmappings->end(); iter++) + { - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); + std::string current = iter->first.substr(0, startName.size()); + std::transform(current.begin(), current.end(), current.begin(), ::tolower); + std::string current2 = iter->first.substr(0, stopName.size()); + std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); - if(current == startName){ - startTime = iter->second; - animate = loops; - time = startTime; - first = true; - } - if(current2 == stopName){ - stopTime = iter->second; - if(first) - break; - } + if(current == startName) + { + mStartTime = iter->second; + mAnimate = loops; + mTime = mStartTime; + first = true; + } + if(current2 == stopName) + { + mStopTime = iter->second; + if(first) + break; + } } } - if(!first){ + if(!first) + { startName = groupname + ": start"; stopName = groupname + ": stop"; - for(std::map::iterator iter = textmappings->begin(); iter != textmappings->end(); iter++){ + for(std::map::iterator iter = mTextmappings->begin(); iter != mTextmappings->end(); iter++) + { - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); + std::string current = iter->first.substr(0, startName.size()); + std::transform(current.begin(), current.end(), current.begin(), ::tolower); + std::string current2 = iter->first.substr(0, stopName.size()); + std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); - if(current == startName){ - startTime = iter->second; - animate = loops; - time = startTime; - first = true; - } - if(current2 == stopName){ - stopTime = iter->second; - if(first) - break; + if(current == startName) + { + mStartTime = iter->second; + mAnimate = loops; + mTime = mStartTime; + first = true; + } + if(current2 == stopName) + { + mStopTime = iter->second; + if(first) + break; + } } } - } - } - - } - void Animation::stopScript(){ - animate = 0; } - - void Animation::handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){ - shapeNumber = 0; + + void Animation::stopScript() + { + mAnimate = 0; + } + + void Animation::handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel) + { + mShapeNumber = 0; if (allshapes == NULL || creaturemodel == NULL || skel == NULL) - { return; - } std::vector::iterator allshapesiter; - for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++) + for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++) + { + //std::map vecPosRot; - { - //std::map vecPosRot; + Nif::NiTriShapeCopy& copy = *allshapesiter; + std::vector* allvertices = ©.vertices; - Nif::NiTriShapeCopy& copy = *allshapesiter; - std::vector* allvertices = ©.vertices; - - - - //std::set vertices; - //std::set normals; - //std::vector boneinfovector = copy.boneinfo; + //std::set vertices; + //std::set normals; + //std::vector boneinfovector = copy.boneinfo; std::map >* verticesToChange = ©.vertsToWeights; - //std::cout << "Name " << copy.sname << "\n"; - Ogre::HardwareVertexBufferSharedPtr vbuf = creaturemodel->getMesh()->getSubMesh(copy.sname)->vertexData->vertexBufferBinding->getBuffer(0); - Ogre::Real* pReal = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL)); + //std::cout << "Name " << copy.sname << "\n"; + Ogre::HardwareVertexBufferSharedPtr vbuf = creaturemodel->getMesh()->getSubMesh(copy.sname)->vertexData->vertexBufferBinding->getBuffer(0); + Ogre::Real* pReal = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL)); - std::vector initialVertices = copy.morph.getInitialVertices(); - //Each shape has multiple indices - if(initialVertices.size() ) - { + std::vector initialVertices = copy.morph.getInitialVertices(); + //Each shape has multiple indices + if(initialVertices.size() ) + { + if(copy.vertices.size() == initialVertices.size()) + { + //Create if it doesn't already exist + if(mShapeIndexI.size() == static_cast (mShapeNumber)) + { + std::vector vec; + mShapeIndexI.push_back(vec); + } + if(mTime >= copy.morph.getStartTime() && mTime <= copy.morph.getStopTime()) + { + float x; + for (unsigned int i = 0; i < copy.morph.getAdditionalVertices().size(); i++) + { + int j = 0; + if(mShapeIndexI[mShapeNumber].size() <= i) + mShapeIndexI[mShapeNumber].push_back(0); - if(copy.vertices.size() == initialVertices.size()) - { - //Create if it doesn't already exist - if(shapeIndexI.size() == static_cast (shapeNumber)) - { - std::vector vec; - shapeIndexI.push_back(vec); - } - if(time >= copy.morph.getStartTime() && time <= copy.morph.getStopTime()){ - float x; - for (unsigned int i = 0; i < copy.morph.getAdditionalVertices().size(); i++){ - int j = 0; - if(shapeIndexI[shapeNumber].size() <= i) - shapeIndexI[shapeNumber].push_back(0); + if(timeIndex(mTime,copy.morph.getRelevantTimes()[i],(mShapeIndexI[mShapeNumber])[i], j, x)) + { + int indexI = (mShapeIndexI[mShapeNumber])[i]; + std::vector relevantData = (copy.morph.getRelevantData()[i]); + float v1 = relevantData[indexI].x; + float v2 = relevantData[j].x; + float t = v1 + (v2 - v1) * x; + + if ( t < 0 ) + t = 0; + if ( t > 1 ) + t = 1; + if( t != 0 && initialVertices.size() == copy.morph.getAdditionalVertices()[i].size()) + for (unsigned int v = 0; v < initialVertices.size(); v++) + initialVertices[v] += ((copy.morph.getAdditionalVertices()[i])[v]) * t; + } - - if(timeIndex(time,copy.morph.getRelevantTimes()[i],(shapeIndexI[shapeNumber])[i], j, x)){ - int indexI = (shapeIndexI[shapeNumber])[i]; - std::vector relevantData = (copy.morph.getRelevantData()[i]); - float v1 = relevantData[indexI].x; - float v2 = relevantData[j].x; - float t = v1 + (v2 - v1) * x; - if ( t < 0 ) t = 0; - if ( t > 1 ) t = 1; - if( t != 0 && initialVertices.size() == copy.morph.getAdditionalVertices()[i].size()) - { - for (unsigned int v = 0; v < initialVertices.size(); v++){ - initialVertices[v] += ((copy.morph.getAdditionalVertices()[i])[v]) * t; - } - } - - } - - - - } - - allvertices = &initialVertices; } - shapeNumber++; - } - } + + allvertices = &initialVertices; + } + mShapeNumber++; + } + } - if(verticesToChange->size() > 0){ + if(verticesToChange->size() > 0) + { for(std::map >::iterator iter = verticesToChange->begin(); iter != verticesToChange->end(); iter++) @@ -214,26 +228,25 @@ namespace MWRender{ Nif::NiSkinData::BoneInfoCopy* boneinfocopy = &(allshapesiter->boneinfo[inds[0].boneinfocopyindex]); Ogre::Bone *bonePtr = 0; - - Ogre::Vector3 vecPos; Ogre::Quaternion vecRot; - std::map::iterator result = vecRotPos.find(boneinfocopy); + std::map::iterator result = mVecRotPos.find(boneinfocopy); - if(result == vecRotPos.end()){ + if(result == mVecRotPos.end()) + { bonePtr = skel->getBone(boneinfocopy->bonename); vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; - - PosAndRot both; - both.vecPos = vecPos; - both.vecRot = vecRot; - vecRotPos[boneinfocopy] = both; + PosAndRot both; + both.vecPos = vecPos; + both.vecRot = vecRot; + mVecRotPos[boneinfocopy] = both; } - else{ + else + { PosAndRot both = result->second; vecPos = both.vecPos; vecRot = both.vecRot; @@ -241,263 +254,249 @@ namespace MWRender{ Ogre::Vector3 absVertPos = (vecPos + vecRot * currentVertex) * inds[0].weight; - - - for(std::size_t i = 1; i < inds.size(); i++){ + for(std::size_t i = 1; i < inds.size(); i++) + { boneinfocopy = &(allshapesiter->boneinfo[inds[i].boneinfocopyindex]); - result = vecRotPos.find(boneinfocopy); + result = mVecRotPos.find(boneinfocopy); - - if(result == vecRotPos.end()){ + if(result == mVecRotPos.end()) + { bonePtr = skel->getBone(boneinfocopy->bonename); vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; - PosAndRot both; - both.vecPos = vecPos; - both.vecRot = vecRot; - vecRotPos[boneinfocopy] = both; + PosAndRot both; + both.vecPos = vecPos; + both.vecRot = vecRot; + mVecRotPos[boneinfocopy] = both; } - else{ - PosAndRot both = result->second; - vecPos = both.vecPos; - vecRot = both.vecRot; + else + { + PosAndRot both = result->second; + vecPos = both.vecPos; + vecRot = both.vecRot; } - absVertPos += (vecPos + vecRot * currentVertex) * inds[i].weight; + } + Ogre::Real* addr = (pReal + 3 * verIndex); + *addr = absVertPos.x; + *(addr+1) = absVertPos.y; + *(addr+2) = absVertPos.z; + + } + } + else + { + //Ogre::Bone *bonePtr = creaturemodel->getSkeleton()->getBone(copy.bonename); + Ogre::Quaternion shaperot = copy.trafo.rotation; + Ogre::Vector3 shapetrans = copy.trafo.trans; + float shapescale = copy.trafo.scale; + std::vector boneSequence = copy.boneSequence; + + Ogre::Vector3 transmult; + Ogre::Quaternion rotmult; + float scale; + if(boneSequence.size() > 0) + { + std::vector::iterator boneSequenceIter = boneSequence.begin(); + if(skel->hasBone(*boneSequenceIter)) + { + Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); + + transmult = bonePtr->getPosition(); + rotmult = bonePtr->getOrientation(); + scale = bonePtr->getScale().x; + boneSequenceIter++; + + for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) + { + if(skel->hasBone(*boneSequenceIter)) + { + Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); + // Computes C = B + AxC*scale + transmult = transmult + rotmult * bonePtr->getPosition(); + rotmult = rotmult * bonePtr->getOrientation(); + scale = scale * bonePtr->getScale().x; + } + //std::cout << "Bone:" << *boneSequenceIter << " "; + } + transmult = transmult + rotmult * shapetrans; + rotmult = rotmult * shaperot; + scale = shapescale * scale; + + //std::cout << "Position: " << transmult << "Rotation: " << rotmult << "\n"; + } + } + else + { + transmult = shapetrans; + rotmult = shaperot; + scale = shapescale; + } + + // Computes C = B + AxC*scale + // final_vector = old_vector + old_rotation*new_vector*old_scale/ + + for(unsigned int i = 0; i < allvertices->size(); i++) + { + Ogre::Vector3 current = transmult + rotmult * (*allvertices)[i]; + Ogre::Real* addr = pReal + i * 3; + *addr = current.x; + *(addr+1) = current.y; + *(addr + 2) = current.z; + + }/* + for(int i = 0; i < allnormals.size(); i++){ + Ogre::Vector3 current =rotmult * allnormals[i]; + Ogre::Real* addr = pRealNormal + i * 3; + *addr = current.x; + *(addr+1) = current.y; + *(addr + 2) = current.z; + + }*/ + + } + vbuf->unlock(); + } + + } + + bool Animation::timeIndex( float time, const std::vector & times, int & i, int & j, float & x ) + { + int count; + if ( (count = times.size()) > 0 ) + { + if ( time <= times[0] ) + { + i = j = 0; + x = 0.0; + return true; + } + if ( time >= times[count - 1] ) + { + i = j = count - 1; + x = 0.0; + return true; + } + + if ( i < 0 || i >= count ) + i = 0; + + float tI = times[i]; + if ( time > tI ) + { + j = i + 1; + float tJ; + while ( time >= ( tJ = times[j]) ) + { + i = j++; + tI = tJ; + } + x = ( time - tI ) / ( tJ - tI ); + return true; + } + else if ( time < tI ) + { + j = i - 1; + float tJ; + while ( time <= ( tJ = times[j] ) ) + { + i = j--; + tI = tJ; + } + x = ( time - tI ) / ( tJ - tI ); + return true; + } + else + { + j = i; + x = 0.0; + return true; + } + } + else + return false; + + } + + void Animation::handleAnimationTransforms() + { + Ogre::SkeletonInstance* skel = mBase->getSkeleton(); + + Ogre::Bone* b = skel->getRootBone(); + b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3)); //This is a trick + + skel->_updateTransforms(); + //skel->_notifyManualBonesDirty(); + + mBase->getAllAnimationStates()->_notifyDirty(); + //mBase->_updateAnimation(); + //mBase->_notifyMoved(); + + std::vector::iterator iter; + int slot = 0; + if(mTransformations) + { + for(iter = mTransformations->begin(); iter != mTransformations->end(); iter++) + { + if(mTime < iter->getStartTime() || mTime < mStartTime || mTime > iter->getStopTime()) + { + slot++; + continue; + } + + float x; + float x2; + + const std::vector & quats = iter->getQuat(); + + const std::vector & ttime = iter->gettTime(); + const std::vector & rtime = iter->getrTime(); + int rindexJ = mRindexI[slot]; + + timeIndex(mTime, rtime, mRindexI[slot], rindexJ, x2); + int tindexJ = mTindexI[slot]; + + const std::vector & translist1 = iter->getTranslist1(); + + timeIndex(mTime, ttime, mTindexI[slot], tindexJ, x); + + Ogre::Vector3 t; + Ogre::Quaternion r; + + bool bTrans = translist1.size() > 0; + + bool bQuats = quats.size() > 0; + + if(skel->hasBone(iter->getBonename())) + { + Ogre::Bone* bone = skel->getBone(iter->getBonename()); + + if(bTrans) + { + Ogre::Vector3 v1 = translist1[mTindexI[slot]]; + Ogre::Vector3 v2 = translist1[tindexJ]; + t = (v1 + (v2 - v1) * x); + bone->setPosition(t); } - Ogre::Real* addr = (pReal + 3 * verIndex); - *addr = absVertPos.x; - *(addr+1) = absVertPos.y; - *(addr+2) = absVertPos.z; + + if(bQuats) + { + r = Ogre::Quaternion::Slerp(x2, quats[mRindexI[slot]], quats[rindexJ], true); + bone->setOrientation(r); + } } - - - - } - else - { - //Ogre::Bone *bonePtr = creaturemodel->getSkeleton()->getBone(copy.bonename); - Ogre::Quaternion shaperot = copy.trafo.rotation; - Ogre::Vector3 shapetrans = copy.trafo.trans; - float shapescale = copy.trafo.scale; - std::vector boneSequence = copy.boneSequence; - - Ogre::Vector3 transmult; - Ogre::Quaternion rotmult; - float scale; - if(boneSequence.size() > 0){ - std::vector::iterator boneSequenceIter = boneSequence.begin(); - if(skel->hasBone(*boneSequenceIter)){ - Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); - - - - - transmult = bonePtr->getPosition(); - rotmult = bonePtr->getOrientation(); - scale = bonePtr->getScale().x; - boneSequenceIter++; - - for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) - { - if(skel->hasBone(*boneSequenceIter)){ - Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); - // Computes C = B + AxC*scale - transmult = transmult + rotmult * bonePtr->getPosition(); - rotmult = rotmult * bonePtr->getOrientation(); - scale = scale * bonePtr->getScale().x; - } - //std::cout << "Bone:" << *boneSequenceIter << " "; - } - transmult = transmult + rotmult * shapetrans; - rotmult = rotmult * shaperot; - scale = shapescale * scale; - - //std::cout << "Position: " << transmult << "Rotation: " << rotmult << "\n"; - } - } - else - { - transmult = shapetrans; - rotmult = shaperot; - scale = shapescale; - } - - - - - // Computes C = B + AxC*scale - // final_vector = old_vector + old_rotation*new_vector*old_scale/ - - for(unsigned int i = 0; i < allvertices->size(); i++){ - Ogre::Vector3 current = transmult + rotmult * (*allvertices)[i]; - Ogre::Real* addr = pReal + i * 3; - *addr = current.x; - *(addr+1) = current.y; - *(addr + 2) = current.z; - - }/* - for(int i = 0; i < allnormals.size(); i++){ - Ogre::Vector3 current =rotmult * allnormals[i]; - Ogre::Real* addr = pRealNormal + i * 3; - *addr = current.x; - *(addr+1) = current.y; - *(addr + 2) = current.z; - - }*/ - - } - vbuf->unlock(); - - } - + slot++; + } + skel->_updateTransforms(); + mBase->getAllAnimationStates()->_notifyDirty(); + } } - bool Animation::timeIndex( float time, const std::vector & times, int & i, int & j, float & x ){ - int count; - if ( (count = times.size()) > 0 ) - { - if ( time <= times[0] ) - { - i = j = 0; - x = 0.0; - return true; - } - if ( time >= times[count - 1] ) - { - i = j = count - 1; - x = 0.0; - return true; - } - - if ( i < 0 || i >= count ) - i = 0; - - float tI = times[i]; - if ( time > tI ) - { - j = i + 1; - float tJ; - while ( time >= ( tJ = times[j]) ) - { - i = j++; - tI = tJ; - } - x = ( time - tI ) / ( tJ - tI ); - return true; - } - else if ( time < tI ) - { - j = i - 1; - float tJ; - while ( time <= ( tJ = times[j] ) ) - { - i = j--; - tI = tJ; - } - x = ( time - tI ) / ( tJ - tI ); - return true; - } - else - { - j = i; - x = 0.0; - return true; - } - } - else - return false; - -} - - void Animation::handleAnimationTransforms(){ - - - Ogre::SkeletonInstance* skel = base->getSkeleton(); - - - Ogre::Bone* b = skel->getRootBone(); - b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3)); //This is a trick - - skel->_updateTransforms(); - //skel->_notifyManualBonesDirty(); - - base->getAllAnimationStates()->_notifyDirty(); - //base->_updateAnimation(); - //base->_notifyMoved(); - - - - - std::vector::iterator iter; - int slot = 0; - if(transformations){ - for(iter = transformations->begin(); iter != transformations->end(); iter++){ - if(time < iter->getStartTime() || time < startTime || time > iter->getStopTime()) - { - slot++; - continue; - } - - float x; - float x2; - - const std::vector & quats = iter->getQuat(); - - const std::vector & ttime = iter->gettTime(); - - - const std::vector & rtime = iter->getrTime(); - int rindexJ = rindexI[slot]; - - timeIndex(time, rtime, rindexI[slot], rindexJ, x2); - int tindexJ = tindexI[slot]; - - - const std::vector & translist1 = iter->getTranslist1(); - - timeIndex(time, ttime, tindexI[slot], tindexJ, x); - - Ogre::Vector3 t; - Ogre::Quaternion r; - - bool bTrans = translist1.size() > 0; - - - bool bQuats = quats.size() > 0; - - if(skel->hasBone(iter->getBonename())){ - Ogre::Bone* bone = skel->getBone(iter->getBonename()); - if(bTrans){ - Ogre::Vector3 v1 = translist1[tindexI[slot]]; - Ogre::Vector3 v2 = translist1[tindexJ]; - t = (v1 + (v2 - v1) * x); - bone->setPosition(t); - - } - if(bQuats){ - r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); - bone->setOrientation(r); - } - - - - - - } - - - slot++; - } - skel->_updateTransforms(); - base->getAllAnimationStates()->_notifyDirty(); -} -} } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 4ab60cff4..cebffaaf2 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -22,36 +22,30 @@ struct PosAndRot{ class Animation{ protected: - Ogre::SceneNode* insert; + Ogre::SceneNode* mInsert; OEngine::Render::OgreRenderer &mRend; - std::map vecRotPos; - static std::map mUniqueIDs; + std::map mVecRotPos; + static std::map sUniqueIDs; - - - - - float time; - float startTime; - float stopTime; - int animate; - //Represents a rotation index for each bone - std::vectorrindexI; + float mTime; + float mStartTime; + float mStopTime; + int mAnimate; + //Represents a rotation index for each bone + std::vectormRindexI; //Represents a translation index for each bone - std::vectortindexI; + std::vectormTindexI; - //Only shapes with morphing data will use a shape number - int shapeNumber; - std::vector > shapeIndexI; + //Only shapes with morphing data will use a shape number + int mShapeNumber; + std::vector > mShapeIndexI; - //Ogre::SkeletonInstance* skel; - std::vector* shapes; //All the NiTriShapeData for a creature + //Ogre::SkeletonInstance* skel; + std::vector* mShapes; //All the NiTriShapeData for a creature - - - std::vector* transformations; - std::map* textmappings; - Ogre::Entity* base; + std::vector* mTransformations; + std::map* mTextmappings; + Ogre::Entity* mBase; void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); void handleAnimationTransforms(); bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); @@ -63,7 +57,6 @@ class Animation{ void startScript(std::string groupname, int mode, int loops); void stopScript(); - virtual ~Animation(); }; diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index e1fa7868c..20037a773 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -12,26 +12,28 @@ using namespace Ogre; using namespace NifOgre; namespace MWRender{ -CreatureAnimation::~CreatureAnimation(){ +CreatureAnimation::~CreatureAnimation() +{ +} -} -CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend){ - insert = ptr.getRefData().getBaseNode(); - MWWorld::LiveCellRef *ref = - ptr.get(); +CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend) +{ + mInsert = ptr.getRefData().getBaseNode(); + MWWorld::LiveCellRef *ref = ptr.get(); assert (ref->base != NULL); - if(!ref->base->model.empty()){ + if(!ref->base->model.empty()) + { const std::string &mesh = "meshes\\" + ref->base->model; std::string meshNumbered = mesh + getUniqueID(mesh) + ">|"; NifOgre::NIFLoader::load(meshNumbered); - base = mRend.getScene()->createEntity(meshNumbered); - base->setVisibilityFlags(RV_Actors); + mBase = mRend.getScene()->createEntity(meshNumbered); + mBase->setVisibilityFlags(RV_Actors); bool transparent = false; - for (unsigned int i=0; igetNumSubEntities(); ++i) + for (unsigned int i=0; i < mBase->getNumSubEntities(); ++i) { - Ogre::MaterialPtr mat = base->getSubEntity(i)->getMaterial(); + Ogre::MaterialPtr mat = mBase->getSubEntity(i)->getMaterial(); Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); while (techIt.hasMoreElements()) { @@ -46,46 +48,51 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O } } } - base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + mBase->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); std::string meshZero = mesh + "0000>|"; - if((transformations = (NIFLoader::getSingletonPtr())->getAnim(meshZero))){ - - for(std::size_t init = 0; init < transformations->size(); init++){ - rindexI.push_back(0); - tindexI.push_back(0); - } - stopTime = transformations->begin()->getStopTime(); - startTime = transformations->begin()->getStartTime(); - shapes = (NIFLoader::getSingletonPtr())->getShapes(meshZero); + if((mTransformations = (NIFLoader::getSingletonPtr())->getAnim(meshZero))) + { + for(std::size_t init = 0; init < mTransformations->size(); init++) + { + mRindexI.push_back(0); + mTindexI.push_back(0); + } + mStopTime = mTransformations->begin()->getStopTime(); + mStartTime = mTransformations->begin()->getStartTime(); + mShapes = (NIFLoader::getSingletonPtr())->getShapes(meshZero); } - textmappings = NIFLoader::getSingletonPtr()->getTextIndices(meshZero); - insert->attachObject(base); + mTextmappings = NIFLoader::getSingletonPtr()->getTextIndices(meshZero); + mInsert->attachObject(mBase); } } -void CreatureAnimation::runAnimation(float timepassed){ - vecRotPos.clear(); - if(animate > 0){ - //Add the amount of time passed to time +void CreatureAnimation::runAnimation(float timepassed) +{ + mVecRotPos.clear(); + if(mAnimate > 0) + { + //Add the amount of time passed to time - //Handle the animation transforms dependent on time + //Handle the animation transforms dependent on time - //Handle the shapes dependent on animation transforms - time += timepassed; - if(time >= stopTime){ - animate--; + //Handle the shapes dependent on animation transforms + mTime += timepassed; + if(mTime >= mStopTime) + { + mAnimate--; //std::cout << "Stopping the animation\n"; - if(animate == 0) - time = stopTime; + if(mAnimate == 0) + mTime = mStopTime; else - time = startTime + (time - stopTime); + mTime = mStartTime + (mTime - mStopTime); } handleAnimationTransforms(); - handleShapes(shapes, base, base->getSkeleton()); + handleShapes(mShapes, mBase, mBase->getSkeleton()); - } + } } + } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index f50b7904b..d158eecb4 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -17,8 +17,7 @@ class CreatureAnimation: public Animation{ public: virtual ~CreatureAnimation(); CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend); - virtual void runAnimation(float timepassed); - + virtual void runAnimation(float timepassed); }; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index fa88b7277..ef075b12b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -14,118 +14,117 @@ using namespace Ogre; using namespace NifOgre; namespace MWRender{ -NpcAnimation::~NpcAnimation(){ - +NpcAnimation::~NpcAnimation() +{ } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv): Animation(_rend), mStateID(-1), inv(_inv), timeToChange(0), - robe(inv.end()), helmet(inv.end()), shirt(inv.end()), - cuirass(inv.end()), greaves(inv.end()), - leftpauldron(inv.end()), rightpauldron(inv.end()), - boots(inv.end()), - leftglove(inv.end()), rightglove(inv.end()), skirtiter(inv.end()), - pants(inv.end()), +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv): Animation(_rend), mStateID(-1), mInv(_inv), timeToChange(0), + robe(mInv.end()), helmet(mInv.end()), shirt(mInv.end()), + cuirass(mInv.end()), greaves(mInv.end()), + leftpauldron(mInv.end()), rightpauldron(mInv.end()), + boots(mInv.end()), + leftglove(mInv.end()), rightglove(mInv.end()), skirtiter(mInv.end()), + pants(mInv.end()), lclavicle(0), - rclavicle(0), - rupperArm(0), - lupperArm(0), - rUpperLeg(0), - lUpperLeg(0), - lForearm(0), - rForearm(0), - lWrist(0), - rWrist(0), - rKnee(0), - lKnee(0), - neck(0), - rAnkle(0), - lAnkle(0), - groin(0), - lfoot(0), - rfoot(0) + rclavicle(0), + rupperArm(0), + lupperArm(0), + rUpperLeg(0), + lUpperLeg(0), + lForearm(0), + rForearm(0), + lWrist(0), + rWrist(0), + rKnee(0), + lKnee(0), + neck(0), + rAnkle(0), + lAnkle(0), + groin(0), + lfoot(0), + rfoot(0) +{ + MWWorld::LiveCellRef *ref = ptr.get(); + Ogre::Entity* blank = 0; + std::vector* blankshape = 0; + mZero = std::make_pair(blank, blankshape); + mChest = std::make_pair(blank, blankshape); + mTail = std::make_pair(blank, blankshape); + mLFreeFoot = std::make_pair(blank, blankshape); + mRFreeFoot = std::make_pair(blank, blankshape); + mRhand = std::make_pair(blank, blankshape); + mLhand = std::make_pair(blank, blankshape); + mSkirt = std::make_pair(blank, blankshape); + for (int init = 0; init < 27; init++) { - MWWorld::LiveCellRef *ref = - ptr.get(); - Ogre::Entity* blank = 0; - std::vector* blankshape = 0; - zero = std::make_pair(blank, blankshape); - chest = std::make_pair(blank, blankshape); - tail = std::make_pair(blank, blankshape); - lFreeFoot = std::make_pair(blank, blankshape); - rFreeFoot = std::make_pair(blank, blankshape); - rhand = std::make_pair(blank, blankshape); - lhand = std::make_pair(blank, blankshape); - skirt = std::make_pair(blank, blankshape); - for (int init = 0; init < 27; init++){ - partslots[init] = -1; //each slot is empty - partpriorities[init] = 0; - } + mPartslots[init] = -1; //each slot is empty + mPartPriorities[init] = 0; + } - //Part selection on last character of the file string - // " Tri Chest - // * Tri Tail - // : Tri Left Foot - // < Tri Right Foot - // > Tri Left Hand - // ? Tri Right Hand - // | Normal + //Part selection on last character of the file string + // " Tri Chest + // * Tri Tail + // : Tri Left Foot + // < Tri Right Foot + // > Tri Left Hand + // ? Tri Right Hand + // | Normal - //Mirroring Parts on second to last character - //suffix == '*' - // vector = Ogre::Vector3(-1,1,1); - // suffix == '?' - // vector = Ogre::Vector3(1,-1,1); - // suffix == '<' - // vector = Ogre::Vector3(1,1,-1); + //Mirroring Parts on second to last character + //suffix == '*' + // vector = Ogre::Vector3(-1,1,1); + // suffix == '?' + // vector = Ogre::Vector3(1,-1,1); + // suffix == '<' + // vector = Ogre::Vector3(1,1,-1); - std::string hairID = ref->base->hair; - std::string headID = ref->base->head; - headModel = "meshes\\" + - MWBase::Environment::get().getWorld()->getStore().bodyParts.find(headID)->model; + std::string hairID = ref->base->hair; + std::string headID = ref->base->head; + headModel = "meshes\\" + + MWBase::Environment::get().getWorld()->getStore().bodyParts.find(headID)->model; - hairModel = "meshes\\" + - MWBase::Environment::get().getWorld()->getStore().bodyParts.find(hairID)->model; - npcName = ref->base->name; + hairModel = "meshes\\" + + MWBase::Environment::get().getWorld()->getStore().bodyParts.find(hairID)->model; + npcName = ref->base->name; - //ESMStore::Races r = - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().races.find(ref->base->race); + //ESMStore::Races r = + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().races.find(ref->base->race); - bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); - char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2); - isFemale = tolower(secondtolast) == 'f'; - std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); - isBeast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; + bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); + char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2); + isFemale = tolower(secondtolast) == 'f'; + std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); + isBeast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; - /*std::cout << "Race: " << ref->base->race ; - if(female){ - std::cout << " Sex: Female" << " Height: " << race->data.height.female << "\n"; - } - else{ - std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; - }*/ + /*std::cout << "Race: " << ref->base->race ; + if(female){ + std::cout << " Sex: Female" << " Height: " << race->data.height.female << "\n"; + } + else{ + std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; + }*/ + std::string smodel = "meshes\\base_anim.nif"; + if(isBeast) + smodel = "meshes\\base_animkna.nif"; - std::string smodel = "meshes\\base_anim.nif"; - if(isBeast) - smodel = "meshes\\base_animkna.nif"; + mInsert = ptr.getRefData().getBaseNode(); + assert(mInsert); + + NifOgre::NIFLoader::load(smodel); - insert = ptr.getRefData().getBaseNode(); - assert(insert); + mBase = mRend.getScene()->createEntity(smodel); - NifOgre::NIFLoader::load(smodel); - - base = mRend.getScene()->createEntity(smodel); - - base->setVisibilityFlags(RV_Actors); + mBase->setVisibilityFlags(RV_Actors); bool transparent = false; - for (unsigned int i=0; igetNumSubEntities(); ++i) + for (unsigned int i=0; igetNumSubEntities(); ++i) { - Ogre::MaterialPtr mat = base->getSubEntity(i)->getMaterial(); + Ogre::MaterialPtr mat = mBase->getSubEntity(i)->getMaterial(); Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); while (techIt.hasMoreElements()) { @@ -140,625 +139,694 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere } } } - base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + mBase->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones + mBase->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones //stay in the same place when we skipanim, or open a gui window + if((mTransformations = (NIFLoader::getSingletonPtr())->getAnim(smodel))) + { - if((transformations = (NIFLoader::getSingletonPtr())->getAnim(smodel))){ + for(unsigned int init = 0; init < mTransformations->size(); init++) + { + mRindexI.push_back(0); + mTindexI.push_back(0); + } - for(unsigned int init = 0; init < transformations->size(); init++){ - rindexI.push_back(0); - tindexI.push_back(0); - } - - stopTime = transformations->begin()->getStopTime(); - startTime = transformations->begin()->getStartTime(); + mStopTime = mTransformations->begin()->getStopTime(); + mStartTime = mTransformations->begin()->getStartTime(); } - textmappings = NIFLoader::getSingletonPtr()->getTextIndices(smodel); - insert->attachObject(base); + mTextmappings = NIFLoader::getSingletonPtr()->getTextIndices(smodel); + mInsert->attachObject(mBase); - if(isFemale) - insert->scale(race->data.height.female, race->data.height.female, race->data.height.female); - else - insert->scale(race->data.height.male, race->data.height.male, race->data.height.male); - updateParts(); + if(isFemale) + mInsert->scale(race->data.height.female, race->data.height.female, race->data.height.female); + else + mInsert->scale(race->data.height.male, race->data.height.male, race->data.height.male); + updateParts(); } -void NpcAnimation::updateParts(){ +void NpcAnimation::updateParts() +{ - bool apparelChanged = false; + bool apparelChanged = false; - //inv.getSlot(MWWorld::InventoryStore::Slot_Robe); - if(robe != inv.getSlot(MWWorld::InventoryStore::Slot_Robe)){ - //A robe was added or removed - removePartGroup(MWWorld::InventoryStore::Slot_Robe); - robe = inv.getSlot(MWWorld::InventoryStore::Slot_Robe); - apparelChanged = true; - } - if(skirtiter != inv.getSlot(MWWorld::InventoryStore::Slot_Skirt)){ - //A robe was added or removed - removePartGroup(MWWorld::InventoryStore::Slot_Skirt); - skirtiter = inv.getSlot(MWWorld::InventoryStore::Slot_Skirt); - apparelChanged = true; - } - if(helmet != inv.getSlot(MWWorld::InventoryStore::Slot_Helmet)){ - apparelChanged = true; - helmet = inv.getSlot(MWWorld::InventoryStore::Slot_Helmet); - removePartGroup(MWWorld::InventoryStore::Slot_Helmet); + //mInv.getSlot(MWWorld::InventoryStore::Slot_Robe); + if(robe != mInv.getSlot(MWWorld::InventoryStore::Slot_Robe)) + { + //A robe was added or removed + removePartGroup(MWWorld::InventoryStore::Slot_Robe); + robe = mInv.getSlot(MWWorld::InventoryStore::Slot_Robe); + apparelChanged = true; + } + if(skirtiter != mInv.getSlot(MWWorld::InventoryStore::Slot_Skirt)) + { + //A robe was added or removed + removePartGroup(MWWorld::InventoryStore::Slot_Skirt); + skirtiter = mInv.getSlot(MWWorld::InventoryStore::Slot_Skirt); + apparelChanged = true; + } + if(helmet != mInv.getSlot(MWWorld::InventoryStore::Slot_Helmet)) + { + apparelChanged = true; + helmet = mInv.getSlot(MWWorld::InventoryStore::Slot_Helmet); + removePartGroup(MWWorld::InventoryStore::Slot_Helmet); + } + if(cuirass != mInv.getSlot(MWWorld::InventoryStore::Slot_Cuirass)) + { + cuirass = mInv.getSlot(MWWorld::InventoryStore::Slot_Cuirass); + removePartGroup(MWWorld::InventoryStore::Slot_Cuirass); + apparelChanged = true; + + } + if(greaves != mInv.getSlot(MWWorld::InventoryStore::Slot_Greaves)) + { + greaves = mInv.getSlot(MWWorld::InventoryStore::Slot_Greaves); + removePartGroup(MWWorld::InventoryStore::Slot_Greaves); + apparelChanged = true; + } + if(leftpauldron != mInv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron)) + { + leftpauldron = mInv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron); + removePartGroup(MWWorld::InventoryStore::Slot_LeftPauldron); + apparelChanged = true; + + } + if(rightpauldron != mInv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron)) + { + rightpauldron = mInv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron); + removePartGroup(MWWorld::InventoryStore::Slot_RightPauldron); + apparelChanged = true; + + } + if(!isBeast && boots != mInv.getSlot(MWWorld::InventoryStore::Slot_Boots)) + { + boots = mInv.getSlot(MWWorld::InventoryStore::Slot_Boots); + removePartGroup(MWWorld::InventoryStore::Slot_Boots); + apparelChanged = true; + + } + if(leftglove != mInv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet)) + { + leftglove = mInv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet); + removePartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet); + apparelChanged = true; + + } + if(rightglove != mInv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet)) + { + rightglove = mInv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet); + removePartGroup(MWWorld::InventoryStore::Slot_RightGauntlet); + apparelChanged = true; + + } + if(shirt != mInv.getSlot(MWWorld::InventoryStore::Slot_Shirt)) + { + shirt = mInv.getSlot(MWWorld::InventoryStore::Slot_Shirt); + removePartGroup(MWWorld::InventoryStore::Slot_Shirt); + apparelChanged = true; + + } + if(pants != mInv.getSlot(MWWorld::InventoryStore::Slot_Pants)) + { + pants = mInv.getSlot(MWWorld::InventoryStore::Slot_Pants); + removePartGroup(MWWorld::InventoryStore::Slot_Pants); + apparelChanged = true; + } + + if(apparelChanged) + { + + if(robe != mInv.end()) + { + MWWorld::Ptr ptr = *robe; + + const ESM::Clothing *clothes = (ptr.get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts); + reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5); + reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5); } - if(cuirass != inv.getSlot(MWWorld::InventoryStore::Slot_Cuirass)){ - cuirass = inv.getSlot(MWWorld::InventoryStore::Slot_Cuirass); - removePartGroup(MWWorld::InventoryStore::Slot_Cuirass); - apparelChanged = true; - - } - if(greaves != inv.getSlot(MWWorld::InventoryStore::Slot_Greaves)){ - greaves = inv.getSlot(MWWorld::InventoryStore::Slot_Greaves); - removePartGroup(MWWorld::InventoryStore::Slot_Greaves); - apparelChanged = true; - } - if(leftpauldron != inv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron)){ - leftpauldron = inv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_LeftPauldron); - apparelChanged = true; - - } - if(rightpauldron != inv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron)){ - rightpauldron = inv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_RightPauldron); - apparelChanged = true; - - } - if(!isBeast && boots != inv.getSlot(MWWorld::InventoryStore::Slot_Boots)){ - boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots); - removePartGroup(MWWorld::InventoryStore::Slot_Boots); - apparelChanged = true; - - } - if(leftglove != inv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet)){ - leftglove = inv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet); - apparelChanged = true; - - } - if(rightglove != inv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet)){ - rightglove = inv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_RightGauntlet); - apparelChanged = true; - - } - if(shirt != inv.getSlot(MWWorld::InventoryStore::Slot_Shirt)){ - shirt = inv.getSlot(MWWorld::InventoryStore::Slot_Shirt); - removePartGroup(MWWorld::InventoryStore::Slot_Shirt); - apparelChanged = true; - - } - if(pants != inv.getSlot(MWWorld::InventoryStore::Slot_Pants)){ - pants = inv.getSlot(MWWorld::InventoryStore::Slot_Pants); - removePartGroup(MWWorld::InventoryStore::Slot_Pants); - apparelChanged = true; + if(skirtiter != mInv.end()) + { + MWWorld::Ptr ptr = *skirtiter; + const ESM::Clothing *clothes = (ptr.get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts); + reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4); + reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4); + reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4); } - if(apparelChanged){ + if(helmet != mInv.end()) + { + removeIndividualPart(ESM::PRT_Hair); + const ESM::Armor *armor = (helmet->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts); - if(robe != inv.end()) + } + if(cuirass != mInv.end()) + { + const ESM::Armor *armor = (cuirass->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts); + + } + if(greaves != mInv.end()) + { + const ESM::Armor *armor = (greaves->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts); + } + + if(leftpauldron != mInv.end()) + { + const ESM::Armor *armor = (leftpauldron->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts); + + } + if(rightpauldron != mInv.end()) + { + const ESM::Armor *armor = (rightpauldron->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); + + } + if(!isBeast && boots != mInv.end()) + { + if(boots->getTypeName() == typeid(ESM::Clothing).name()) { - MWWorld::Ptr ptr = *robe; - - const ESM::Clothing *clothes = (ptr.get())->base; + const ESM::Clothing *clothes = (boots->get())->base; std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5); + addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts); } - if(skirtiter != inv.end()) + else if(boots->getTypeName() == typeid(ESM::Armor).name()) { - MWWorld::Ptr ptr = *skirtiter; + const ESM::Armor *armor = (boots->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts); + } - const ESM::Clothing *clothes = (ptr.get())->base; + } + if(leftglove != mInv.end()){ + if(leftglove->getTypeName() == typeid(ESM::Clothing).name()) + { + const ESM::Clothing *clothes = (leftglove->get())->base; std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4); + addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts); } - - if(helmet != inv.end()){ - removeIndividualPart(ESM::PRT_Hair); - const ESM::Armor *armor = (helmet->get())->base; + else + { + const ESM::Armor *armor = (leftglove->get())->base; std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts); - - } - if(cuirass != inv.end()){ - const ESM::Armor *armor = (cuirass->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts); - - } - if(greaves != inv.end()){ - const ESM::Armor *armor = (greaves->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts); - + addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts); } - if(leftpauldron != inv.end()){ - const ESM::Armor *armor = (leftpauldron->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts); - - } - if(rightpauldron != inv.end()){ - const ESM::Armor *armor = (rightpauldron->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); - - } - if(!isBeast && boots != inv.end()){ - if(boots->getTypeName() == typeid(ESM::Clothing).name()){ - const ESM::Clothing *clothes = (boots->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts); - } - else if(boots->getTypeName() == typeid(ESM::Armor).name()) - { - const ESM::Armor *armor = (boots->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts); - } - - } - if(leftglove != inv.end()){ - if(leftglove->getTypeName() == typeid(ESM::Clothing).name()){ - const ESM::Clothing *clothes = (leftglove->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (leftglove->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts); - } - - } - if(rightglove != inv.end()){ - if(rightglove->getTypeName() == typeid(ESM::Clothing).name()){ - const ESM::Clothing *clothes = (rightglove->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (rightglove->get())->base; - std::vector parts = armor->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts); - } - - } - - if(shirt != inv.end()){ - const ESM::Clothing *clothes = (shirt->get())->base; + } + if(rightglove != mInv.end()){ + if(rightglove->getTypeName() == typeid(ESM::Clothing).name()) + { + const ESM::Clothing *clothes = (rightglove->get())->base; std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts); + addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts); } - if(pants != inv.end()){ - const ESM::Clothing *clothes = (pants->get())->base; - std::vector parts = clothes->parts.parts; - addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts); + else + { + const ESM::Armor *armor = (rightglove->get())->base; + std::vector parts = armor->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts); } + } - if(partpriorities[ESM::PRT_Head] < 1){ - addOrReplaceIndividualPart(ESM::PRT_Head, -1,1,headModel); - } - if(partpriorities[ESM::PRT_Hair] < 1 && partpriorities[ESM::PRT_Head] <= 1){ - addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1,hairModel); - } - if(partpriorities[ESM::PRT_Neck] < 1){ - const ESM::BodyPart *neckPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "neck"); - if(neckPart) - addOrReplaceIndividualPart(ESM::PRT_Neck, -1,1,"meshes\\" + neckPart->model); - } - if(partpriorities[ESM::PRT_Cuirass] < 1){ - const ESM::BodyPart *chestPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "chest"); - if(chestPart) - addOrReplaceIndividualPart(ESM::PRT_Cuirass, -1,1,"meshes\\" + chestPart->model); - } - - if(partpriorities[ESM::PRT_Groin] < 1){ - const ESM::BodyPart *groinPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "groin"); - if(groinPart) - addOrReplaceIndividualPart(ESM::PRT_Groin, -1,1,"meshes\\" + groinPart->model); - } - if(partpriorities[ESM::PRT_RHand] < 1){ - const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); - if(!handPart) - handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); - if(handPart) - addOrReplaceIndividualPart(ESM::PRT_RHand, -1,1,"meshes\\" + handPart->model); - } - if(partpriorities[ESM::PRT_LHand] < 1){ - const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); - if(!handPart) - handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); - if(handPart) - addOrReplaceIndividualPart(ESM::PRT_LHand, -1,1,"meshes\\" + handPart->model); - } - - if(partpriorities[ESM::PRT_RWrist] < 1){ - const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); - if(wristPart) - addOrReplaceIndividualPart(ESM::PRT_RWrist, -1,1,"meshes\\" + wristPart->model); - } - if(partpriorities[ESM::PRT_LWrist] < 1){ - const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); - if(wristPart) - addOrReplaceIndividualPart(ESM::PRT_LWrist, -1,1,"meshes\\" + wristPart->model); - } - if(partpriorities[ESM::PRT_RForearm] < 1){ - const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); - if(bodyRaceID == "b_n_argonian_f_") - forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); - if(forearmPart) - addOrReplaceIndividualPart(ESM::PRT_RForearm, -1,1,"meshes\\" + forearmPart->model); - } - if(partpriorities[ESM::PRT_LForearm] < 1){ - const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); - if(bodyRaceID == "b_n_argonian_f_") - forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); - if(forearmPart) - addOrReplaceIndividualPart(ESM::PRT_LForearm, -1,1,"meshes\\" + forearmPart->model); - } - if(partpriorities[ESM::PRT_RUpperarm] < 1){ - const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); - if(armPart) - addOrReplaceIndividualPart(ESM::PRT_RUpperarm, -1,1,"meshes\\" + armPart->model); - } - if(partpriorities[ESM::PRT_LUpperarm] < 1){ - const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); - if(armPart) - addOrReplaceIndividualPart(ESM::PRT_LUpperarm, -1,1,"meshes\\" + armPart->model); - } - if(partpriorities[ESM::PRT_RFoot] < 1){ - const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); - if(isBeast && !footPart) - footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); - if(footPart) - addOrReplaceIndividualPart(ESM::PRT_RFoot, -1,1,"meshes\\" + footPart->model); - } - if(partpriorities[ESM::PRT_LFoot] < 1){ - const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); - if(isBeast && !footPart) - footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); - if(footPart) - addOrReplaceIndividualPart(ESM::PRT_LFoot, -1,1,"meshes\\" + footPart->model); - } - if(partpriorities[ESM::PRT_RAnkle] < 1){ - const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); - if(anklePart) - addOrReplaceIndividualPart(ESM::PRT_RAnkle, -1,1,"meshes\\" + anklePart->model); - } - if(partpriorities[ESM::PRT_LAnkle] < 1){ - const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); - if(anklePart) - addOrReplaceIndividualPart(ESM::PRT_LAnkle, -1,1,"meshes\\" + anklePart->model); - } - if(partpriorities[ESM::PRT_RKnee] < 1){ - const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); - if(kneePart) - addOrReplaceIndividualPart(ESM::PRT_RKnee, -1,1,"meshes\\" + kneePart->model); - } - if(partpriorities[ESM::PRT_LKnee] < 1){ - const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); - if(kneePart) - addOrReplaceIndividualPart(ESM::PRT_LKnee, -1,1,"meshes\\" + kneePart->model); - } - if(partpriorities[ESM::PRT_RLeg] < 1){ - const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); - if(legPart) - addOrReplaceIndividualPart(ESM::PRT_RLeg, -1,1,"meshes\\" + legPart->model); - } - if(partpriorities[ESM::PRT_LLeg] < 1){ - const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); - if(legPart) - addOrReplaceIndividualPart(ESM::PRT_LLeg, -1,1,"meshes\\" + legPart->model); - } - if(partpriorities[ESM::PRT_Tail] < 1){ - const ESM::BodyPart *tailPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "tail"); - if(tailPart) - addOrReplaceIndividualPart(ESM::PRT_Tail, -1,1,"meshes\\" + tailPart->model); - } - - - - + if(shirt != mInv.end()) + { + const ESM::Clothing *clothes = (shirt->get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts); + } + if(pants != mInv.end()) + { + const ESM::Clothing *clothes = (pants->get())->base; + std::vector parts = clothes->parts.parts; + addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts); + } + } + if(mPartPriorities[ESM::PRT_Head] < 1) + { + addOrReplaceIndividualPart(ESM::PRT_Head, -1,1,headModel); + } + if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1) + { + addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1,hairModel); + } + if(mPartPriorities[ESM::PRT_Neck] < 1) + { + const ESM::BodyPart *neckPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "neck"); + if(neckPart) + addOrReplaceIndividualPart(ESM::PRT_Neck, -1,1,"meshes\\" + neckPart->model); + } + if(mPartPriorities[ESM::PRT_Cuirass] < 1) + { + const ESM::BodyPart *chestPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "chest"); + if(chestPart) + addOrReplaceIndividualPart(ESM::PRT_Cuirass, -1,1,"meshes\\" + chestPart->model); + } + if(mPartPriorities[ESM::PRT_Groin] < 1) + { + const ESM::BodyPart *groinPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "groin"); + if(groinPart) + addOrReplaceIndividualPart(ESM::PRT_Groin, -1,1,"meshes\\" + groinPart->model); + } + if(mPartPriorities[ESM::PRT_RHand] < 1) + { + const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); + if(!handPart) + handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); + if(handPart) + addOrReplaceIndividualPart(ESM::PRT_RHand, -1,1,"meshes\\" + handPart->model); + } + if(mPartPriorities[ESM::PRT_LHand] < 1) + { + const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); + if(!handPart) + handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); + if(handPart) + addOrReplaceIndividualPart(ESM::PRT_LHand, -1,1,"meshes\\" + handPart->model); + } + if(mPartPriorities[ESM::PRT_RWrist] < 1) + { + const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); + if(wristPart) + addOrReplaceIndividualPart(ESM::PRT_RWrist, -1,1,"meshes\\" + wristPart->model); + } + if(mPartPriorities[ESM::PRT_LWrist] < 1) + { + const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); + if(wristPart) + addOrReplaceIndividualPart(ESM::PRT_LWrist, -1,1,"meshes\\" + wristPart->model); + } + if(mPartPriorities[ESM::PRT_RForearm] < 1) + { + const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); + if(bodyRaceID == "b_n_argonian_f_") + forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); + if(forearmPart) + addOrReplaceIndividualPart(ESM::PRT_RForearm, -1,1,"meshes\\" + forearmPart->model); + } + if(mPartPriorities[ESM::PRT_LForearm] < 1) + { + const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); + if(bodyRaceID == "b_n_argonian_f_") + forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); + if(forearmPart) + addOrReplaceIndividualPart(ESM::PRT_LForearm, -1,1,"meshes\\" + forearmPart->model); + } + if(mPartPriorities[ESM::PRT_RUpperarm] < 1) + { + const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); + if(armPart) + addOrReplaceIndividualPart(ESM::PRT_RUpperarm, -1,1,"meshes\\" + armPart->model); + } + if(mPartPriorities[ESM::PRT_LUpperarm] < 1) + { + const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); + if(armPart) + addOrReplaceIndividualPart(ESM::PRT_LUpperarm, -1,1,"meshes\\" + armPart->model); + } + if(mPartPriorities[ESM::PRT_RFoot] < 1) + { + const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); + if(isBeast && !footPart) + footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); + if(footPart) + addOrReplaceIndividualPart(ESM::PRT_RFoot, -1,1,"meshes\\" + footPart->model); + } + if(mPartPriorities[ESM::PRT_LFoot] < 1) + { + const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); + if(isBeast && !footPart) + footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); + if(footPart) + addOrReplaceIndividualPart(ESM::PRT_LFoot, -1,1,"meshes\\" + footPart->model); + } + if(mPartPriorities[ESM::PRT_RAnkle] < 1) + { + const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); + if(anklePart) + addOrReplaceIndividualPart(ESM::PRT_RAnkle, -1,1,"meshes\\" + anklePart->model); + } + if(mPartPriorities[ESM::PRT_LAnkle] < 1) + { + const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); + if(anklePart) + addOrReplaceIndividualPart(ESM::PRT_LAnkle, -1,1,"meshes\\" + anklePart->model); + } + if(mPartPriorities[ESM::PRT_RKnee] < 1) + { + const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); + if(kneePart) + addOrReplaceIndividualPart(ESM::PRT_RKnee, -1,1,"meshes\\" + kneePart->model); + } + if(mPartPriorities[ESM::PRT_LKnee] < 1) + { + const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); + if(kneePart) + addOrReplaceIndividualPart(ESM::PRT_LKnee, -1,1,"meshes\\" + kneePart->model); + } + if(mPartPriorities[ESM::PRT_RLeg] < 1) + { + const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); + if(legPart) + addOrReplaceIndividualPart(ESM::PRT_RLeg, -1,1,"meshes\\" + legPart->model); + } + if(mPartPriorities[ESM::PRT_LLeg] < 1) + { + const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); + if(legPart) + addOrReplaceIndividualPart(ESM::PRT_LLeg, -1,1,"meshes\\" + legPart->model); + } + if(mPartPriorities[ESM::PRT_Tail] < 1) + { + const ESM::BodyPart *tailPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "tail"); + if(tailPart) + addOrReplaceIndividualPart(ESM::PRT_Tail, -1,1,"meshes\\" + tailPart->model); + } } -Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, std::string bonename){ - +Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, std::string bonename) +{ NIFLoader::load(mesh); Ogre::Entity* part = mRend.getScene()->createEntity(mesh); part->setVisibilityFlags(RV_Actors); - base->attachObjectToBone(bonename, part); + mBase->attachObjectToBone(bonename, part); return part; } -void NpcAnimation::insertFootPart(int type, const std::string &mesh){ +void NpcAnimation::insertFootPart(int type, const std::string &mesh) +{ std::string meshAndSuffix = mesh; if(type == ESM::PRT_LFoot) meshAndSuffix += "*|"; NIFLoader::load(meshAndSuffix); Ogre::Entity* part = mRend.getScene()->createEntity(meshAndSuffix); std::vector* shape = ((NIFLoader::getSingletonPtr())->getShapes(meshAndSuffix)); - if(shape == 0){ - if(type == ESM::PRT_LFoot){ - base->attachObjectToBone("Left Foot", part); + if(shape == 0) + { + if(type == ESM::PRT_LFoot) + { + mBase->attachObjectToBone("Left Foot", part); lfoot = part; } - else if (type == ESM::PRT_RFoot){ - base->attachObjectToBone("Right Foot", part); + else if (type == ESM::PRT_RFoot) + { + mBase->attachObjectToBone("Right Foot", part); rfoot = part; } } - else{ + else + { if(type == ESM::PRT_LFoot) - lFreeFoot = insertFreePart(mesh, "::"); + mLFreeFoot = insertFreePart(mesh, "::"); else if (type == ESM::PRT_RFoot) - rFreeFoot = insertFreePart(mesh, ":<"); + mRFreeFoot = insertFreePart(mesh, ":<"); } - - } -std::pair*> NpcAnimation::insertFreePart(const std::string &mesh, const std::string& suffix){ +std::pair*> NpcAnimation::insertFreePart(const std::string &mesh, const std::string& suffix) +{ std::string meshNumbered = mesh + getUniqueID(mesh + suffix) + suffix; NIFLoader::load(meshNumbered); Ogre::Entity* part = mRend.getScene()->createEntity(meshNumbered); part->setVisibilityFlags(RV_Actors); - insert->attachObject(part); + mInsert->attachObject(part); std::vector* shape = ((NIFLoader::getSingletonPtr())->getShapes(mesh + "0000" + suffix)); - if(shape){ - handleShapes(shape, part, base->getSkeleton()); - } - std::pair*> pair = std::make_pair(part, shape); - return pair; + if(shape) + handleShapes(shape, part, mBase->getSkeleton()); + std::pair*> pair = std::make_pair(part, shape); + return pair; } +void NpcAnimation::runAnimation(float timepassed) +{ + if(timeToChange > .2){ + timeToChange = 0; + updateParts(); + } - -void NpcAnimation::runAnimation(float timepassed){ - - if(timeToChange > .2){ - - timeToChange = 0; - - updateParts(); - } - - timeToChange += timepassed; + timeToChange += timepassed; //1. Add the amount of time passed to time - //2. Handle the animation transforms dependent on time + //2. Handle the animation transforms dependent on time - //3. Handle the shapes dependent on animation transforms - if(animate > 0){ - time += timepassed; + //3. Handle the shapes dependent on animation transforms + if(mAnimate > 0) + { + mTime += timepassed; - if(time > stopTime){ - animate--; + if(mTime > mStopTime) + { + mAnimate--; - if(animate == 0) - time = stopTime; + if(mAnimate == 0) + mTime = mStopTime; else - time = startTime + (time - stopTime); + mTime = mStartTime + (mTime - mStopTime); } - handleAnimationTransforms(); + handleAnimationTransforms(); + mVecRotPos.clear(); - vecRotPos.clear(); + if(mLFreeFoot.first) + handleShapes(mLFreeFoot.second, mLFreeFoot.first, mBase->getSkeleton()); + if(mRFreeFoot.first) + handleShapes(mRFreeFoot.second, mRFreeFoot.first, mBase->getSkeleton()); + if(mChest.first) + handleShapes(mChest.second, mChest.first, mBase->getSkeleton()); + if(mTail.first) + handleShapes(mTail.second, mTail.first, mBase->getSkeleton()); + if(mSkirt.first) + handleShapes(mSkirt.second, mSkirt.first, mBase->getSkeleton()); + if(mLhand.first) + handleShapes(mLhand.second, mLhand.first, mBase->getSkeleton()); + if(mRhand.first) + handleShapes(mRhand.second, mRhand.first, mBase->getSkeleton()); - if(lFreeFoot.first) - handleShapes(lFreeFoot.second, lFreeFoot.first, base->getSkeleton()); - if(rFreeFoot.first) - handleShapes(rFreeFoot.second, rFreeFoot.first, base->getSkeleton()); - - if(chest.first) - handleShapes(chest.second, chest.first, base->getSkeleton()); - if(tail.first) - handleShapes(tail.second, tail.first, base->getSkeleton()); - if(skirt.first){ - handleShapes(skirt.second, skirt.first, base->getSkeleton()); - } - if(lhand.first) - handleShapes(lhand.second, lhand.first, base->getSkeleton()); - if(rhand.first) - handleShapes(rhand.second, rhand.first, base->getSkeleton()); - -} + } } void NpcAnimation::removeIndividualPart(int type){ - partpriorities[type] = 0; - partslots[type] = -1; + mPartPriorities[type] = 0; + mPartslots[type] = -1; - if(type == ESM::PRT_Head && head){ //0 - base->detachObjectFromBone(head); + if(type == ESM::PRT_Head && head) //0 + { + mBase->detachObjectFromBone(head); head = 0; } - else if(type == ESM::PRT_Hair && hair){//1 - base->detachObjectFromBone(hair); + else if(type == ESM::PRT_Hair && hair) //1 + { + mBase->detachObjectFromBone(hair); hair = 0; } - else if(type == ESM::PRT_Neck && neck){//2 - base->detachObjectFromBone(neck); + else if(type == ESM::PRT_Neck && neck) //2 + { + mBase->detachObjectFromBone(neck); neck = 0; } - else if(type == ESM::PRT_Cuirass && chest.first){//3 - insert->detachObject(chest.first); - chest = zero; + else if(type == ESM::PRT_Cuirass && mChest.first) //3 + { + mInsert->detachObject(mChest.first); + mChest = mZero; } - else if(type == ESM::PRT_Groin && groin){//4 - base->detachObjectFromBone(groin); + else if(type == ESM::PRT_Groin && groin) //4 + { + mBase->detachObjectFromBone(groin); groin = 0; } - else if(type == ESM::PRT_Skirt && skirt.first){//5 - insert->detachObject(skirt.first); - skirt = zero; + else if(type == ESM::PRT_Skirt && mSkirt.first) //5 + { + mInsert->detachObject(mSkirt.first); + mSkirt = mZero; } - else if(type == ESM::PRT_RHand && rhand.first){//6 - insert->detachObject(rhand.first); - rhand = zero; + else if(type == ESM::PRT_RHand && mRhand.first) //6 + { + mInsert->detachObject(mRhand.first); + mRhand = mZero; } - else if(type == ESM::PRT_LHand && lhand.first){//7 - insert->detachObject(lhand.first); - lhand = zero; + else if(type == ESM::PRT_LHand && mLhand.first) //7 + { + mInsert->detachObject(mLhand.first); + mLhand = mZero; } - else if(type == ESM::PRT_RWrist && rWrist){//8 - base->detachObjectFromBone(rWrist); + else if(type == ESM::PRT_RWrist && rWrist) //8 + { + mBase->detachObjectFromBone(rWrist); rWrist = 0; } - else if(type == ESM::PRT_LWrist && lWrist){//9 - base->detachObjectFromBone(lWrist); + else if(type == ESM::PRT_LWrist && lWrist) //9 + { + mBase->detachObjectFromBone(lWrist); lWrist = 0; } - else if(type == ESM::PRT_Shield){//10 + else if(type == ESM::PRT_Shield) //10 + { } - else if(type == ESM::PRT_RForearm && rForearm){//11 - base->detachObjectFromBone(rForearm); + else if(type == ESM::PRT_RForearm && rForearm) //11 + { + mBase->detachObjectFromBone(rForearm); rForearm = 0; } - else if(type == ESM::PRT_LForearm && lForearm){//12 - base->detachObjectFromBone(lForearm); + else if(type == ESM::PRT_LForearm && lForearm) //12 + { + mBase->detachObjectFromBone(lForearm); lForearm = 0; } - else if(type == ESM::PRT_RUpperarm && rupperArm){//13 - base->detachObjectFromBone(rupperArm); + else if(type == ESM::PRT_RUpperarm && rupperArm) //13 + { + mBase->detachObjectFromBone(rupperArm); rupperArm = 0; } - else if(type == ESM::PRT_LUpperarm && lupperArm){//14 - base->detachObjectFromBone(lupperArm); + else if(type == ESM::PRT_LUpperarm && lupperArm) //14 + { + mBase->detachObjectFromBone(lupperArm); lupperArm = 0; } - else if(type == ESM::PRT_RFoot){ //15 - if(rfoot){ - base->detachObjectFromBone(rfoot); + else if(type == ESM::PRT_RFoot) //15 + { + if(rfoot) + { + mBase->detachObjectFromBone(rfoot); rfoot = 0; } - else if(rFreeFoot.first){ - insert->detachObject(rFreeFoot.first); - rFreeFoot = zero; + else if(mRFreeFoot.first) + { + mInsert->detachObject(mRFreeFoot.first); + mRFreeFoot = mZero; } } - else if(type == ESM::PRT_LFoot){ //16 - if(lfoot){ - base->detachObjectFromBone(lfoot); + else if(type == ESM::PRT_LFoot) //16 + { + if(lfoot) + { + mBase->detachObjectFromBone(lfoot); lfoot = 0; } - else if(lFreeFoot.first){ - insert->detachObject(lFreeFoot.first); - lFreeFoot = zero; + else if(mLFreeFoot.first) + { + mInsert->detachObject(mLFreeFoot.first); + mLFreeFoot = mZero; } } - else if(type == ESM::PRT_RAnkle && rAnkle){ //17 - base->detachObjectFromBone(rAnkle); + else if(type == ESM::PRT_RAnkle && rAnkle) //17 + { + mBase->detachObjectFromBone(rAnkle); rAnkle = 0; } - else if(type == ESM::PRT_LAnkle && lAnkle){ //18 - base->detachObjectFromBone(lAnkle); + else if(type == ESM::PRT_LAnkle && lAnkle) //18 + { + mBase->detachObjectFromBone(lAnkle); lAnkle = 0; } - else if(type == ESM::PRT_RKnee && rKnee){ //19 - base->detachObjectFromBone(rKnee); + else if(type == ESM::PRT_RKnee && rKnee) //19 + { + mBase->detachObjectFromBone(rKnee); rKnee = 0; } - else if(type == ESM::PRT_LKnee && lKnee){ //20 - base->detachObjectFromBone(lKnee); + else if(type == ESM::PRT_LKnee && lKnee) //20 + { + mBase->detachObjectFromBone(lKnee); lKnee = 0; } - else if(type == ESM::PRT_RLeg && rUpperLeg){ //21 - base->detachObjectFromBone(rUpperLeg); + else if(type == ESM::PRT_RLeg && rUpperLeg) //21 + { + mBase->detachObjectFromBone(rUpperLeg); rUpperLeg = 0; } - else if(type == ESM::PRT_LLeg && lUpperLeg){ //22 - base->detachObjectFromBone(lUpperLeg); + else if(type == ESM::PRT_LLeg && lUpperLeg) //22 + { + mBase->detachObjectFromBone(lUpperLeg); lUpperLeg = 0; } - else if(type == ESM::PRT_RPauldron && rclavicle){ //23 - base->detachObjectFromBone(rclavicle); + else if(type == ESM::PRT_RPauldron && rclavicle) //23 + { + mBase->detachObjectFromBone(rclavicle); rclavicle = 0; } - else if(type == ESM::PRT_LPauldron && lclavicle){ //24 - base->detachObjectFromBone(lclavicle); + else if(type == ESM::PRT_LPauldron && lclavicle) //24 + { + mBase->detachObjectFromBone(lclavicle); lclavicle = 0; } - else if(type == ESM::PRT_Weapon){ //25 + else if(type == ESM::PRT_Weapon) //25 + { } - else if(type == ESM::PRT_Tail && tail.first){ //26 - insert->detachObject(tail.first); - tail = zero; + else if(type == ESM::PRT_Tail && mTail.first) //26 + { + mInsert->detachObject(mTail.first); + mTail = mZero; } - - - } - void NpcAnimation::reserveIndividualPart(int type, int group, int priority){ - if(priority > partpriorities[type]){ + void NpcAnimation::reserveIndividualPart(int type, int group, int priority) + { + if(priority > mPartPriorities[type]) + { removeIndividualPart(type); - partpriorities[type] = priority; - partslots[type] = group; + mPartPriorities[type] = priority; + mPartslots[type] = group; } } - void NpcAnimation::removePartGroup(int group){ - for(int i = 0; i < 27; i++){ - if(partslots[i] == group){ + void NpcAnimation::removePartGroup(int group) + { + for(int i = 0; i < 27; i++) + if(mPartslots[i] == group) removeIndividualPart(i); - } - } } - bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh){ - if(priority > partpriorities[type]){ + bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh) + { + if(priority > mPartPriorities[type]) + { removeIndividualPart(type); - partslots[type] = group; - partpriorities[type] = priority; - switch(type){ + mPartslots[type] = group; + mPartPriorities[type] = priority; + switch(type) + { case ESM::PRT_Head: //0 head = insertBoundedPart(mesh, "Head"); break; @@ -769,19 +837,19 @@ void NpcAnimation::removeIndividualPart(int type){ neck = insertBoundedPart(mesh, "Neck"); break; case ESM::PRT_Cuirass: //3 - chest = insertFreePart(mesh, ":\""); + mChest = insertFreePart(mesh, ":\""); break; case ESM::PRT_Groin: //4 groin = insertBoundedPart(mesh, "Groin"); break; case ESM::PRT_Skirt: //5 - skirt = insertFreePart(mesh, ":|"); + mSkirt = insertFreePart(mesh, ":|"); break; case ESM::PRT_RHand: //6 - rhand = insertFreePart(mesh, ":?"); + mRhand = insertFreePart(mesh, ":?"); break; case ESM::PRT_LHand: //7 - lhand = insertFreePart(mesh, ":>"); + mLhand = insertFreePart(mesh, ":>"); break; case ESM::PRT_RWrist: //8 rWrist = insertBoundedPart(mesh, "Right Wrist"); @@ -836,33 +904,30 @@ void NpcAnimation::removeIndividualPart(int type){ case ESM::PRT_Weapon: //25 break; case ESM::PRT_Tail: //26 - tail = insertFreePart(mesh, ":*"); + mTail = insertFreePart(mesh, ":*"); break; - - } return true; } return false; } - void NpcAnimation::addPartGroup(int group, int priority, std::vector& parts){ + void NpcAnimation::addPartGroup(int group, int priority, std::vector& parts) + { for(std::size_t i = 0; i < parts.size(); i++) - { - ESM::PartReference part = parts[i]; + { + ESM::PartReference part = parts[i]; - const ESM::BodyPart *bodypart = 0; + const ESM::BodyPart *bodypart = 0; - if(isFemale) - bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.female); - if(!bodypart) - bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.male); - if(bodypart){ - addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); - } - else - reserveIndividualPart(part.part, group, priority); - - } + if(isFemale) + bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.female); + if(!bodypart) + bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.male); + if(bodypart) + addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); + else + reserveIndividualPart(part.part, group, priority); + } } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 8f4f8181d..973549619 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -20,54 +20,52 @@ namespace MWRender{ class NpcAnimation: public Animation{ private: - MWWorld::InventoryStore& inv; - int mStateID; - //Free Parts - std::pair*> chest; - std::pair*> skirt; - std::pair*> lhand; - std::pair*> rhand; - std::pair*> tail; - std::pair*> lFreeFoot; - std::pair*> rFreeFoot; + MWWorld::InventoryStore& mInv; + int mStateID; + //Free Parts + std::pair*> mChest; + std::pair*> mSkirt; + std::pair*> mLhand; + std::pair*> mRhand; + std::pair*> mTail; + std::pair*> mLFreeFoot; + std::pair*> mRFreeFoot; - int partslots[27]; //Each part slot is taken by clothing, armor, or is empty - int partpriorities[27]; - std::pair*> zero; + int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty + int mPartPriorities[27]; + std::pair*> mZero; + //Bounded Parts + Ogre::Entity* lclavicle; + Ogre::Entity* rclavicle; + Ogre::Entity* rupperArm; + Ogre::Entity* lupperArm; + Ogre::Entity* rUpperLeg; + Ogre::Entity* lUpperLeg; + Ogre::Entity* lForearm; + Ogre::Entity* rForearm; + Ogre::Entity* lWrist; + Ogre::Entity* rWrist; + Ogre::Entity* rKnee; + Ogre::Entity* lKnee; + Ogre::Entity* neck; + Ogre::Entity* rAnkle; + Ogre::Entity* lAnkle; + Ogre::Entity* groin; + Ogre::Entity* lfoot; + Ogre::Entity* rfoot; + Ogre::Entity* hair; + Ogre::Entity* head; - - //Bounded Parts - Ogre::Entity* lclavicle; - Ogre::Entity* rclavicle; - Ogre::Entity* rupperArm; - Ogre::Entity* lupperArm; - Ogre::Entity* rUpperLeg; - Ogre::Entity* lUpperLeg; - Ogre::Entity* lForearm; - Ogre::Entity* rForearm; - Ogre::Entity* lWrist; - Ogre::Entity* rWrist; - Ogre::Entity* rKnee; - Ogre::Entity* lKnee; - Ogre::Entity* neck; - Ogre::Entity* rAnkle; - Ogre::Entity* lAnkle; - Ogre::Entity* groin; - Ogre::Entity* lfoot; - Ogre::Entity* rfoot; - Ogre::Entity* hair; - Ogre::Entity* head; - - Ogre::SceneNode* insert; + Ogre::SceneNode* insert; bool isBeast; bool isFemale; - std::string headModel; - std::string hairModel; - std::string npcName; - std::string bodyRaceID; - float timeToChange; - MWWorld::ContainerStoreIterator robe; + std::string headModel; + std::string hairModel; + std::string npcName; + std::string bodyRaceID; + float timeToChange; + MWWorld::ContainerStoreIterator robe; MWWorld::ContainerStoreIterator helmet; MWWorld::ContainerStoreIterator shirt; MWWorld::ContainerStoreIterator cuirass; @@ -80,22 +78,21 @@ private: MWWorld::ContainerStoreIterator rightglove; MWWorld::ContainerStoreIterator skirtiter; - public: - NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); - virtual ~NpcAnimation(); +public: + NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); + virtual ~NpcAnimation(); Ogre::Entity* insertBoundedPart(const std::string &mesh, std::string bonename); - std::pair*> insertFreePart(const std::string &mesh, const std::string& suffix); - void insertFootPart(int type, const std::string &mesh); - virtual void runAnimation(float timepassed); - void updateParts(); + std::pair*> insertFreePart(const std::string &mesh, const std::string& suffix); + void insertFootPart(int type, const std::string &mesh); + virtual void runAnimation(float timepassed); + void updateParts(); void removeIndividualPart(int type); void reserveIndividualPart(int type, int group, int priority); bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); - void removePartGroup(int group); + void removePartGroup(int group); void addPartGroup(int group, int priority, std::vector& parts); - }; } #endif From 182017b8e92c6259bed6ceab4f47b098152a671f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 13 Jul 2012 13:01:30 +0200 Subject: [PATCH 124/688] Issue #314: Moved ingredients and potions to a different type of record list --- apps/esmtool/esmtool.cpp | 2 +- components/esm/loadalch.cpp | 4 +++- components/esm/loadalch.hpp | 4 +++- components/esm/loadingr.cpp | 4 +++- components/esm/loadingr.hpp | 4 +++- components/esm_store/store.hpp | 4 ++-- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index f417d5c60..4888d3ceb 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -187,7 +187,7 @@ int main(int argc, char**argv) case REC_ALCH: { Potion p; - p.load(esm); + p.load(esm, id); if(quiet) break; cout << " Name: " << p.name << endl; break; diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index d3bc36a77..cd36887b0 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -2,8 +2,10 @@ namespace ESM { -void Potion::load(ESMReader &esm) +void Potion::load(ESMReader &esm, const std::string& id) { + mId = id; + model = esm.getHNString("MODL"); icon = esm.getHNOString("TEXT"); // not ITEX here for some reason script = esm.getHNOString("SCRI"); diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index c21e5dea0..6917fb448 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -24,7 +24,9 @@ struct Potion std::string name, model, icon, script; EffectList effects; - void load(ESMReader &esm); + std::string mId; + + void load(ESMReader &esm, const std::string& id); }; } #endif diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index 471f71780..a745ff669 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -3,8 +3,10 @@ namespace ESM { -void Ingredient::load(ESMReader &esm) +void Ingredient::load(ESMReader &esm, const std::string& id) { + mId = id; + model = esm.getHNString("MODL"); name = esm.getHNString("FNAM"); esm.getHNT(data, "IRDT", 56); diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index af9599ed0..5c1c3bd75 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -24,7 +24,9 @@ struct Ingredient IRDTstruct data; std::string name, model, icon, script; - void load(ESMReader &esm); + std::string mId; + + void load(ESMReader &esm, const std::string& id); }; } #endif diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp index 507196a86..991925bd4 100644 --- a/components/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -30,7 +30,7 @@ namespace ESMS // Each individual list RecListT activators; - RecListT potions; + RecListWithIDT potions; RecListT appas; RecListT armors; RecListT bodyParts; @@ -47,7 +47,7 @@ namespace ESMS RecListT enchants; RecListT factions; RecListT globals; - RecListT ingreds; + RecListWithIDT ingreds; RecListT creatureLists; RecListT itemLists; RecListT lights; From 782d417d5bd17a45ed202756f9dac795dd6f446a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 13 Jul 2012 13:09:22 +0200 Subject: [PATCH 125/688] Issue #314: potion usage --- apps/openmw/mwclass/potion.cpp | 12 ++++++++++++ apps/openmw/mwclass/potion.hpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 5b446fbe9..d3d615262 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/actionapply.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" @@ -144,4 +145,15 @@ namespace MWClass return info; } + + boost::shared_ptr Potion::use (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + ptr.getRefData().setCount (ptr.getRefData().getCount()-1); + + return boost::shared_ptr ( + new MWWorld::ActionApply (ptr, ref->base->mId, ptr)); + } } diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 74779864a..ac5d85f23 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -34,6 +34,9 @@ namespace MWClass virtual int getValue (const MWWorld::Ptr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; + ///< Generate action for using via inventory menu + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; From 573d7e7e45c2b823a3f471157c917b712e1301a5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 13 Jul 2012 15:51:57 +0200 Subject: [PATCH 126/688] Issue #314: fix to potion use function (was mixing up potion with potion user) --- apps/openmw/mwclass/potion.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index d3d615262..45cb07840 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -11,6 +11,7 @@ #include "../mwworld/actionapply.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/player.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -153,7 +154,9 @@ namespace MWClass ptr.getRefData().setCount (ptr.getRefData().getCount()-1); + MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + return boost::shared_ptr ( - new MWWorld::ActionApply (ptr, ref->base->mId, ptr)); + new MWWorld::ActionApply (actor, ref->base->mId, actor)); } } From 9436ca4b0cea93fcd53cba5884d360518cfe1bf9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 13 Jul 2012 09:32:58 -0700 Subject: [PATCH 127/688] Use vectors for ShapeData properties --- components/nif/data.hpp | 16 ++++++++----- components/nif/nif_file.hpp | 26 ++++++++++++++++++++++ components/nifbullet/bullet_nif_loader.cpp | 17 ++++++-------- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 208337f35..d08ba7b32 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -96,7 +96,9 @@ public: class ShapeData : public Record { public: - std::vector vertices, normals, colors, uvlist; + std::vector vertices, normals; + std::vector colors; + std::vector< std::vector > uvlist; Ogre::Vector3 center; float radius; @@ -105,16 +107,16 @@ public: int verts = nif->getUShort(); if(nif->getInt()) - nif->getFloats(vertices, verts*3); + nif->getVector3s(vertices, verts); if(nif->getInt()) - nif->getFloats(normals, verts*3); + nif->getVector3s(normals, verts); center = nif->getVector3(); radius = nif->getFloat(); if(nif->getInt()) - nif->getFloats(colors, verts*4); + nif->getVector4s(colors, verts); // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. @@ -122,7 +124,11 @@ public: uvs &= 0x3f; if(nif->getInt()) - nif->getFloats(uvlist, uvs*verts*2); + { + uvlist.resize(uvs); + for(int i = 0;i < uvs;i++) + nif->getVector2s(uvlist[i], verts); + } } }; diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index c33790742..f6c23ec9a 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -142,6 +143,13 @@ public: unsigned short getUShort() { return read_le16(); } int getInt() { return read_le32(); } float getFloat() { return read_le32f(); } + Ogre::Vector2 getVector2() + { + float a[2]; + for(size_t i = 0;i < 2;i++) + a[i] = getFloat(); + return Ogre::Vector2(a); + } Ogre::Vector3 getVector3() { float a[3]; @@ -208,6 +216,24 @@ public: for(size_t i = 0;i < vec.size();i++) vec[i] = getFloat(); } + void getVector2s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector2(); + } + void getVector3s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector3(); + } + void getVector4s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector4(); + } }; diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 5f562504b..bdf508f6c 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -271,19 +271,16 @@ void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape *shape, int flags Nif::NiTriShapeData *data = shape->data.getPtr(); - float* vertices = &data->vertices[0]; - short* triangles = &data->triangles[0]; + const std::vector &vertices = data->vertices; const Ogre::Matrix3 &rot = shape->trafo.rotation; const Ogre::Vector3 &pos = shape->trafo.pos; - float scale = shape->trafo.scale; - for(unsigned int i=0; i < data->triangles.size(); i = i+3) + float scale = shape->trafo.scale * parentScale; + short* triangles = &data->triangles[0]; + for(size_t i = 0;i < data->triangles.size();i+=3) { - Ogre::Vector3 b1(vertices[triangles[i+0]*3]*parentScale,vertices[triangles[i+0]*3+1]*parentScale,vertices[triangles[i+0]*3+2]*parentScale); - Ogre::Vector3 b2(vertices[triangles[i+1]*3]*parentScale,vertices[triangles[i+1]*3+1]*parentScale,vertices[triangles[i+1]*3+2]*parentScale); - Ogre::Vector3 b3(vertices[triangles[i+2]*3]*parentScale,vertices[triangles[i+2]*3+1]*parentScale,vertices[triangles[i+2]*3+2]*parentScale); - b1 = pos + rot*b1*scale; - b2 = pos + rot*b2*scale; - b3 = pos + rot*b3*scale; + Ogre::Vector3 b1 = pos + rot*vertices[triangles[i+0]]*scale; + Ogre::Vector3 b2 = pos + rot*vertices[triangles[i+1]]*scale; + Ogre::Vector3 b3 = pos + rot*vertices[triangles[i+2]]*scale; mTriMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); } } From fdfe40a55a3bf0744cb7fb3a6d548c0eb35e34e5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 13 Jul 2012 13:41:08 -0700 Subject: [PATCH 128/688] Use a different loader object for each NIF mesh --- components/nifogre/ogre_nif_loader.cpp | 6 ++++-- components/nifogre/ogre_nif_loader.hpp | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 883cf7fce..efe9ddea6 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -128,6 +128,8 @@ public: }; +NIFLoader::LoaderMap NIFLoader::sLoaders; + void NIFLoader::warn(const std::string &msg) { std::cerr << "NIFLoader: Warn:" << msg << "\n"; @@ -331,8 +333,8 @@ Ogre::MeshPtr NIFLoader::load(const std::string &name, const std::string &group) Ogre::MeshPtr themesh = meshMgr.getByName(name, group); if(themesh.isNull()) { - static NIFLoader loader; - themesh = meshMgr.createManual(name, group, &loader); + NIFLoader *loader = &sLoaders[name]; + themesh = meshMgr.createManual(name, group, loader); } return themesh; } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index bc1ef304c..985c64e0d 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -41,7 +41,7 @@ #include #include -#include +#include // For warning messages #include using namespace boost::algorithm; @@ -90,11 +90,11 @@ public: const std::string &group="General"); private: - NIFLoader() {} - NIFLoader(NIFLoader& n) {} - void warn(const std::string &msg); void fail(const std::string &msg); + + typedef std::map LoaderMap; + static LoaderMap sLoaders; }; } From 939d0d2fc5b4a1a8d78e708ed7c519a2ca5416e2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 13 Jul 2012 18:25:35 -0700 Subject: [PATCH 129/688] Return a list of meshes and the skeleton from NIFLoader::load --- apps/openmw/mwrender/creatureanimation.cpp | 5 +- apps/openmw/mwrender/npcanimation.cpp | 12 +- apps/openmw/mwrender/objects.cpp | 5 +- apps/openmw/mwrender/sky.cpp | 206 +++++++++++---------- apps/openmw/mwrender/sky.hpp | 5 +- components/nifogre/ogre_nif_loader.cpp | 12 +- components/nifogre/ogre_nif_loader.hpp | 20 +- 7 files changed, 146 insertions(+), 119 deletions(-) diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 6bdd58ac3..0690a7be7 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,8 +26,9 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O { std::string mesh = "meshes\\" + ref->base->model; - NifOgre::NIFLoader::load(mesh); - base = mRend.getScene()->createEntity(mesh); + // FIXME: There can be more than one! + NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(mesh); + base = mRend.getScene()->createEntity(meshes[0].first->getName()); base->setVisibilityFlags(RV_Actors); bool transparent = false; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index fa33d18ff..d6600bc1b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -82,8 +82,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere assert(insert); std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - NifOgre::NIFLoader::load(smodel); - base = mRend.getScene()->createEntity(smodel); + + // FIXME: There can be more than one! + NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(smodel); + base = mRend.getScene()->createEntity(meshes[0].first->getName()); base->setVisibilityFlags(RV_Actors); bool transparent = false; @@ -382,9 +384,9 @@ void NpcAnimation::updateParts() Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) { - - NIFLoader::load(mesh); - Ogre::Entity* part = mRend.getScene()->createEntity(mesh); + // FIXME: There can be more than one! + NifOgre::MeshPairList meshes = NIFLoader::load(mesh); + Ogre::Entity* part = mRend.getScene()->createEntity(meshes[0].first->getName()); part->setVisibilityFlags(RV_Actors); base->attachObjectToBone(bonename, part); diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index fb2bfb3c5..ea58ec716 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -92,8 +92,9 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); - NifOgre::NIFLoader::load(mesh); - Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh); + // FIXME: There can be more than one! + NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(mesh); + Ogre::Entity *ent = mRenderer.getScene()->createEntity(meshes[0].first->getName()); Ogre::Vector3 extents = ent->getBoundingBox().getSize(); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 45e48240b..8f49e03df 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -453,16 +453,6 @@ void SkyManager::create() HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); // Stars - /// \todo sky_night_02.nif (available in Bloodmoon) - MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); - Entity* night1_ent = mSceneMgr->createEntity("meshes\\sky_night_01.nif"); - night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); - night1_ent->setVisibilityFlags(RV_Sky); - night1_ent->setCastShadows(false); - - mAtmosphereNight = mRootNode->createChildSceneNode(); - mAtmosphereNight->attachObject(night1_ent); - // Stars vertex shader HighLevelGpuProgramPtr stars_vp = mgr.createProgram("Stars_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); @@ -517,35 +507,35 @@ void SkyManager::create() stars_fp->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); stars_fp->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - for (unsigned int i=0; igetNumSubEntities(); ++i) + /// \todo sky_night_02.nif (available in Bloodmoon) + mAtmosphereNight = mRootNode->createChildSceneNode(); + NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); + for(size_t i = 0;i < meshes.size();i++) { - MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); - mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); - mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); - mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0); - mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); - mp->getTechnique(0)->getPass(0)->setVertexProgram(stars_vp->getName()); - mp->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName()); - mp->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); - mStarsMaterials[i] = mp; + Entity* night1_ent = mSceneMgr->createEntity(meshes[i].first->getName()); + night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); + night1_ent->setVisibilityFlags(RV_Sky); + night1_ent->setCastShadows(false); + + mAtmosphereNight->attachObject(night1_ent); + + for (unsigned int i=0; igetNumSubEntities(); ++i) + { + MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); + mp->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); + mp->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); + mp->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 1.0); + mp->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mp->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mp->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mp->getTechnique(0)->getPass(0)->setVertexProgram(stars_vp->getName()); + mp->getTechnique(0)->getPass(0)->setFragmentProgram(stars_fp->getName()); + mp->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); + mStarsMaterials.push_back(mp); + } } // Atmosphere (day) - mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); - Entity* atmosphere_ent = mSceneMgr->createEntity("meshes\\sky_atmosphere.nif"); - atmosphere_ent->setCastShadows(false); - - ModVertexAlpha(atmosphere_ent, 0); - - atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); - atmosphere_ent->setVisibilityFlags(RV_Sky); - mAtmosphereDay = mRootNode->createChildSceneNode(); - mAtmosphereDay->attachObject(atmosphere_ent); - mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); - // Atmosphere shader HighLevelGpuProgramPtr vshader = mgr.createProgram("Atmosphere_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); @@ -555,22 +545,21 @@ void SkyManager::create() StringUtil::StrStreamType outStream; outStream << - "void main_vp( \n" - " float4 position : POSITION, \n" - " in float4 color : COLOR, \n" - " out float4 oPosition : POSITION, \n" - " out float4 oVertexColor : TEXCOORD0, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" - " oPosition = mul( worldViewProj, position ); \n" + "void main_vp( \n" + " float4 position : POSITION, \n" + " in float4 color : COLOR, \n" + " out float4 oPosition : POSITION, \n" + " out float4 oVertexColor : TEXCOORD0, \n" + " uniform float4x4 worldViewProj \n" + ") \n" + "{ \n" + " oPosition = mul( worldViewProj, position ); \n" " oVertexColor = color; \n" "}"; vshader->setSource(outStream.str()); vshader->load(); vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); HighLevelGpuProgramPtr fshader = mgr.createProgram("Atmosphere_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); @@ -580,15 +569,15 @@ void SkyManager::create() StringUtil::StrStreamType _outStream; _outStream << - "void main_fp( \n" - " in float4 iVertexColor : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n"; + "void main_fp( \n" + " in float4 iVertexColor : TEXCOORD0, \n" + " out float4 oColor : COLOR, \n"; if (RenderingManager::useMRT()) _outStream << " out float4 oColor1 : COLOR1, \n"; _outStream << " uniform float4 emissive \n" - ") \n" - "{ \n" + ") \n" + "{ \n" " oColor = iVertexColor * emissive; \n"; if (RenderingManager::useMRT()) _outStream << " oColor1 = float4(1, 0, 0, 1); \n"; @@ -598,19 +587,36 @@ void SkyManager::create() fshader->load(); fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); + + mAtmosphereDay = mRootNode->createChildSceneNode(); + meshes = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); + for(size_t i = 0;i < meshes.size();i++) + { + Entity* atmosphere_ent = mSceneMgr->createEntity(meshes[i].first->getName()); + atmosphere_ent->setCastShadows(false); + + ModVertexAlpha(atmosphere_ent, 0); + + atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); + atmosphere_ent->setVisibilityFlags(RV_Sky); + mAtmosphereDay->attachObject(atmosphere_ent); + + mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); + mAtmosphereMaterial = mAtmosphereMaterial->clone("Atmosphere"); + atmosphere_ent->getSubEntity(0)->setMaterial(mAtmosphereMaterial); + + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); + + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 0.0); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + } // Clouds - NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); - Entity* clouds_ent = mSceneMgr->createEntity("meshes\\sky_clouds_01.nif"); - clouds_ent->setVisibilityFlags(RV_Sky); - clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); - SceneNode* clouds_node = mRootNode->createChildSceneNode(); - clouds_node->attachObject(clouds_ent); - mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); - mCloudMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); - clouds_ent->setCastShadows(false); - // Clouds vertex shader HighLevelGpuProgramPtr vshader2 = mgr.createProgram("Clouds_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); @@ -618,24 +624,23 @@ void SkyManager::create() vshader2->setParameter("entry_point", "main_vp"); StringUtil::StrStreamType outStream3; outStream3 << - "void main_vp( \n" - " float4 position : POSITION, \n" - " in float4 color : COLOR, \n" + "void main_vp( \n" + " float4 position : POSITION, \n" + " in float4 color : COLOR, \n" " out float4 oColor : TEXCOORD1, \n" " in float2 uv : TEXCOORD0, \n" " out float2 oUV : TEXCOORD0, \n" - " out float4 oPosition : POSITION, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" + " out float4 oPosition : POSITION, \n" + " uniform float4x4 worldViewProj \n" + ") \n" + "{ \n" " oUV = uv; \n" " oColor = color; \n" - " oPosition = mul( worldViewProj, position ); \n" + " oPosition = mul( worldViewProj, position ); \n" "}"; vshader2->setSource(outStream3.str()); vshader2->load(); vshader2->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - mCloudMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader2->getName()); // Clouds fragment shader mCloudFragmentShader = mgr.createProgram("Clouds_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -644,10 +649,10 @@ void SkyManager::create() mCloudFragmentShader->setParameter("entry_point", "main_fp"); StringUtil::StrStreamType outStream2; outStream2 << - "void main_fp( \n" + "void main_fp( \n" " in float2 uv : TEXCOORD0, \n" " in float4 color : TEXCOORD1, \n" - " out float4 oColor : COLOR, \n"; + " out float4 oColor : COLOR, \n"; if (RenderingManager::useMRT()) outStream2 << " out float4 oColor1 : COLOR1, \n"; outStream2 << @@ -658,8 +663,8 @@ void SkyManager::create() " uniform float speed, \n" " uniform float opacity, \n" " uniform float4 emissive \n" - ") \n" - "{ \n" + ") \n" + "{ \n" " uv += float2(0,1) * time * speed * 0.003; \n" // Scroll in y direction " float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n" " oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n"; @@ -670,31 +675,36 @@ void SkyManager::create() mCloudFragmentShader->setSource(outStream2.str()); mCloudFragmentShader->load(); mCloudFragmentShader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - mCloudMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(mCloudFragmentShader->getName()); + + SceneNode* clouds_node = mRootNode->createChildSceneNode(); + meshes = NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); + for(size_t i = 0;i < meshes.size();i++) + { + Entity* clouds_ent = mSceneMgr->createEntity(meshes[i].first->getName()); + clouds_ent->setVisibilityFlags(RV_Sky); + clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); + clouds_node->attachObject(clouds_ent); + + mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); + mCloudMaterial = mCloudMaterial->clone("Clouds"); + clouds_ent->getSubEntity(0)->setMaterial(mCloudMaterial); + + mCloudMaterial->getTechnique(0)->getPass(0)->setPolygonModeOverrideable(false); + clouds_ent->setCastShadows(false); + + mCloudMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader2->getName()); + mCloudMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(mCloudFragmentShader->getName()); + ModVertexAlpha(clouds_ent, 1); + + mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); + mCloudMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mCloudMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates(); + mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("textures\\tx_sky_cloudy.dds"); + mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); + } setCloudsOpacity(0.75); - ModVertexAlpha(clouds_ent, 1); - - // I'm not sure if the materials are being used by any other objects - // Make a unique "modifiable" copy of the materials to be sure - mCloudMaterial = mCloudMaterial->clone("Clouds"); - clouds_ent->getSubEntity(0)->setMaterial(mCloudMaterial); - mAtmosphereMaterial = mAtmosphereMaterial->clone("Atmosphere"); - atmosphere_ent->getSubEntity(0)->setMaterial(mAtmosphereMaterial); - - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, 0.0); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setAmbient(0.0, 0.0, 0.0); - mCloudMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1.0, 1.0, 1.0); - mCloudMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); - mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); - - mCloudMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates(); - mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("textures\\tx_sky_cloudy.dds"); - mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); - mCreated = true; } @@ -851,7 +861,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) else { mAtmosphereNight->setVisible(true); - for (int i=0; i<7; ++i) + for (size_t i=0; igetTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, weather.mNightFade); mStarsOpacity = weather.mNightFade; } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index e11745e82..7b7cc2d16 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -1,13 +1,14 @@ #ifndef _GAME_RENDER_SKY_H #define _GAME_RENDER_SKY_H +#include + #include #include #include #include #include -#include "sky.hpp" #include "../mwworld/weather.hpp" namespace Ogre @@ -195,7 +196,7 @@ namespace MWRender Ogre::MaterialPtr mCloudMaterial; Ogre::MaterialPtr mAtmosphereMaterial; - Ogre::MaterialPtr mStarsMaterials[7]; + std::vector mStarsMaterials; Ogre::HighLevelGpuProgramPtr mCloudFragmentShader; diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index efe9ddea6..a479e78a5 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -322,12 +322,16 @@ void NIFLoader::createMaterial(const Ogre::String &name, void NIFLoader::loadResource(Ogre::Resource *resource) { - warn("Found no records in NIF."); + warn("Found no records in NIF for "+resource->getName()); } -Ogre::MeshPtr NIFLoader::load(const std::string &name, const std::string &group) +MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, const std::string &group) { Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + MeshPairList ret; + + if(skel != NULL) + skel->setNull(); // Check if the resource already exists Ogre::MeshPtr themesh = meshMgr.getByName(name, group); @@ -336,7 +340,9 @@ Ogre::MeshPtr NIFLoader::load(const std::string &name, const std::string &group) NIFLoader *loader = &sLoaders[name]; themesh = meshMgr.createManual(name, group, loader); } - return themesh; + ret.push_back(std::make_pair(themesh, std::string())); + + return ret; } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 985c64e0d..1eef7eb37 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -67,6 +68,12 @@ namespace Nif namespace NifOgre { + +/** This holds a list of meshes along with the names of their parent nodes + */ +typedef std::vector< std::pair > MeshPairList; + + /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into something Ogre can use. @@ -75,19 +82,18 @@ namespace NifOgre NIFLoader::load("somemesh.nif"); - Afterwards, you can use the mesh name "somemesh.nif" normally to - create entities and so on. The mesh isn't loaded from disk until - OGRE needs it for rendering. Thus the above load() command is not - very resource intensive, and can safely be done for a large number - of meshes at load time. + This returns a list of meshes used by the model, as well as the names of + their parent nodes (as they pertain to the skeleton, which is optionally + returned in the second argument if it exists). */ class NIFLoader : Ogre::ManualResourceLoader { public: virtual void loadResource(Ogre::Resource *resource); - static Ogre::MeshPtr load(const std::string &name, - const std::string &group="General"); + static MeshPairList load(const std::string &name, + Ogre::SkeletonPtr *skel=NULL, + const std::string &group="General"); private: void warn(const std::string &msg); From 16c2ea3a751500733bb69b7571c1a6061e6396b3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jul 2012 11:13:38 +0200 Subject: [PATCH 130/688] terrain colourmap --- apps/openmw/mwrender/terrain.cpp | 3 +++ apps/openmw/mwrender/terrainmaterial.cpp | 20 +++++++++++++++---- apps/openmw/mwrender/terrainmaterial.hpp | 9 +++++---- extern/shiny | 2 +- files/materials/terrain.shader | 25 +++++++++++++++++++++++- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 67cee435c..b1d9dee12 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -191,6 +191,9 @@ namespace MWRender y*(mLandSize-1), mLandSize); + mActiveProfile->setGlobalColourMapEnabled(true); + mActiveProfile->setGlobalColourMap (terrain, vertex->getName()); + //this is a hack to get around the fact that Ogre seems to //corrupt the global colour map leading to rendering errors //MaterialPtr mat = terrain->getMaterial(); diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index ae04306f6..f8551a3fd 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -31,6 +31,7 @@ namespace MWRender TerrainMaterial::Profile::Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc) : Ogre::TerrainMaterialGenerator::Profile(parent, name, desc) + , mGlobalColourMap(false) { } @@ -56,11 +57,17 @@ namespace MWRender createPass(); return Ogre::MaterialManager::getSingleton().getByName(matName); + } - /* - Ogre::MaterialPtr m = Ogre::MaterialManager::getSingleton().create(matName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - return m; - */ + void TerrainMaterial::Profile::setGlobalColourMapEnabled (bool enabled) + { + mGlobalColourMap = enabled; + mParent->_markChanged(); + } + + void TerrainMaterial::Profile::setGlobalColourMap (Ogre::Terrain* terrain, const std::string& name) + { + sh::Factory::getInstance ().setTextureAlias (terrain->getMaterialName () + "_colourMap", name); } int TerrainMaterial::Profile::getLayersPerPass () const @@ -76,6 +83,11 @@ namespace MWRender p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); + + p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); + + sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); + colourMap->setProperty ("texture_alias", sh::makeProperty(mMaterial->getName() + "_colourMap")); } Ogre::MaterialPtr TerrainMaterial::Profile::generateForCompositeMap(const Ogre::Terrain* terrain) diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index 73bbd7e07..2ee0224fd 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -47,10 +47,6 @@ namespace MWRender class Profile : public Ogre::TerrainMaterialGenerator::Profile { - protected: - Ogre::TerrainMaterialGenerator* mParent; - Ogre::String mName; - Ogre::String mDesc; public: Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc); virtual ~Profile(); @@ -69,9 +65,14 @@ namespace MWRender virtual void requestOptions(Ogre::Terrain* terrain); + void setGlobalColourMapEnabled(bool enabled); + void setGlobalColourMap (Ogre::Terrain* terrain, const std::string& name); + private: sh::MaterialInstance* mMaterial; + bool mGlobalColourMap; + void createPass (int index=0); int getLayersPerPass () const; diff --git a/extern/shiny b/extern/shiny index 1c25aca08..5cf02ac0c 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 1c25aca082214beddd05fa9b8adf481c7299cf0e +Subproject commit 5cf02ac0c39115fcf7f69eea7a79c9c6ad7dbd71 diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 8b186def3..bb776d691 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -3,8 +3,11 @@ #define FOG @shGlobalSettingBool(fog) #define MRT @shGlobalSettingBool(mrt_output) +#define COLOUR_MAP @shPropertyBool(colour_map) + @shAllocatePassthrough(1, depth) +@shAllocatePassthrough(2, UV) #ifdef SH_VERTEX_SHADER @@ -26,6 +29,7 @@ shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); @shPassthroughAssign(depth, shOutputPosition.z); + @shPassthroughAssign(UV, uv0); } @@ -34,11 +38,20 @@ SH_BEGIN_PROGRAM +#if COLOUR_MAP + shSampler2D(colourMap) +#endif + +#if FOG + shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) + shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) +#endif @shPassthroughFragmentInputs #if MRT shDeclareMrtOutput(1) + shUniform(float, far) @shAutoConstant(far, far_clip_distance) #endif @@ -47,12 +60,22 @@ { float depth = @shPassthroughReceive(depth); + float2 UV = @shPassthroughReceive(UV); shOutputColour(0) = float4(1,0,0,1); + +#if COLOUR_MAP + shOutputColour(0).rgb *= shSample(colourMap, UV).rgb; +#endif + +#if FOG + float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); +#endif #if MRT - //shOutputColour(1) = float4(1,1,1,1); + shOutputColour(1) = float4(depth / far,1,1,1); #endif } From d41050fb79a60f62458620f10c0363c3d7e78811 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jul 2012 11:21:40 +0200 Subject: [PATCH 131/688] merge --- components/nifogre/ogre_nif_loader.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index e7c677f83..bde948970 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -212,23 +212,23 @@ void NIFLoader::createMaterial(const Ogre::String &name, const Ogre::Vector3 &emissive, float glossiness, float alpha, int alphaFlags, float alphaTest, - const String &texName, bool vertexColor) + const Ogre::String &texName, bool vertexColor) { if (texName.empty()) return; sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (name, "openmw_objects_base"); instance->setProperty ("ambient", sh::makeProperty ( - new sh::Vector3(ambient.array[0], ambient.array[1], ambient.array[2]))); + new sh::Vector3(ambient.x, ambient.y, ambient.z))); instance->setProperty ("diffuse", sh::makeProperty ( - new sh::Vector4(diffuse.array[0], diffuse.array[1], diffuse.array[2], alpha))); + new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); instance->setProperty ("specular", sh::makeProperty ( - new sh::Vector4(specular.array[0], specular.array[1], specular.array[2], glossiness))); + new sh::Vector4(specular.x, specular.y, specular.z, glossiness))); instance->setProperty ("emissive", sh::makeProperty ( - new sh::Vector3(emissive.array[0], emissive.array[1], emissive.array[2]))); + new sh::Vector3(emissive.x, emissive.y, emissive.z))); instance->setProperty ("diffuseMap", sh::makeProperty(texName)); @@ -679,7 +679,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou { //std::cout << "new"; createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, - d->glossiness, d->alpha, alphaFlags, alphaTest, texName, shape->data->colors.length != 0); + d->glossiness, d->alpha, alphaFlags, alphaTest, texName, shape->data->colors.size() != 0); MaterialMap.insert(std::make_pair(texName,material)); } } @@ -689,7 +689,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou // material for it. const Ogre::Vector3 zero(0.0f), one(1.0f); createMaterial(material, one, one, zero, zero, 0.0f, 1.0f, - alphaFlags, alphaTest, texName, shape->data->colors.length != 0); + alphaFlags, alphaTest, texName, shape->data->colors.size() != 0); } } } // End of material block, if(!hidden) ... From 5a381006e52e6815f4f86f8eeec09f6c1e8c15eb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 14 Jul 2012 09:20:09 -0700 Subject: [PATCH 132/688] Fix parsing of some key lists It seems some still want you to read the interpolation type even when there's no keys. --- components/nif/data.hpp | 4 ++-- components/nif/nif_file.hpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/nif/data.hpp b/components/nif/data.hpp index d08ba7b32..63df23b27 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -394,9 +394,9 @@ struct NiMorphData : public Record nif->getChar(); mMorphs.resize(morphCount); - for(int i=0; igetInt(); - if(count == 0) return; + if(count == 0 && !force) + return; mInterpolationType = nif->getInt(); mKeys.resize(count); From 93c641efa7ffe3dd4bd67bde62cae898c37cc79d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jul 2012 18:25:43 +0200 Subject: [PATCH 133/688] terrain albedo --- apps/openmw/mwrender/terrainmaterial.cpp | 58 ++++++++++++++++++++++-- apps/openmw/mwrender/terrainmaterial.hpp | 2 +- extern/shiny | 2 +- files/materials/terrain.shader | 48 +++++++++++++++++++- 4 files changed, 102 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index f8551a3fd..d7c42efee 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -4,6 +4,21 @@ #include +namespace +{ + Ogre::String getComponent (int num) + { + if (num == 0) + return "x"; + else if (num == 1) + return "y"; + else if (num == 2) + return "z"; + else + return "w"; + } +} + namespace MWRender { @@ -54,7 +69,7 @@ namespace MWRender mMaterial->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); - createPass(); + createPass(0, terrain); return Ogre::MaterialManager::getSingleton().getByName(matName); } @@ -72,10 +87,10 @@ namespace MWRender int TerrainMaterial::Profile::getLayersPerPass () const { - return 10; + return 12; } - void TerrainMaterial::Profile::createPass (int index) + void TerrainMaterial::Profile::createPass (int index, const Ogre::Terrain* terrain) { int layerOffset = index * getLayersPerPass(); @@ -86,8 +101,41 @@ namespace MWRender p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap))); + // global colour map sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap"); - colourMap->setProperty ("texture_alias", sh::makeProperty(mMaterial->getName() + "_colourMap")); + colourMap->setProperty ("texture_alias", sh::makeProperty (new sh::StringValue(mMaterial->getName() + "_colourMap"))); + colourMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + // global normal map + sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap"); + normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); + normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + + uint maxLayers = getMaxLayers(terrain); + uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); + uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + + p->mShaderProperties.setProperty ("num_layers", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numLayers)))); + p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); + + // blend maps + for (uint i = 0; i < numBlendTextures; ++i) + { + sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); + blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(i)))); + blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + } + + // layer maps + for (uint i = 0; i < numLayers; ++i) + { + sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); + diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(i, 0)))); + p->mShaderProperties.setProperty ("blendmap_index_" + Ogre::StringConverter::toString(i), + sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(int((i-1) / 4))))); + p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), + sh::makeProperty(new sh::StringValue(getComponent(int((i-1) % 4))))); + } } Ogre::MaterialPtr TerrainMaterial::Profile::generateForCompositeMap(const Ogre::Terrain* terrain) @@ -111,7 +159,7 @@ namespace MWRender void TerrainMaterial::Profile::requestOptions(Ogre::Terrain* terrain) { terrain->_setMorphRequired(true); - terrain->_setNormalMapRequired(false); + terrain->_setNormalMapRequired(true); // global normal map terrain->_setLightMapRequired(false); terrain->_setCompositeMapRequired(false); } diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index 2ee0224fd..a9f957442 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -73,7 +73,7 @@ namespace MWRender bool mGlobalColourMap; - void createPass (int index=0); + void createPass (int index, const Ogre::Terrain* terrain); int getLayersPerPass () const; diff --git a/extern/shiny b/extern/shiny index 5cf02ac0c..a83d479c4 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 5cf02ac0c39115fcf7f69eea7a79c9c6ad7dbd71 +Subproject commit a83d479c461feead0356946f841c2c474760f420 diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index bb776d691..34b52caf6 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -5,6 +5,17 @@ #define COLOUR_MAP @shPropertyBool(colour_map) +#define NUM_LAYERS @shPropertyString(num_layers) + + +#define COMPONENT_0 x +#define COMPONENT_1 y +#define COMPONENT_2 z +#define COMPONENT_3 w + +#define IS_FIRST_PASS 1 + + @shAllocatePassthrough(1, depth) @shAllocatePassthrough(2, UV) @@ -41,6 +52,16 @@ #if COLOUR_MAP shSampler2D(colourMap) #endif + + shSampler2D(normalMap) // global normal map + +@shForeach(@shPropertyString(num_blendmaps)) + shSampler2D(blendMap@shIterator) +@shEndForeach + +@shForeach(@shPropertyString(num_layers)) + shSampler2D(diffuseMap@shIterator) +@shEndForeach #if FOG shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) @@ -62,11 +83,36 @@ float depth = @shPassthroughReceive(depth); float2 UV = @shPassthroughReceive(UV); - shOutputColour(0) = float4(1,0,0,1); + float3 normal = shSample(normalMap, UV).rgb * 2 - 1; + + // fetch blendmaps +@shForeach(@shPropertyString(num_blendmaps)) + float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); +@shEndForeach + + float blendAmount; + float3 albedo; +@shForeach(@shPropertyString(num_layers)) + + +#if IS_FIRST_PASS == 1 && @shIterator == 0 + // first layer of first pass doesn't need a blend map + albedo = shSample(diffuseMap0, UV * 5).rgb; +#else + blendAmount = blendValues@shPropertyString(blendmap_index_@shIterator).@shPropertyString(blendmap_component_@shIterator); + + albedo += shSample(diffuseMap@shIterator, UV * 5).rgb * blendAmount; +#endif +@shEndForeach + + shOutputColour(0) = float4(1,1,1,1); #if COLOUR_MAP shOutputColour(0).rgb *= shSample(colourMap, UV).rgb; #endif + + shOutputColour(0).rgb *= albedo; + #if FOG float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); From 29f91753f7b624b094ef3e9f0f237d560832b514 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jul 2012 18:44:03 +0200 Subject: [PATCH 134/688] correction --- apps/openmw/mwrender/terrainmaterial.cpp | 2 +- files/materials/terrain.shader | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index d7c42efee..4c8f06030 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -87,7 +87,7 @@ namespace MWRender int TerrainMaterial::Profile::getLayersPerPass () const { - return 12; + return 11; } void TerrainMaterial::Profile::createPass (int index, const Ogre::Terrain* terrain) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 34b52caf6..47f81d05d 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -90,18 +90,17 @@ float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); @shEndForeach - float blendAmount; float3 albedo; @shForeach(@shPropertyString(num_layers)) #if IS_FIRST_PASS == 1 && @shIterator == 0 // first layer of first pass doesn't need a blend map - albedo = shSample(diffuseMap0, UV * 5).rgb; + albedo = shSample(diffuseMap0, UV * 10).rgb; #else - blendAmount = blendValues@shPropertyString(blendmap_index_@shIterator).@shPropertyString(blendmap_component_@shIterator); + #define BLEND_AMOUNT blendValues@shPropertyString(blendmap_index_@shIterator).@shPropertyString(blendmap_component_@shIterator) - albedo += shSample(diffuseMap@shIterator, UV * 5).rgb * blendAmount; + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, BLEND_AMOUNT); #endif @shEndForeach From 5345d4eeefdc909c687e5f249944bc7e2cd8454c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jul 2012 18:45:03 +0200 Subject: [PATCH 135/688] fix a warning --- components/nif/nif_file.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index a21882c6d..42e312b7f 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -83,7 +83,7 @@ class NIFFile float read_le32f() { union { - int i; + uint32_t i; float f; } u = { read_le32() }; return u.f; From 32e14907a218ab4223747e7f12b8b904b20d4ea3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jul 2012 19:09:35 +0200 Subject: [PATCH 136/688] add a default value for CMAKE_BUILD_TYPE, resolves error when it is not set --- CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b561815ca..6c822256f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,13 @@ set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") +# Add a sensible build type default and warning because empty means no optimization and no debug info. +if(NOT CMAKE_BUILD_TYPE) + message("WARNING: CMAKE_BUILD_TYPE is not defined!\n Defaulting to CMAKE_BUILD_TYPE=RelWithDebInfo. Use ccmake to set a proper value.") + set(CMAKE_BUILD_TYPE RelWithDebInfo + CACHE STRING "Type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif(NOT CMAKE_BUILD_TYPE) + # Debug suffix for plugins set(DEBUG_SUFFIX "") if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") From 94c3fb81d11e916c7517c7ddaa9979dbf1a4cec4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 14 Jul 2012 21:36:42 +0200 Subject: [PATCH 137/688] check if CMAKE_BUILD_TYPE is defined instead of defining it by default --- CMakeLists.txt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c822256f..a8a8ad18b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,17 +20,12 @@ set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") -# Add a sensible build type default and warning because empty means no optimization and no debug info. -if(NOT CMAKE_BUILD_TYPE) - message("WARNING: CMAKE_BUILD_TYPE is not defined!\n Defaulting to CMAKE_BUILD_TYPE=RelWithDebInfo. Use ccmake to set a proper value.") - set(CMAKE_BUILD_TYPE RelWithDebInfo - CACHE STRING "Type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) -endif(NOT CMAKE_BUILD_TYPE) - # Debug suffix for plugins set(DEBUG_SUFFIX "") -if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") - set(DEBUG_SUFFIX "_d") +if (DEFINED CMAKE_BUILD_TYPE) + if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(DEBUG_SUFFIX "_d") + endif() endif() # doxygen main page From 778e59ee37919ab3320ec3374b5d3f8a9c06039a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jul 2012 08:21:43 +0200 Subject: [PATCH 138/688] terrain as it was before, with about 4x less code --- apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwrender/terrainmaterial.cpp | 63 +++---- apps/openmw/mwrender/terrainmaterial.hpp | 4 - extern/shiny | 2 +- files/materials/objects.shader | 37 ++-- files/materials/terrain.shader | 196 ++++++++++++++++++++-- files/materials/terrain.shaderset | 4 +- 7 files changed, 242 insertions(+), 65 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d8aefaaa0..15660ade5 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -107,6 +107,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); + sh::Factory::getInstance ().setGlobalSetting ("num_lights", Settings::Manager::getString ("num lights", "Objects")); applyCompositors(); diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 4c8f06030..89d895358 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -69,31 +69,6 @@ namespace MWRender mMaterial->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); - createPass(0, terrain); - - return Ogre::MaterialManager::getSingleton().getByName(matName); - } - - void TerrainMaterial::Profile::setGlobalColourMapEnabled (bool enabled) - { - mGlobalColourMap = enabled; - mParent->_markChanged(); - } - - void TerrainMaterial::Profile::setGlobalColourMap (Ogre::Terrain* terrain, const std::string& name) - { - sh::Factory::getInstance ().setTextureAlias (terrain->getMaterialName () + "_colourMap", name); - } - - int TerrainMaterial::Profile::getLayersPerPass () const - { - return 11; - } - - void TerrainMaterial::Profile::createPass (int index, const Ogre::Terrain* terrain) - { - int layerOffset = index * getLayersPerPass(); - sh::MaterialInstancePass* p = mMaterial->createPass (); p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); @@ -131,11 +106,32 @@ namespace MWRender { sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(i, 0)))); - p->mShaderProperties.setProperty ("blendmap_index_" + Ogre::StringConverter::toString(i), - sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(int((i-1) / 4))))); p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), - sh::makeProperty(new sh::StringValue(getComponent(int((i-1) % 4))))); + sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(int((i-1) / 4)) + "." + getComponent(int((i-1) % 4))))); } + + // shadow + for (uint i = 0; i < 3; ++i) + { + sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); + shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); + } + + p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty(new sh::StringValue( + Ogre::StringConverter::toString(numBlendTextures + numLayers + 2)))); + + return Ogre::MaterialManager::getSingleton().getByName(matName); + } + + void TerrainMaterial::Profile::setGlobalColourMapEnabled (bool enabled) + { + mGlobalColourMap = enabled; + mParent->_markChanged(); + } + + void TerrainMaterial::Profile::setGlobalColourMap (Ogre::Terrain* terrain, const std::string& name) + { + sh::Factory::getInstance ().setTextureAlias (terrain->getMaterialName () + "_colourMap", name); } Ogre::MaterialPtr TerrainMaterial::Profile::generateForCompositeMap(const Ogre::Terrain* terrain) @@ -145,7 +141,16 @@ namespace MWRender Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const { - return 32; + // count the texture units free + Ogre::uint8 freeTextureUnits = 16; + // normalmap + --freeTextureUnits; + // colourmap + --freeTextureUnits; + freeTextureUnits -= 3; // shadow PSSM + + // each layer needs 1.25 units (1xdiffusespec, 0.25xblend) + return static_cast(freeTextureUnits / (1.25f)); } void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain) diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index a9f957442..3e31b2a58 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -73,10 +73,6 @@ namespace MWRender bool mGlobalColourMap; - void createPass (int index, const Ogre::Terrain* terrain); - - int getLayersPerPass () const; - }; TerrainMaterial(); diff --git a/extern/shiny b/extern/shiny index a83d479c4..5a9bda601 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit a83d479c461feead0356946f841c2c474760f420 +Subproject commit 5a9bda6010413555736479ef03103f764fecb91d diff --git a/files/materials/objects.shader b/files/materials/objects.shader index c343a32b0..eabdb8ca3 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -1,7 +1,5 @@ #include "core.h" -#include "shadows.h" - #define FOG @shGlobalSettingBool(fog) #define MRT @shPropertyNotBool(is_transparent) && @shGlobalSettingBool(mrt_output) @@ -10,6 +8,10 @@ #define SHADOWS_PSSM LIGHTING && @shGlobalSettingBool(shadows_pssm) #define SHADOWS LIGHTING && @shGlobalSettingBool(shadows) +#if SHADOWS || SHADOWS_PSSM +#include "shadows.h" +#endif + #if FOG || MRT || SHADOWS_PSSM #define NEED_DEPTH #endif @@ -110,7 +112,7 @@ shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) - @shForeach(8) + @shForeach(@shGlobalSettingString(num_lights)) shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) shUniform(float4, lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) shUniform(float4, lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) @@ -149,31 +151,32 @@ #if LIGHTING float3 normal = normalize(normalPassthrough); - float3 lightDir, diffuse; + float3 lightDir; + float3 diffuse = float3(0,0,0); float d; float3 ambient = materialAmbient.xyz * lightAmbient.xyz; - @shForeach(8) - // shadows only for the first (directional) light -#if @shIterator == 0 - #if SHADOWS + // shadows only for the first (directional) light +#if SHADOWS float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); - #endif - #if SHADOWS_PSSM +#endif +#if SHADOWS_PSSM float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints); - #endif +#endif - #if SHADOWS || SHADOWS_PSSM +#if SHADOWS || SHADOWS_PSSM float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); - #endif - - #if !SHADOWS && !SHADOWS_PSSM - float shadow = 1.0; - #endif #endif + +#if !SHADOWS && !SHADOWS_PSSM + float shadow = 1.0; +#endif + + @shForeach(@shGlobalSettingString(num_lights)) + lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePositionPassthrough.xyz * lightPosObjSpace@shIterator.w); d = length(lightDir); diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 47f81d05d..2b7091838 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -1,32 +1,70 @@ #include "core.h" +#define IS_FIRST_PASS 1 + #define FOG @shGlobalSettingBool(fog) #define MRT @shGlobalSettingBool(mrt_output) +#define LIGHTING @shGlobalSettingBool(lighting) + +#define SHADOWS_PSSM LIGHTING && @shGlobalSettingBool(shadows_pssm) +#define SHADOWS LIGHTING && @shGlobalSettingBool(shadows) + +#if SHADOWS || SHADOWS_PSSM +#include "shadows.h" +#endif + #define COLOUR_MAP @shPropertyBool(colour_map) #define NUM_LAYERS @shPropertyString(num_layers) - -#define COMPONENT_0 x -#define COMPONENT_1 y -#define COMPONENT_2 z -#define COMPONENT_3 w - -#define IS_FIRST_PASS 1 - +#if MRT || FOG || SHADOWS_PSSM +#define NEED_DEPTH 1 +#endif +#if NEED_DEPTH @shAllocatePassthrough(1, depth) +#endif + @shAllocatePassthrough(2, UV) +#if LIGHTING +@shAllocatePassthrough(3, objSpacePosition) +#endif + +#if SHADOWS +@shAllocatePassthrough(4, lightSpacePos0) +#endif +#if SHADOWS_PSSM +@shForeach(3) + @shAllocatePassthrough(4, lightSpacePos@shIterator) +@shEndForeach +#endif + #ifdef SH_VERTEX_SHADER + // ------------------------------------- VERTEX --------------------------------------- + SH_BEGIN_PROGRAM shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) shUniform(float4x4, viewProjMatrix) @shAutoConstant(viewProjMatrix, viewproj_matrix) + shUniform(float2, lodMorph) @shAutoConstant(lodMorph, custom, 1001) + shInput(float2, uv0) + shInput(float2, delta) // lodDelta, lodThreshold + +#if SHADOWS + shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) +#endif + +#if SHADOWS_PSSM + @shForeach(3) + shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) + @shEndForeach +#endif + @shPassthroughVertexOutputs @@ -36,16 +74,54 @@ float4 worldPos = shMatrixMult(worldMatrix, shInputPosition); + // determine whether to apply the LOD morph to this vertex + // we store the deltas against all vertices so we only want to apply + // the morph to the ones which would disappear. The target LOD which is + // being morphed to is stored in lodMorph.y, and the LOD at which + // the vertex should be morphed is stored in uv.w. If we subtract + // the former from the latter, and arrange to only morph if the + // result is negative (it will only be -1 in fact, since after that + // the vertex will never be indexed), we will achieve our aim. + // sign(vertexLOD - targetLOD) == -1 is to morph + float toMorph = -min(0, sign(delta.y - lodMorph.y)); + + // morph + // this assumes XZ terrain alignment + worldPos.y += delta.x * toMorph * lodMorph.x; + shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); +#if NEED_DEPTH @shPassthroughAssign(depth, shOutputPosition.z); +#endif + @shPassthroughAssign(UV, uv0); + +#if LIGHTING + @shPassthroughAssign(objSpacePosition, shInputPosition.xyz); +#endif + +#if SHADOWS + float4 lightSpacePos = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); + @shPassthroughAssign(lightSpacePos0, lightSpacePos); +#endif +#if SHADOWS_PSSM + float4 wPos = shMatrixMult(worldMatrix, shInputPosition); + + float4 lightSpacePos; + @shForeach(3) + lightSpacePos = shMatrixMult(texViewProjMatrix@shIterator, wPos); + @shPassthroughAssign(lightSpacePos@shIterator, lightSpacePos); + @shEndForeach +#endif } #else + // ----------------------------------- FRAGMENT ------------------------------------------ + SH_BEGIN_PROGRAM @@ -55,10 +131,11 @@ shSampler2D(normalMap) // global normal map + @shForeach(@shPropertyString(num_blendmaps)) shSampler2D(blendMap@shIterator) @shEndForeach - + @shForeach(@shPropertyString(num_layers)) shSampler2D(diffuseMap@shIterator) @shEndForeach @@ -76,21 +153,57 @@ #endif +#if LIGHTING + shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) + @shForeach(@shGlobalSettingString(num_lights)) + shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) + shUniform(float4, lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) + shUniform(float4, lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) + @shEndForeach +#endif + + +#if SHADOWS + shSampler2D(shadowMap0) + shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, @shPropertyString(shadowtexture_offset)) +#endif +#if SHADOWS_PSSM + @shForeach(3) + shSampler2D(shadowMap@shIterator) + shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(@shPropertyString(shadowtexture_offset))) + @shEndForeach + shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints) +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) +#endif + SH_START_PROGRAM { +#if NEED_DEPTH float depth = @shPassthroughReceive(depth); +#endif + float2 UV = @shPassthroughReceive(UV); +#if LIGHTING + float3 objSpacePosition = @shPassthroughReceive(objSpacePosition); + float3 normal = shSample(normalMap, UV).rgb * 2 - 1; + normal = normalize(normal); +#endif - // fetch blendmaps + + + // Layer calculations @shForeach(@shPropertyString(num_blendmaps)) float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); @shEndForeach - float3 albedo; + float3 albedo = float3(0,0,0); @shForeach(@shPropertyString(num_layers)) @@ -98,9 +211,11 @@ // first layer of first pass doesn't need a blend map albedo = shSample(diffuseMap0, UV * 10).rgb; #else - #define BLEND_AMOUNT blendValues@shPropertyString(blendmap_index_@shIterator).@shPropertyString(blendmap_component_@shIterator) + #define BLEND_AMOUNT blendValues@shPropertyString(blendmap_component_@shIterator) + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, BLEND_AMOUNT); + #endif @shEndForeach @@ -113,6 +228,63 @@ shOutputColour(0).rgb *= albedo; + + + + + // Lighting + +#if LIGHTING + // shadows only for the first (directional) light +#if SHADOWS + float4 lightSpacePos0 = @shPassthroughReceive(lightSpacePos0); + float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); +#endif +#if SHADOWS_PSSM + @shForeach(3) + float4 lightSpacePos@shIterator = @shPassthroughReceive(lightSpacePos@shIterator); + @shEndForeach + + float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depth, pssmSplitPoints); +#endif + +#if SHADOWS || SHADOWS_PSSM + float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; + float fade = 1-((depth - shadowFar_fadeStart.y) / fadeRange); + shadow = (depth > shadowFar_fadeStart.x) ? 1 : ((depth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); +#endif + +#if !SHADOWS && !SHADOWS_PSSM + float shadow = 1.0; +#endif + + + + float3 lightDir; + float3 diffuse = float3(0,0,0); + float d; + + @shForeach(@shGlobalSettingString(num_lights)) + + lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePosition.xyz * lightPosObjSpace@shIterator.w); + d = length(lightDir); + + + lightDir = normalize(lightDir); + +#if @shIterator == 0 && (SHADOWS || SHADOWS_PSSM) + diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow; +#else + diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); +#endif + @shEndForeach + + shOutputColour(0).xyz *= (lightAmbient.xyz + diffuse); +#endif + + + + #if FOG float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); diff --git a/files/materials/terrain.shaderset b/files/materials/terrain.shaderset index d31429dbb..4132b8e9c 100644 --- a/files/materials/terrain.shaderset +++ b/files/materials/terrain.shaderset @@ -2,7 +2,7 @@ shader_set terrain_vertex { source terrain.shader type vertex - profiles_cg vs_2_0 arbvp1 + profiles_cg vs_2_0 vp40 arbvp1 profiles_hlsl vs_2_0 } @@ -10,6 +10,6 @@ shader_set terrain_fragment { source terrain.shader type fragment - profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 profiles_hlsl ps_2_0 } From 9ed2f1df6716e2d1a065970ba8122df301da43fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jul 2012 08:39:34 +0200 Subject: [PATCH 139/688] boost wave not needed anymore --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05287439b..d5ea17657 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,7 +195,7 @@ endif() find_package(OGRE REQUIRED) find_package(MyGUI REQUIRED) -find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread wave) +find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread) find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) From 6a447c88fb8349e17846dde2ef0d83c2fe24be58 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 06:45:21 -0700 Subject: [PATCH 140/688] Create meshes from the NiTriShapes in the NIF. This doesn't actually load them yet. It's also very slow for certain NIFs. --- components/nifogre/ogre_nif_loader.cpp | 72 ++++++++++++++++++++++---- components/nifogre/ogre_nif_loader.hpp | 9 +++- 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index a479e78a5..40740ac56 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -132,13 +132,13 @@ NIFLoader::LoaderMap NIFLoader::sLoaders; void NIFLoader::warn(const std::string &msg) { - std::cerr << "NIFLoader: Warn:" << msg << "\n"; + std::cerr << "NIFLoader: Warn: " << msg << std::endl; } void NIFLoader::fail(const std::string &msg) { std::cerr << "NIFLoader: Fail: "<< msg << std::endl; - assert(1); + abort(); } @@ -325,24 +325,74 @@ void NIFLoader::loadResource(Ogre::Resource *resource) warn("Found no records in NIF for "+resource->getName()); } +void NIFLoader::createMeshes(const std::string &name, const std::string &group, Nif::Node *node, MeshPairList &meshes, int flags) +{ + flags |= node->flags; + + // TODO: Check for extra data + + if(node->recType == Nif::RC_NiTriShape) + { + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + std::string fullname = name+"@"+node->name; + + Ogre::MeshPtr mesh = meshMgr.getByName(fullname); + if(mesh.isNull()) + { + NIFLoader *loader = &sLoaders[fullname]; + loader->mName = name; + loader->mShapeName = node->name; + + mesh = meshMgr.createManual(fullname, group, loader); + } + + meshes.push_back(std::make_pair(mesh, (node->parent ? node->parent->name : std::string()))); + } + else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && + node->recType != Nif::RC_NiRotatingParticles) + warn("Unhandled mesh node type: "+node->recName); + + Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + createMeshes(name, group, children[i].getPtr(), meshes, flags); + } + } +} + MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, const std::string &group) { - Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - MeshPairList ret; + MeshPairList meshes; if(skel != NULL) skel->setNull(); - // Check if the resource already exists - Ogre::MeshPtr themesh = meshMgr.getByName(name, group); - if(themesh.isNull()) + Nif::NIFFile nif(name); + if (nif.numRecords() < 1) { - NIFLoader *loader = &sLoaders[name]; - themesh = meshMgr.createManual(name, group, loader); + nif.warn("Found no records in NIF."); + return meshes; } - ret.push_back(std::make_pair(themesh, std::string())); - return ret; + // The first record is assumed to be the root node + Nif::Record *r = nif.getRecord(0); + assert(r != NULL); + + Nif::Node *node = dynamic_cast(r); + if(node == NULL) + { + nif.warn("First record in file was not a node, but a "+ + r->recName+". Skipping file."); + return meshes; + } + + createMeshes(name, group, node, meshes); + + return meshes; } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 1eef7eb37..092cc23a8 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -96,8 +96,13 @@ public: const std::string &group="General"); private: - void warn(const std::string &msg); - void fail(const std::string &msg); + std::string mName; + std::string mShapeName; + + static void warn(const std::string &msg); + static void fail(const std::string &msg); + + static void createMeshes(const std::string &name, const std::string &group, Nif::Node *node, MeshPairList &meshes, int flags=0); typedef std::map LoaderMap; static LoaderMap sLoaders; From a8ebb39883fba74f7e1cb64529f50ba0e81f65d5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 07:41:19 -0700 Subject: [PATCH 141/688] Avoid Mangle for BSA accesses The way it was set up was not very efficient, and we're using Ogre for resource management anyway, so it's best to just use that. --- components/bsa/bsa_archive.cpp | 7 +- components/bsa/bsa_file.cpp | 330 ++++++++++++++++++++------------- components/bsa/bsa_file.hpp | 142 +++++++------- 3 files changed, 265 insertions(+), 214 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index e9ce3f615..07921da69 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -28,13 +28,11 @@ #include #include #include "bsa_file.hpp" -#include namespace { using namespace Ogre; -using namespace Mangle::Stream; using namespace Bsa; struct ciLessBoost : std::binary_function @@ -239,10 +237,7 @@ public: // Open the file - StreamPtr strm = narc->getFile(passed.c_str()); - - // Wrap it into an Ogre::DataStream. - return DataStreamPtr(new Mangle2OgreStream(strm)); + return narc->getFile(passed.c_str()); } bool exists(const String& filename) { diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index f19606703..b5145a4e4 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -23,171 +23,235 @@ #include "bsa_file.hpp" -#include -#include - #include #include #include +#include + using namespace std; -using namespace Mangle::Stream; using namespace Bsa; +class ConstrainedDataStream : public Ogre::DataStream { + std::ifstream mStream; + const size_t mStart; + size_t mPos; + bool mIsEOF; + +public: + ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) + : mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false) + { + mSize = length; + if(!mStream.seekg(mStart, std::ios_base::beg)) + throw std::runtime_error("Error seeking to start of BSA entry"); + } + + ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname, + size_t start, size_t length) + : Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary), + mStart(start), mPos(0), mIsEOF(false) + { + mSize = length; + if(!mStream.seekg(mStart, std::ios_base::beg)) + throw std::runtime_error("Error seeking to start of BSA entry"); + } + + + virtual size_t read(void *buf, size_t count) + { + mStream.clear(); + + if(count > mSize-mPos) + { + count = mSize-mPos; + mIsEOF = true; + } + mStream.read(reinterpret_cast(buf), count); + + count = mStream.gcount(); + mPos += count; + return count; + } + + virtual void skip(long count) + { + if((count >= 0 && (size_t)count <= mSize-mPos) || + (count < 0 && (size_t)-count <= mPos)) + { + mStream.clear(); + if(mStream.seekg(count, std::ios_base::cur)) + { + mPos += count; + mIsEOF = false; + } + } + } + + virtual void seek(size_t pos) + { + if(pos < mSize) + { + mStream.clear(); + if(mStream.seekg(pos+mStart, std::ios_base::beg)) + { + mPos = pos; + mIsEOF = false; + } + } + } + + virtual size_t tell() const + { return mPos; } + + virtual bool eof() const + { return mIsEOF; } + + virtual void close() + { mStream.close(); } +}; + + /// Error handling void BSAFile::fail(const string &msg) { - throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + filename); + throw std::runtime_error("BSA Error: " + msg + "\nArchive: " + filename); } /// Read header information from the input source void BSAFile::readHeader() { - /* - * The layout of a BSA archive is as follows: - * - * - 12 bytes header, contains 3 ints: - * id number - equal to 0x100 - * dirsize - size of the directory block (see below) - * numfiles - number of files - * - * ---------- start of directory block ----------- - * - * - 8 bytes*numfiles, each record contains: - * fileSize - * offset into data buffer (see below) - * - * - 4 bytes*numfiles, each record is an offset into the following name buffer - * - * - name buffer, indexed by the previous table, each string is - * null-terminated. Size is (dirsize - 12*numfiles). - * - * ---------- end of directory block ------------- - * - * - 8*filenum - hash table block, we currently ignore this - * - * ----------- start of data buffer -------------- - * - * - The rest of the archive is file data, indexed by the - * offsets in the directory block. The offsets start at 0 at - * the beginning of this buffer. - * - */ - assert(!isLoaded); - assert(input); - assert(input->hasSize); - assert(input->hasPosition); - assert(input->isSeekable); + /* + * The layout of a BSA archive is as follows: + * + * - 12 bytes header, contains 3 ints: + * id number - equal to 0x100 + * dirsize - size of the directory block (see below) + * numfiles - number of files + * + * ---------- start of directory block ----------- + * + * - 8 bytes*numfiles, each record contains: + * fileSize + * offset into data buffer (see below) + * + * - 4 bytes*numfiles, each record is an offset into the following name buffer + * + * - name buffer, indexed by the previous table, each string is + * null-terminated. Size is (dirsize - 12*numfiles). + * + * ---------- end of directory block ------------- + * + * - 8*filenum - hash table block, we currently ignore this + * + * ----------- start of data buffer -------------- + * + * - The rest of the archive is file data, indexed by the + * offsets in the directory block. The offsets start at 0 at + * the beginning of this buffer. + * + */ + assert(!isLoaded); - // Total archive size - size_t fsize = input->size(); + std::ifstream input(filename.c_str(), std::ios_base::binary); - if( fsize < 12 ) - fail("File too small to be a valid BSA archive"); - - // Get essential header numbers - size_t dirsize, filenum; - - { - // First 12 bytes - uint32_t head[3]; - - input->read(head, 12); - - if(head[0] != 0x100) - fail("Unrecognized BSA header"); - - // Total number of bytes used in size/offset-table + filename - // sections. - dirsize = head[1]; - - // Number of files - filenum = head[2]; - } - - // Each file must take up at least 21 bytes of data in the bsa. So - // if files*21 overflows the file size then we are guaranteed that - // the archive is corrupt. - if( (filenum*21 > fsize -12) || - (dirsize+8*filenum > fsize -12) ) - fail("Directory information larger than entire archive"); - - // Read the offset info into a temporary buffer - vector offsets(3*filenum); - input->read(&offsets[0], 12*filenum); - - // Read the string table - stringBuf.resize(dirsize-12*filenum); - input->read(&stringBuf[0], stringBuf.size()); - - // Check our position - assert(input->tell() == 12+dirsize); - - // Calculate the offset of the data buffer. All file offsets are - // relative to this. 12 header bytes + directory + hash table - // (skipped) - size_t fileDataOffset = 12 + dirsize + 8*filenum; - - // Set up the the FileStruct table - files.resize(filenum); - for(size_t i=0;i fsize) - fail("Archive contains offsets outside itself"); - - // Add the file name to the lookup - lookup[fs.name] = i; + fsize = input.tellg(); + input.seekg(0); } - isLoaded = true; + if(fsize < 12) + fail("File too small to be a valid BSA archive"); + + // Get essential header numbers + size_t dirsize, filenum; + { + // First 12 bytes + uint32_t head[3]; + + input.read(reinterpret_cast(head), 12); + + if(head[0] != 0x100) + fail("Unrecognized BSA header"); + + // Total number of bytes used in size/offset-table + filename + // sections. + dirsize = head[1]; + + // Number of files + filenum = head[2]; + } + + // Each file must take up at least 21 bytes of data in the bsa. So + // if files*21 overflows the file size then we are guaranteed that + // the archive is corrupt. + if((filenum*21 > fsize -12) || (dirsize+8*filenum > fsize -12) ) + fail("Directory information larger than entire archive"); + + // Read the offset info into a temporary buffer + vector offsets(3*filenum); + input.read(reinterpret_cast(&offsets[0]), 12*filenum); + + // Read the string table + stringBuf.resize(dirsize-12*filenum); + input.read(&stringBuf[0], stringBuf.size()); + + // Check our position + assert(input.tellg() == 12+dirsize); + + // Calculate the offset of the data buffer. All file offsets are + // relative to this. 12 header bytes + directory + hash table + // (skipped) + size_t fileDataOffset = 12 + dirsize + 8*filenum; + + // Set up the the FileStruct table + files.resize(filenum); + for(size_t i=0;i fsize) + fail("Archive contains offsets outside itself"); + + // Add the file name to the lookup + lookup[fs.name] = i; + } + + isLoaded = true; } /// Get the index of a given file name, or -1 if not found int BSAFile::getIndex(const char *str) const { - Lookup::const_iterator it; - it = lookup.find(str); + Lookup::const_iterator it = lookup.find(str); + if(it == lookup.end()) + return -1; - if(it == lookup.end()) return -1; - else - { - int res = it->second; - assert(res >= 0 && res < static_cast (files.size())); - return res; - } + int res = it->second; + assert(res >= 0 && (size_t)res < files.size()); + return res; } /// Open an archive file. void BSAFile::open(const string &file) { - filename = file; - input = StreamPtr(new FileStream(file)); - readHeader(); + filename = file; + readHeader(); } -/** Open an archive from a generic stream. The 'name' parameter is - used for error messages. -*/ -void BSAFile::open(StreamPtr inp, const string &name) +Ogre::DataStreamPtr BSAFile::getFile(const char *file) { - filename = name; - input = inp; - readHeader(); -} - -StreamPtr BSAFile::getFile(const char *file) -{ - assert(file); - int i = getIndex(file); - if(i == -1) - fail("File not found: " + string(file)); - - FileStruct &fs = files[i]; - - return StreamPtr(new SliceStream(input, fs.offset, fs.fileSize)); + assert(file); + int i = getIndex(file); + if(i == -1) + fail("File not found: " + string(file)); + + const FileStruct &fs = files[i]; + return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, fs.offset, fs.fileSize)); } diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index 95fac0f4d..afe0c739c 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -24,13 +24,15 @@ #ifndef BSA_BSA_FILE_H #define BSA_BSA_FILE_H -#include #include #include #include #include #include +#include + + namespace Bsa { @@ -39,98 +41,88 @@ namespace Bsa */ class BSAFile { - public: +public: + /// Represents one file entry in the archive + struct FileStruct + { + // File size and offset in file. We store the offset from the + // beginning of the file, not the offset into the data buffer + // (which is what is stored in the archive.) + uint32_t fileSize, offset; - /// Represents one file entry in the archive - struct FileStruct - { - // File size and offset in file. We store the offset from the - // beginning of the file, not the offset into the data buffer - // (which is what is stored in the archive.) - uint32_t fileSize, offset; + // Zero-terminated file name + const char *name; + }; + typedef std::vector FileList; - // Zero-terminated file name - char* name; - }; +private: + /// Table of files in this archive + FileList files; - typedef std::vector FileList; + /// Filename string buffer + std::vector stringBuf; - private: + /// True when an archive has been loaded + bool isLoaded; - /// The archive source - Mangle::Stream::StreamPtr input; + /// Used for error messages + std::string filename; - /// Table of files in this archive - FileList files; + /// Case insensitive string comparison + struct iltstr + { + bool operator()(const char *s1, const char *s2) const + { return strcasecmp(s1,s2) < 0; } + }; - /// Filename string buffer - std::vector stringBuf; + /** A map used for fast file name lookup. The value is the index into + the files[] vector above. The iltstr ensures that file name + checks are case insensitive. + */ + typedef std::map Lookup; + Lookup lookup; - /// True when an archive has been loaded - bool isLoaded; + /// Error handling + void fail(const std::string &msg); - /// Used for error messages - std::string filename; + /// Read header information from the input source + void readHeader(); - /// Case insensitive string comparison - struct iltstr - { - bool operator()(const char *s1, const char *s2) const - { return strcasecmp(s1,s2) < 0; } - }; + /// Get the index of a given file name, or -1 if not found + int getIndex(const char *str) const; - /** A map used for fast file name lookup. The value is the index into - the files[] vector above. The iltstr ensures that file name - checks are case insensitive. - */ - typedef std::map Lookup; - Lookup lookup; +public: + /* ----------------------------------- + * BSA management methods + * ----------------------------------- + */ - /// Error handling - void fail(const std::string &msg); + BSAFile() + : isLoaded(false) + { } - /// Read header information from the input source - void readHeader(); + /// Open an archive file. + void open(const std::string &file); - /// Get the index of a given file name, or -1 if not found - int getIndex(const char *str) const; + /* ----------------------------------- + * Archive file routines + * ----------------------------------- + */ - public: + /// Check if a file exists + bool exists(const char *file) const + { return getIndex(file) != -1; } - /* ----------------------------------- - * BSA management methods - * ----------------------------------- - */ + /** Open a file contained in the archive. Throws an exception if the + file doesn't exist. - BSAFile() - : input(), isLoaded(false) {} + NOTE: All files opened from one archive will share a common file + handle. This is NOT thread safe. + */ + Ogre::DataStreamPtr getFile(const char *file); - /// Open an archive file. - void open(const std::string &file); - - /** Open an archive from a generic stream. The 'name' parameter is - used for error messages. - */ - void open(Mangle::Stream::StreamPtr inp, const std::string &name); - - /* ----------------------------------- - * Archive file routines - * ----------------------------------- - */ - - /// Check if a file exists - bool exists(const char *file) const { return getIndex(file) != -1; } - - /** Open a file contained in the archive. Throws an exception if the - file doesn't exist. - - NOTE: All files opened from one archive will share a common file - handle. This is NOT thread safe. - */ - Mangle::Stream::StreamPtr getFile(const char *file); - - /// Get a list of all files - const FileList &getList() const + /// Get a list of all files + const FileList &getList() const { return files; } }; From 7734771245a58dec8ce7c1d312a7f3b268141427 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 08:31:03 -0700 Subject: [PATCH 142/688] Use Ogre to load ESM data instead of Mangle --- components/esm/esm_reader.cpp | 18 ++++++++++-------- components/esm/esm_reader.hpp | 10 +++++----- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/components/esm/esm_reader.cpp b/components/esm/esm_reader.cpp index e9bfcf5ee..6b8409b45 100644 --- a/components/esm/esm_reader.cpp +++ b/components/esm/esm_reader.cpp @@ -27,7 +27,7 @@ void ESMReader::restoreContext(const ESM_Context &rc) void ESMReader::close() { - mEsm.reset(); + mEsm.setNull(); mCtx.filename.clear(); mCtx.leftFile = 0; mCtx.leftRec = 0; @@ -37,7 +37,7 @@ void ESMReader::close() mCtx.subName.val = 0; } -void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name) +void ESMReader::openRaw(Ogre::DataStreamPtr _esm, const std::string &name) { close(); mEsm = _esm; @@ -57,7 +57,7 @@ void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name) mSpf = SF_Other; } -void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name) +void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) { openRaw(_esm, name); @@ -107,14 +107,16 @@ void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name) void ESMReader::open(const std::string &file) { - using namespace Mangle::Stream; - open(StreamPtr(new FileStream(file)), file); + std::ifstream *stream = new std::ifstream(file.c_str(), std::ios_base::binary); + // Ogre will delete the stream for us + open(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); } void ESMReader::openRaw(const std::string &file) { - using namespace Mangle::Stream; - openRaw(StreamPtr(new FileStream(file)), file); + std::ifstream *stream = new std::ifstream(file.c_str(), std::ios_base::binary); + // Ogre will delete the stream for us + openRaw(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); } int64_t ESMReader::getHNLong(const char *name) @@ -339,7 +341,7 @@ void ESMReader::fail(const std::string &msg) ss << "\n File: " << mCtx.filename; ss << "\n Record: " << mCtx.recName.toString(); ss << "\n Subrecord: " << mCtx.subName.toString(); - if (mEsm != NULL) + if (!mEsm.isNull()) ss << "\n Offset: 0x" << hex << mEsm->tell(); throw std::runtime_error(ss.str()); } diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 340482891..17cca7a91 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -11,8 +11,8 @@ #include #include -#include -#include +#include + #include #include @@ -183,11 +183,11 @@ public: /// Raw opening. Opens the file and sets everything up but doesn't /// parse the header. - void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name); + void openRaw(Ogre::DataStreamPtr _esm, const std::string &name); /// Load ES file from a new stream, parses the header. Closes the /// currently open file first, if any. - void open(Mangle::Stream::StreamPtr _esm, const std::string &name); + void open(Ogre::DataStreamPtr _esm, const std::string &name); void open(const std::string &file); @@ -354,7 +354,7 @@ public: void setEncoding(const std::string& encoding); private: - Mangle::Stream::StreamPtr mEsm; + Ogre::DataStreamPtr mEsm; ESM_Context mCtx; From 2a3ce5ee6df8da1ca3a98dcf7080b834b4691692 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 08:40:18 -0700 Subject: [PATCH 143/688] Remove Mangle::Stream The base Stream class is left because some part of the terrain esm land factory inherits from it, though it's largely unused (few of the methods work, and none actually do anything). --- CMakeLists.txt | 3 +- libs/mangle/stream/clients/audiere_file.cpp | 32 --- libs/mangle/stream/clients/audiere_file.hpp | 38 --- libs/mangle/stream/clients/io_stream.cpp | 221 ------------------ libs/mangle/stream/clients/io_stream.hpp | 43 ---- .../mangle/stream/clients/ogre_datastream.hpp | 68 ------ libs/mangle/stream/filters/buffer_stream.hpp | 74 ------ libs/mangle/stream/filters/pure_filter.hpp | 46 ---- libs/mangle/stream/filters/slice_stream.hpp | 101 -------- libs/mangle/stream/servers/file_stream.hpp | 32 --- libs/mangle/stream/servers/memory_stream.hpp | 116 --------- .../mangle/stream/servers/ogre_datastream.hpp | 37 --- libs/mangle/stream/servers/outfile_stream.hpp | 41 ---- libs/mangle/stream/servers/phys_stream.hpp | 36 --- libs/mangle/stream/servers/std_ostream.hpp | 78 ------- libs/mangle/stream/servers/std_stream.hpp | 70 ------ 16 files changed, 1 insertion(+), 1035 deletions(-) delete mode 100644 libs/mangle/stream/clients/audiere_file.cpp delete mode 100644 libs/mangle/stream/clients/audiere_file.hpp delete mode 100644 libs/mangle/stream/clients/io_stream.cpp delete mode 100644 libs/mangle/stream/clients/io_stream.hpp delete mode 100644 libs/mangle/stream/clients/ogre_datastream.hpp delete mode 100644 libs/mangle/stream/filters/buffer_stream.hpp delete mode 100644 libs/mangle/stream/filters/pure_filter.hpp delete mode 100644 libs/mangle/stream/filters/slice_stream.hpp delete mode 100644 libs/mangle/stream/servers/file_stream.hpp delete mode 100644 libs/mangle/stream/servers/memory_stream.hpp delete mode 100644 libs/mangle/stream/servers/ogre_datastream.hpp delete mode 100644 libs/mangle/stream/servers/outfile_stream.hpp delete mode 100644 libs/mangle/stream/servers/phys_stream.hpp delete mode 100644 libs/mangle/stream/servers/std_ostream.hpp delete mode 100644 libs/mangle/stream/servers/std_stream.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a8a8ad18b..e3175fa7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,9 +101,8 @@ ENDIF() set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) -set(MANGLE_VFS ${LIBDIR}/mangle/vfs/servers/ogre_vfs.cpp) set(MANGLE_INPUT ${LIBDIR}/mangle/input/servers/ois_driver.cpp) -set(MANGLE_ALL ${MANGLE_VFS} ${MANGLE_INPUT}) +set(MANGLE_ALL ${MANGLE_INPUT}) source_group(libs\\mangle FILES ${MANGLE_ALL}) set(OENGINE_OGRE diff --git a/libs/mangle/stream/clients/audiere_file.cpp b/libs/mangle/stream/clients/audiere_file.cpp deleted file mode 100644 index 16bc7891a..000000000 --- a/libs/mangle/stream/clients/audiere_file.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "audiere_file.hpp" - -using namespace audiere; -using namespace Mangle::Stream; - -bool AudiereFile::seek(int pos, SeekMode mode) -{ - assert(inp->isSeekable); - assert(inp->hasPosition); - - size_t newPos; - - switch(mode) - { - case BEGIN: newPos = pos; break; - case CURRENT: newPos = pos+tell(); break; - case END: - // Seeking from the end. This requires that we're able to get - // the entire size of the stream. The pos also has to be - // non-positive. - assert(inp->hasSize); - assert(pos <= 0); - newPos = inp->size() + pos; - break; - default: - assert(0 && "invalid seek mode"); - } - - inp->seek(newPos); - return inp->tell() == newPos; - -} diff --git a/libs/mangle/stream/clients/audiere_file.hpp b/libs/mangle/stream/clients/audiere_file.hpp deleted file mode 100644 index 61e26f21b..000000000 --- a/libs/mangle/stream/clients/audiere_file.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef MANGLE_STREAM_AUDIERECLIENT_H -#define MANGLE_STREAM_AUDIERECLIENT_H - -#include -#include - -#include "../stream.hpp" - -namespace Mangle { -namespace Stream { - -/** @brief An Audiere::File that wraps a Mangle::Stream input. - - This lets Audiere read sound files from any generic archive or - file manager that supports Mangle streams. - */ -class AudiereFile : public audiere::RefImplementation -{ - StreamPtr inp; - - public: - AudiereFile(StreamPtr _inp) - : inp(_inp) {} - - /// Read 'count' bytes, return bytes successfully read - int ADR_CALL read(void *buf, int count) - { return inp->read(buf,count); } - - /// Seek, relative to specified seek mode. Returns true if successful. - bool ADR_CALL seek(int pos, audiere::File::SeekMode mode); - - /// Get current position - int ADR_CALL tell() - { assert(inp->hasPosition); return inp->tell(); } -}; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/clients/io_stream.cpp b/libs/mangle/stream/clients/io_stream.cpp deleted file mode 100644 index 5f1edc221..000000000 --- a/libs/mangle/stream/clients/io_stream.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "io_stream.hpp" - -// This seems to work -#ifndef EOF -#define EOF -1 -#endif - -using namespace Mangle::Stream; - -#define BSIZE 1024 - -// Streambuf for normal stream reading -class _istreambuf : public std::streambuf -{ - StreamPtr client; - char buf[BSIZE]; - -public: - _istreambuf(StreamPtr strm) : client(strm) - { - // Make sure we picked the right class - assert(client->isReadable); - assert(!client->hasPtr); - - // Tell streambuf to delegate reading operations to underflow() - setg(NULL,NULL,NULL); - - // Disallow writing - setp(NULL,NULL); - } - - /* Underflow is called when there is no more info to read in the - input buffer. We need to refill buf with new data (if any), and - set up the internal pointers with setg() to reflect the new - state. - */ - int underflow() - { - // Read some more data - size_t read = client->read(buf, BSIZE); - assert(read <= BSIZE); - - // If we're out of data, then EOF - if(read == 0) - return EOF; - - // Otherwise, set up input buffer - setg(buf, buf, buf+read); - - // Return the first char - return *((unsigned char*)buf); - } - - // Seek stream, if the source supports it. Ignores the second - // parameter as Mangle doesn't separate input and output pointers. - std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in) - { - // Does this stream know how to seek? - if(!client->isSeekable || !client->hasPosition) - // If not, signal an error. - return -1; - - // Set stream position and reset the buffer. - client->seek(pos); - setg(NULL,NULL,NULL); - - return client->tell(); - } -}; - -// Streambuf optimized for pointer-based input streams -class _ptrstreambuf : public std::streambuf -{ - StreamPtr client; - -public: - _ptrstreambuf(StreamPtr strm) : client(strm) - { - // Make sure we picked the right class - assert(client->isReadable); - assert(client->hasPtr); - - // seekpos() does all the work - seekpos(0); - } - - // Underflow is only called when we're at the end of the file - int underflow() { return EOF; } - - // Seek to a new position within the memory stream. This bypasses - // client->seek() entirely so isSeekable doesn't have to be set. - std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in) - { - // All pointer streams need a size - assert(client->hasSize); - - // Figure out how much will be left of the stream after seeking - size_t size = client->size() - pos; - - // Get a pointer - char* ptr = (char*)client->getPtr(pos,size); - - // And use it - setg(ptr,ptr,ptr+size); - - return pos; - } -}; - -// Streambuf for stream writing -class _ostreambuf : public std::streambuf -{ - StreamPtr client; - char buf[BSIZE]; - -public: - _ostreambuf(StreamPtr strm) : client(strm) - { - // Make sure we picked the right class - assert(client->isWritable); - - // Inform streambuf about our nice buffer - setp(buf, buf+BSIZE); - - // Disallow reading - setg(NULL,NULL,NULL); - } - - /* Sync means to flush (write) all current data to the output - stream. It will also set up the entire output buffer to be usable - again. - */ - int sync() - { - // Get the number of bytes that streambuf wants us to write - int num = pptr() - pbase(); - assert(num >= 0); - - // Is there any work to do? - if(num == 0) return 0; - - if((int)client->write(pbase(), num) != num) - // Inform caller that writing failed - return -1; - - // Reset output buffer pointers - setp(buf, buf+BSIZE); - - // No error - return 0; - } - - /* Called whenever the output buffer is full. - */ - int overflow(int c) - { - // First, write all existing data - if(sync()) return EOF; - - // Put the requested character in the next round of output - if(c != EOF) - { - *pptr() = c; - pbump(1); - } - - // No error - return 0; - } - - // Seek stream, if the source supports it. - std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::out) - { - if(!client->isSeekable || !client->hasPosition) - return -1; - - // Flush data and reset buffers - sync(); - - // Set stream position - client->seek(pos); - - return client->tell(); - } -}; - -MangleIStream::MangleIStream(StreamPtr inp) - : std::istream(NULL) -{ - assert(inp->isReadable); - - // Pick the right streambuf implementation based on whether the - // input supports pointers or not. - if(inp->hasPtr) - buf = new _ptrstreambuf(inp); - else - buf = new _istreambuf(inp); - - rdbuf(buf); -} - -MangleIStream::~MangleIStream() -{ - delete buf; -} - -MangleOStream::MangleOStream(StreamPtr out) - : std::ostream(NULL) -{ - assert(out->isWritable); - buf = new _ostreambuf(out); - - rdbuf(buf); -} - -MangleOStream::~MangleOStream() -{ - // Make sure we don't have lingering data on exit - flush(); - delete buf; -} diff --git a/libs/mangle/stream/clients/io_stream.hpp b/libs/mangle/stream/clients/io_stream.hpp deleted file mode 100644 index 98c6252ed..000000000 --- a/libs/mangle/stream/clients/io_stream.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MANGLE_STREAM_IOSTREAM_H -#define MANGLE_STREAM_IOSTREAM_H - -#include -#include "../stream.hpp" -#include - -namespace Mangle { -namespace Stream { - - /** This file contains classes for wrapping an std::istream or - std::ostream around a Mangle::Stream. - - This allows you to use Mangle streams in places that require std - streams. - - This is much easier than trying to make your own custom streams - into iostreams. The std::iostream interface is horrible and NOT - designed for easy subclassing. Create a Mangle::Stream instead, - and use this wrapper. - */ - - // An istream wrapping a readable Mangle::Stream. Has extra - // optimizations for pointer-based streams. - class MangleIStream : public std::istream - { - std::streambuf *buf; - public: - MangleIStream(StreamPtr inp); - ~MangleIStream(); - }; - - // An ostream wrapping a writable Mangle::Stream. - class MangleOStream : public std::ostream - { - std::streambuf *buf; - public: - MangleOStream(StreamPtr inp); - ~MangleOStream(); - }; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/clients/ogre_datastream.hpp b/libs/mangle/stream/clients/ogre_datastream.hpp deleted file mode 100644 index 76a6f20cf..000000000 --- a/libs/mangle/stream/clients/ogre_datastream.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef MANGLE_STREAM_OGRECLIENT_H -#define MANGLE_STREAM_OGRECLIENT_H - -#include -#include -#include "../stream.hpp" - -namespace Mangle { -namespace Stream { - -/** An OGRE DataStream that wraps a Mangle::Stream input. - - This has been built and tested against OGRE 1.6.2. You might have - to make your own modifications if you're working with newer (or - older) versions. - */ -class Mangle2OgreStream : public Ogre::DataStream -{ - StreamPtr inp; - - void init() - { - // Get the size, if possible - if(inp->hasSize) - mSize = inp->size(); - - // Allow writing if inp supports it - if(inp->isWritable) - mAccess |= Ogre::DataStream::WRITE; - } - - public: - /// Constructor without name - Mangle2OgreStream(StreamPtr _inp) - : inp(_inp) { init(); } - - /// Constructor for a named data stream - Mangle2OgreStream(const Ogre::String &name, StreamPtr _inp) - : Ogre::DataStream(name), inp(_inp) { init(); } - - // Only implement the DataStream functions we have to implement - - size_t read(void *buf, size_t count) - { return inp->read(buf,count); } - - size_t write(const void *buf, size_t count) - { assert(inp->isWritable); return inp->write(buf,count); } - - void skip(long count) - { - assert(inp->isSeekable && inp->hasPosition); - inp->seek(inp->tell() + count); - } - - void seek(size_t pos) - { assert(inp->isSeekable); inp->seek(pos); } - - size_t tell() const - { assert(inp->hasPosition); return inp->tell(); } - - bool eof() const { return inp->eof(); } - - /// Does nothing - void close() {} -}; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/filters/buffer_stream.hpp b/libs/mangle/stream/filters/buffer_stream.hpp deleted file mode 100644 index f037212a3..000000000 --- a/libs/mangle/stream/filters/buffer_stream.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef MANGLE_STREAM_BUFFER_H -#define MANGLE_STREAM_BUFFER_H - -#include "../servers/memory_stream.hpp" -#include - -namespace Mangle { -namespace Stream { - -/** A Stream that reads another Stream into a buffer, and serves it as - a MemoryStream. Might be expanded with other capabilities later. - */ - -class BufferStream : public MemoryStream -{ - std::vector buffer; - - public: - /* - input = stream to copy - ADD = each read increment (for streams without size()) - */ - BufferStream(StreamPtr input, size_t ADD = 32*1024) - { - assert(input); - - // Allocate memory, read the stream into it. Then call set() - if(input->hasSize) - { - // We assume that we can get the position as well - assert(input->hasPosition); - - // Calculate how much is left of the stream - size_t left = input->size() - input->tell(); - - // Allocate the buffer and fill it - buffer.resize(left); - input->read(&buffer[0], left); - } - else - { - // We DON'T know how big the stream is. We'll have to read - // it in increments. - size_t len=0, newlen; - - while(!input->eof()) - { - // Read one block - newlen = len + ADD; - buffer.resize(newlen); - size_t read = input->read(&buffer[len], ADD); - - // Increase the total length - len += read; - - // If we read less than expected, we should be at the - // end of the stream - assert(read == ADD || (read < ADD && input->eof())); - } - - // Downsize to match the real length - buffer.resize(len); - } - - // After the buffer has been filled, set up our MemoryStream - // ancestor to reference it. - set(&buffer[0], buffer.size()); - } -}; - -typedef boost::shared_ptr BufferStreamPtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/filters/pure_filter.hpp b/libs/mangle/stream/filters/pure_filter.hpp deleted file mode 100644 index f0ce91f87..000000000 --- a/libs/mangle/stream/filters/pure_filter.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef MANGLE_STREAM_FILTER_H -#define MANGLE_STREAM_FILTER_H - -#include "../stream.hpp" - -namespace Mangle { -namespace Stream { - -/** A stream that filters another stream with no changes. Intended as - a base class for other filters. - */ -class PureFilter : public Stream -{ - protected: - StreamPtr src; - - public: - PureFilter() {} - PureFilter(StreamPtr _src) - { setStream(_src); } - - void setStream(StreamPtr _src) - { - src = _src; - isSeekable = src->isSeekable; - isWritable = src->isWritable; - hasPosition = src->hasPosition; - hasSize = src->hasSize; - hasPtr = src->hasPtr; - } - - size_t read(void *buf, size_t count) { return src->read(buf, count); } - size_t write(const void *buf, size_t count) { return src->write(buf,count); } - void flush() { src->flush(); } - void seek(size_t pos) { src->seek(pos); } - size_t tell() const { return src->tell(); } - size_t size() const { return src->size(); } - bool eof() const { return src->eof(); } - const void *getPtr() { return src->getPtr(); } - const void *getPtr(size_t size) { return src->getPtr(size); } - const void *getPtr(size_t pos, size_t size) - { return src->getPtr(pos, size); } -}; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/filters/slice_stream.hpp b/libs/mangle/stream/filters/slice_stream.hpp deleted file mode 100644 index 6337b9d57..000000000 --- a/libs/mangle/stream/filters/slice_stream.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef MANGLE_STREAM_SLICE_H -#define MANGLE_STREAM_SLICE_H - -#include "../stream.hpp" - -namespace Mangle { -namespace Stream { - -/** A Stream that represents a subset (called a slice) of another stream. - */ -class SliceStream : public Stream -{ - StreamPtr src; - size_t offset, length, pos; - - public: - SliceStream(StreamPtr _src, size_t _offset, size_t _length) - : src(_src), offset(_offset), length(_length), pos(0) - { - assert(src->hasSize); - assert(src->isSeekable); - - // Make sure we can actually fit inside the source stream - assert(src->size() >= offset+length); - - isSeekable = true; - hasPosition = true; - hasSize = true; - hasPtr = src->hasPtr; - isWritable = src->isWritable; - } - - size_t read(void *buf, size_t count) - { - // Check that we're not reading past our slice - if(count > length-pos) - count = length-pos; - - // Seek into place and start reading - src->seek(offset+pos); - count = src->read(buf, count); - - pos += count; - assert(pos <= length); - return count; - } - - // Note that writing to a slice does not allow you to append data, - // you may only overwrite existing data. - size_t write(const void *buf, size_t count) - { - assert(isWritable); - // Check that we're not reading past our slice - if(count > length-pos) - count = length-pos; - - // Seek into place and action - src->seek(offset+pos); - count = src->write(buf, count); - - pos += count; - assert(pos <= length); - return count; - } - - void seek(size_t _pos) - { - pos = _pos; - if(pos > length) pos = length; - } - - bool eof() const { return pos == length; } - size_t tell() const { return pos; } - size_t size() const { return length; } - void flush() { src->flush(); } - - const void *getPtr() { return getPtr(0, length); } - const void *getPtr(size_t size) - { - const void *ptr = getPtr(pos, size); - seek(pos+size); - return ptr; - } - const void *getPtr(size_t pos, size_t size) - { - // Boundry checks on pos and size. Bounding the size is - // important even when getting pointers, as the source stream - // may use the size parameter for something (such as memory - // mapping or buffering.) - if(pos > length) pos = length; - if(pos+size > length) size = length-pos; - - // Ask the source to kindly give us a pointer - return src->getPtr(offset+pos, size); - } -}; - -typedef boost::shared_ptr SliceStreamPtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/servers/file_stream.hpp b/libs/mangle/stream/servers/file_stream.hpp deleted file mode 100644 index 314a49642..000000000 --- a/libs/mangle/stream/servers/file_stream.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MANGLE_STREAM_FILESERVER_H -#define MANGLE_STREAM_FILESERVER_H - -#include "std_stream.hpp" -#include -#include - -namespace Mangle { -namespace Stream { - -/** Very simple file input stream, based on std::ifstream - */ -class FileStream : public StdStream -{ - std::ifstream file; - - public: - FileStream(const std::string &name) - : StdStream(&file) - { - file.open(name.c_str(), std::ios::binary); - - if(file.fail()) - throw std::runtime_error("FileStream: failed to open file " + name); - } - ~FileStream() { file.close(); } -}; - -typedef boost::shared_ptr FileStreamPtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/servers/memory_stream.hpp b/libs/mangle/stream/servers/memory_stream.hpp deleted file mode 100644 index 0849e4a3c..000000000 --- a/libs/mangle/stream/servers/memory_stream.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef MANGLE_STREAM_MEMSERVER_H -#define MANGLE_STREAM_MEMSERVER_H - -#include -#include "../stream.hpp" -#include - -namespace Mangle { -namespace Stream { - -// Do this before the class declaration, since the class itself -// uses it. -class MemoryStream; -typedef boost::shared_ptr MemoryStreamPtr; - -/** A Stream wrapping a memory buffer - - This will create a fully seekable stream out of any pointer/length - pair you give it. - */ -class MemoryStream : public Stream -{ - const void *data; - size_t length, pos; - - public: - MemoryStream(const void *ptr, size_t len) - : data(ptr), length(len), pos(0) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - hasPtr = true; - } - - MemoryStream() - : data(NULL), length(0), pos(0) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - hasPtr = true; - } - - size_t read(void *buf, size_t count) - { - assert(data != NULL); - assert(pos <= length); - - // Don't read more than we have - if(count > (length - pos)) - count = length - pos; - - // Copy data - if(count) - memcpy(buf, ((char*)data)+pos, count); - - // aaand remember to increase the count - pos += count; - - return count; - } - - void seek(size_t _pos) - { - pos = _pos; - if(pos > length) - pos = length; - } - - size_t tell() const { return pos; } - size_t size() const { return length; } - bool eof() const { return pos == length; } - - const void *getPtr() { return data; } - const void *getPtr(size_t size) - { - // This variant of getPtr must move the position pointer - size_t opos = pos; - pos += size; - if(pos > length) pos = length; - return ((char*)data)+opos; - } - const void *getPtr(size_t pos, size_t size) - { - if(pos > length) pos = length; - return ((char*)data)+pos; - } - - // New members in MemoryStream: - - /// Set a new buffer and length. This will rewind the position to zero. - void set(const void* _data, size_t _length) - { - data = _data; - length = _length; - pos = 0; - } - - /// Clone this memory stream - /** Make a new stream of the same buffer. If setPos is true, we also - set the clone's position to be the same as ours. - - No memory is copied during this operation, the new stream is - just another 'view' into the same shared memory buffer. - */ - MemoryStreamPtr clone(bool setPos=false) const - { - MemoryStreamPtr res(new MemoryStream(data, length)); - if(setPos) res->seek(pos); - return res; - } -}; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/servers/ogre_datastream.hpp b/libs/mangle/stream/servers/ogre_datastream.hpp deleted file mode 100644 index a5be98c84..000000000 --- a/libs/mangle/stream/servers/ogre_datastream.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef MANGLE_STREAM_OGRESERVER_H -#define MANGLE_STREAM_OGRESERVER_H - -#include - -namespace Mangle { -namespace Stream { - -/** A Stream wrapping an OGRE DataStream. - - This has been built and tested against OGRE 1.6.2. You might have - to make your own modifications if you're working with newer (or - older) versions. - */ -class OgreStream : public Stream -{ - Ogre::DataStreamPtr inp; - - public: - OgreStream(Ogre::DataStreamPtr _inp) : inp(_inp) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - } - - size_t read(void *buf, size_t count) { return inp->read(buf,count); } - void seek(size_t pos) { inp->seek(pos); } - size_t tell() const { return inp->tell(); } - size_t size() const { return inp->size(); } - bool eof() const { return inp->eof(); } -}; - -typedef boost::shared_ptr OgreStreamPtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/servers/outfile_stream.hpp b/libs/mangle/stream/servers/outfile_stream.hpp deleted file mode 100644 index 8d953d904..000000000 --- a/libs/mangle/stream/servers/outfile_stream.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MANGLE_OSTREAM_FILESERVER_H -#define MANGLE_OSTREAM_FILESERVER_H - -#include "std_ostream.hpp" -#include - -namespace Mangle { -namespace Stream { - -/** File stream based on std::ofstream, only supports writing. - */ -class OutFileStream : public StdOStream -{ - std::ofstream file; - - public: - /** - By default we overwrite the file. If append=true, then we will - open an existing file and seek to the end instead. - */ - OutFileStream(const std::string &name, bool append=false) - : StdOStream(&file) - { - std::ios::openmode mode = std::ios::binary; - if(append) - mode |= std::ios::app; - else - mode |= std::ios::trunc; - - file.open(name.c_str(), mode); - - if(file.fail()) - throw std::runtime_error("OutFileStream: failed to open file " + name); - } - ~OutFileStream() { file.close(); } -}; - -typedef boost::shared_ptr OutFileStreamPtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/servers/phys_stream.hpp b/libs/mangle/stream/servers/phys_stream.hpp deleted file mode 100644 index 4312ac041..000000000 --- a/libs/mangle/stream/servers/phys_stream.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef MANGLE_STREAM_OGRESERVER_H -#define MANGLE_STREAM_OGRESERVER_H - -#include - -namespace Mangle { -namespace Stream { - -/// A Stream wrapping a PHYSFS_file stream from the PhysFS library. -class PhysFile : public Stream -{ - PHYSFS_file *file; - - public: - PhysFile(PHYSFS_file *inp) : file(inp) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - } - - ~PhysFile() { PHYSFS_close(file); } - - size_t read(void *buf, size_t count) - { return PHYSFS_read(file, buf, 1, count); } - - void seek(size_t pos) { PHYSFS_seek(file, pos); } - size_t tell() const { return PHYSFS_tell(file); } - size_t size() const { return PHYSFS_fileLength(file); } - bool eof() const { return PHYSFS_eof(file); } -}; - -typedef boost::shared_ptr PhysFilePtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/servers/std_ostream.hpp b/libs/mangle/stream/servers/std_ostream.hpp deleted file mode 100644 index f406e1a93..000000000 --- a/libs/mangle/stream/servers/std_ostream.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef MANGLE_OSTREAM_STDIOSERVER_H -#define MANGLE_OSTREAM_STDIOSERVER_H - -#include "../stream.hpp" -#include -#include - -namespace Mangle { -namespace Stream { - -/** Simple wrapper for std::ostream, only supports output. - */ -class StdOStream : public Stream -{ - std::ostream *inf; - - static void fail(const std::string &msg) - { throw std::runtime_error("StdOStream: " + msg); } - - public: - StdOStream(std::ostream *_inf) - : inf(_inf) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - isWritable = true; - isReadable = false; - } - - size_t write(const void* buf, size_t len) - { - inf->write((const char*)buf, len); - if(inf->fail()) - fail("error writing to stream"); - // Just return len, but that is ok. The only cases where we would - // return less than len is when an error occured. - return len; - } - - void flush() - { - inf->flush(); - } - - void seek(size_t pos) - { - inf->seekp(pos); - if(inf->fail()) - fail("seek error"); - } - - size_t tell() const - // Hack around the fact that ifstream->tellp() isn't const - { return ((StdOStream*)this)->inf->tellp(); } - - size_t size() const - { - // Use the standard iostream size hack, terrible as it is. - std::streampos pos = inf->tellp(); - inf->seekp(0, std::ios::end); - size_t res = inf->tellp(); - inf->seekp(pos); - - if(inf->fail()) - fail("could not get stream size"); - - return res; - } - - bool eof() const - { return inf->eof(); } -}; - -typedef boost::shared_ptr StdOStreamPtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/servers/std_stream.hpp b/libs/mangle/stream/servers/std_stream.hpp deleted file mode 100644 index 163f023f6..000000000 --- a/libs/mangle/stream/servers/std_stream.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef MANGLE_STREAM_STDIOSERVER_H -#define MANGLE_STREAM_STDIOSERVER_H - -#include "../stream.hpp" -#include -#include - -namespace Mangle { -namespace Stream { - -/** Simple wrapper for std::istream. - */ -class StdStream : public Stream -{ - std::istream *inf; - - static void fail(const std::string &msg) - { throw std::runtime_error("StdStream: " + msg); } - - public: - StdStream(std::istream *_inf) - : inf(_inf) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - } - - size_t read(void* buf, size_t len) - { - inf->read((char*)buf, len); - if(inf->bad()) - fail("error reading from stream"); - return inf->gcount(); - } - - void seek(size_t pos) - { - inf->clear(); - inf->seekg(pos); - if(inf->fail()) - fail("seek error"); - } - - size_t tell() const - // Hack around the fact that ifstream->tellg() isn't const - { return ((StdStream*)this)->inf->tellg(); } - - size_t size() const - { - // Use the standard iostream size hack, terrible as it is. - std::streampos pos = inf->tellg(); - inf->seekg(0, std::ios::end); - size_t res = inf->tellg(); - inf->seekg(pos); - - if(inf->fail()) - fail("could not get stream size"); - - return res; - } - - bool eof() const - { return inf->eof(); } -}; - -typedef boost::shared_ptr StdStreamPtr; - -}} // namespaces -#endif From bd68f7bd3380521393c9f05ee92b1fbb374bf195 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 08:51:51 -0700 Subject: [PATCH 144/688] Remove final direct uses of Mangle::Stream --- components/nifogre/ogre_nif_loader.hpp | 1 - components/terrain/esm_land_factory.cpp | 34 ---- components/terrain/esm_land_factory.hpp | 4 - components/terrain/land_factory.hpp | 12 -- libs/mangle/stream/stream.hpp | 103 ---------- libs/mangle/stream/tests/.gitignore | 2 - libs/mangle/stream/tests/Makefile | 34 ---- .../stream/tests/audiere_client_test.cpp | 34 ---- .../stream/tests/buffer_filter_test.cpp | 41 ---- libs/mangle/stream/tests/file_server_test.cpp | 18 -- libs/mangle/stream/tests/file_write_test.cpp | 41 ---- libs/mangle/stream/tests/iostream_test.cpp | 176 ------------------ .../stream/tests/memory_server_test.cpp | 42 ----- libs/mangle/stream/tests/ogre_client_test.cpp | 22 --- .../tests/output/audiere_client_test.out | 9 - .../tests/output/buffer_filter_test.out | 22 --- .../stream/tests/output/file_server_test.out | 3 - .../stream/tests/output/file_write_test.out | 12 -- .../stream/tests/output/iostream_test.out | 32 ---- .../tests/output/memory_server_test.out | 23 --- .../stream/tests/output/ogre_client_test.out | 5 - .../stream/tests/output/slice_filter_test.out | 36 ---- .../mangle/stream/tests/slice_filter_test.cpp | 42 ----- libs/mangle/stream/tests/test.sh | 18 -- 24 files changed, 766 deletions(-) delete mode 100644 libs/mangle/stream/stream.hpp delete mode 100644 libs/mangle/stream/tests/.gitignore delete mode 100644 libs/mangle/stream/tests/Makefile delete mode 100644 libs/mangle/stream/tests/audiere_client_test.cpp delete mode 100644 libs/mangle/stream/tests/buffer_filter_test.cpp delete mode 100644 libs/mangle/stream/tests/file_server_test.cpp delete mode 100644 libs/mangle/stream/tests/file_write_test.cpp delete mode 100644 libs/mangle/stream/tests/iostream_test.cpp delete mode 100644 libs/mangle/stream/tests/memory_server_test.cpp delete mode 100644 libs/mangle/stream/tests/ogre_client_test.cpp delete mode 100644 libs/mangle/stream/tests/output/audiere_client_test.out delete mode 100644 libs/mangle/stream/tests/output/buffer_filter_test.out delete mode 100644 libs/mangle/stream/tests/output/file_server_test.out delete mode 100644 libs/mangle/stream/tests/output/file_write_test.out delete mode 100644 libs/mangle/stream/tests/output/iostream_test.out delete mode 100644 libs/mangle/stream/tests/output/memory_server_test.out delete mode 100644 libs/mangle/stream/tests/output/ogre_client_test.out delete mode 100644 libs/mangle/stream/tests/output/slice_filter_test.out delete mode 100644 libs/mangle/stream/tests/slice_filter_test.cpp delete mode 100755 libs/mangle/stream/tests/test.sh diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 64efc70c7..0620ddf49 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -31,7 +31,6 @@ #include #include -#include #include "../nif/nif_file.hpp" #include "../nif/node.hpp" #include "../nif/data.hpp" diff --git a/components/terrain/esm_land_factory.cpp b/components/terrain/esm_land_factory.cpp index 315188234..a6335c6dc 100644 --- a/components/terrain/esm_land_factory.cpp +++ b/components/terrain/esm_land_factory.cpp @@ -8,41 +8,7 @@ using namespace Terrain; -static class ESMLandStream : public Mangle::Stream -{ -public: - ESMLandStream(ESM::Land *l, ESM::ESMReader &r) - { - } -}; - bool ESMLandFactory::has(int x, int y) { return store.landscapes.has(x,y); } - -LandDataPtr get(int x, int y, LandInfo &info) -{ - assert(has(x,y)); - - // Set up the info - info.grid = LGT_Quadratic; - info.data = LDT_Float; - - const float SIZE = 8192; // CHECK - - info.xsize = SIZE; - info.ysize = SIZE; - info.numx = 65; - info.numy = 65; - info.xoffset = SIZE*x; - info.yoffset = SIZE*y; - - // Get the Land struct from store - ESM::Land* land = store.landscapes.find(x,y); - assert(land->hasData); - - // Create a stream for the data and return it. - LandDataPtr ptr(new ESMLandStream(land, reader)); - return ptr; -} diff --git a/components/terrain/esm_land_factory.hpp b/components/terrain/esm_land_factory.hpp index 4fd95caa9..bb1f9a8c6 100644 --- a/components/terrain/esm_land_factory.hpp +++ b/components/terrain/esm_land_factory.hpp @@ -32,10 +32,6 @@ namespace Terrain // True if this factory has any data for the given grid cell. bool has(int x, int y); - - // Return stream to data for this cell. Additional data about the - // landscape is returned through the LandInfo struct. - LandDataPtr get(int x, int y, LandInfo &info); }; } #endif diff --git a/components/terrain/land_factory.hpp b/components/terrain/land_factory.hpp index f41946b49..c4d160443 100644 --- a/components/terrain/land_factory.hpp +++ b/components/terrain/land_factory.hpp @@ -1,8 +1,6 @@ #ifndef TERRAIN_LAND_FACTORY_H #define TERRAIN_LAND_FACTORY_H -#include - namespace Terrain { enum LandInfoGridType @@ -32,12 +30,6 @@ namespace Terrain float xoffset, yoffset; }; - /* We use normal streams for data. This allows us to just pass (for - example) a file stream as height map input later, with no extra - fuzz. - */ - typedef Mangle::Stream::StreamPtr LandDataPtr; - /* Factory class that provides streams to land data cells. Each "cell" has a unique integer coordinate in the plane. @@ -46,10 +38,6 @@ namespace Terrain { // True if this factory has any data for the given grid cell. virtual bool has(int x, int y) = 0; - - // Return stream to data for this cell. Additional data about the - // landscape is returned through the LandInfo struct. - virtual LandDataPtr get(int x, int y, LandInfo &info) = 0; }; } #endif diff --git a/libs/mangle/stream/stream.hpp b/libs/mangle/stream/stream.hpp deleted file mode 100644 index 2ee4fcbd8..000000000 --- a/libs/mangle/stream/stream.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef MANGLE_STREAM_INPUT_H -#define MANGLE_STREAM_INPUT_H - -#include -#include "../tools/shared_ptr.hpp" -#include - -namespace Mangle { -namespace Stream { - -/// An abstract interface for a stream data. -class Stream -{ - public: - // Feature options. These should be set in the constructor. - - /// If true, seek() works - bool isSeekable; - - /// If true, tell() works - bool hasPosition; - - /// If true, size() works - bool hasSize; - - /// If true, write() works. Writing through pointer operations is - /// not (yet) supported. - bool isWritable; - - /// If true, read() and eof() works. - bool isReadable; - - /// If true, the getPtr() functions work - bool hasPtr; - - /// Initialize all bools to false by default, except isReadable. - Stream() : - isSeekable(false), hasPosition(false), hasSize(false), - isWritable(false), isReadable(true), hasPtr(false) {} - - /// Virtual destructor - virtual ~Stream() {} - - /** Read a given number of bytes from the stream. Returns the actual - number read. If the return value is less than count, then the - stream is empty or an error occured. Only required for readable - streams. - */ - virtual size_t read(void* buf, size_t count) { assert(0); return 0; } - - /** Write a given number of bytes from the stream. Semantics is - similar to read(). Only valid if isWritable is true. - - The returned value is the number of bytes written. However in - most cases, unlike for read(), a write-count less than requested - usually indicates an error. The implementation should throw such - errors as exceptions rather than expect the caller to handle - them. - - Since most implementations do NOT support writing we default to - an assert(0) here. - */ - virtual size_t write(const void *buf, size_t count) { assert(0); return 0; } - - /// Flush an output stream. Does nothing for non-writing streams. - virtual void flush() {} - - /// Seek to an absolute position in this stream. Not all streams are - /// seekable. - virtual void seek(size_t pos) { assert(0); } - - /// Get the current position in the stream. Non-seekable streams are - /// not required to keep track of this. - virtual size_t tell() const { assert(0); return 0; } - - /// Return the total size of the stream. For streams hasSize is - /// false, size() should fail in some way, since it is an error to - /// call it in those cases. - virtual size_t size() const { assert(0); return 0; } - - /// Returns true if the stream is empty. Required for readable - /// streams. - virtual bool eof() const { assert(0); return 0; } - - /// Return a pointer to the entire stream. This function (and the - /// other getPtr() variants below) should only be implemented for - /// memory-based streams where using them would be an optimization. - virtual const void *getPtr() { assert(0); return NULL; } - - /// Get a pointer to a memory region of 'size' bytes starting from - /// position 'pos' - virtual const void *getPtr(size_t pos, size_t size) { assert(0); return NULL; } - - /// Get a pointer to a memory region of 'size' bytes from the - /// current position. Unlike the two other getPtr variants, this - /// will advance the position past the returned area. - virtual const void *getPtr(size_t size) { assert(0); return NULL; } -}; - -typedef boost::shared_ptr StreamPtr; - -}} // namespaces -#endif diff --git a/libs/mangle/stream/tests/.gitignore b/libs/mangle/stream/tests/.gitignore deleted file mode 100644 index 9dfd618e2..000000000 --- a/libs/mangle/stream/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*_test -test.file diff --git a/libs/mangle/stream/tests/Makefile b/libs/mangle/stream/tests/Makefile deleted file mode 100644 index 5f4397819..000000000 --- a/libs/mangle/stream/tests/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -GCC=g++ -I../ -Wall -Werror - -all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test iostream_test - -I_OGRE=$(shell pkg-config --cflags OGRE) -L_OGRE=$(shell pkg-config --libs OGRE) -L_AUDIERE=-laudiere - -ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream.hpp - $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) - -audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp - $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) - -iostream_test: iostream_test.cpp ../clients/io_stream.cpp - $(GCC) $^ -o $@ - -file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp - $(GCC) $< -o $@ - -file_write_test: file_write_test.cpp ../stream.hpp ../servers/outfile_stream.hpp ../servers/std_ostream.hpp - $(GCC) $< -o $@ - -memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp - $(GCC) $< -o $@ - -buffer_filter_test: buffer_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/buffer_stream.hpp - $(GCC) $< -o $@ - -slice_filter_test: slice_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/slice_stream.hpp - $(GCC) $< -o $@ - -clean: - rm *_test diff --git a/libs/mangle/stream/tests/audiere_client_test.cpp b/libs/mangle/stream/tests/audiere_client_test.cpp deleted file mode 100644 index 82be569cc..000000000 --- a/libs/mangle/stream/tests/audiere_client_test.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "../servers/memory_stream.hpp" -#include "../clients/audiere_file.hpp" -#include -#include - -using namespace Mangle::Stream; -using namespace audiere; -using namespace std; - -int main() -{ - char str[12]; - memset(str, 0, 12); - StreamPtr inp(new MemoryStream("hello world", 11)); - FilePtr p(new AudiereFile(inp)); - cout << "pos=" << p->tell() << endl; - p->read(str, 2); - cout << "2 bytes: " << str << endl; - cout << "pos=" << p->tell() << endl; - p->seek(4, File::BEGIN); - cout << "pos=" << p->tell() << endl; - p->read(str, 3); - cout << "3 bytes: " << str << endl; - p->seek(-1, File::CURRENT); - cout << "pos=" << p->tell() << endl; - p->seek(-4, File::END); - cout << "pos=" << p->tell() << endl; - p->read(str, 4); - cout << "last 4 bytes: " << str << endl; - p->seek(0, File::BEGIN); - p->read(str, 11); - cout << "entire stream: " << str << endl; - return 0; -} diff --git a/libs/mangle/stream/tests/buffer_filter_test.cpp b/libs/mangle/stream/tests/buffer_filter_test.cpp deleted file mode 100644 index e53bda651..000000000 --- a/libs/mangle/stream/tests/buffer_filter_test.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -#include "../filters/buffer_stream.hpp" - -using namespace Mangle::Stream; -using namespace std; - -int main() -{ - StreamPtr orig (new MemoryStream("hello world", 11)); - StreamPtr inp (new BufferStream(orig)); - - cout << "Size: " << inp->size() << endl; - cout << "Pos: " << inp->tell() << "\nSeeking...\n"; - inp->seek(3); - cout << "Pos: " << inp->tell() << endl; - char data[12]; - memset(data, 0, 12); - cout << "Reading: " << inp->read(data, 4) << endl; - cout << "Four bytes: " << data << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << "\nSeeking again...\n"; - inp->seek(33); - cout << "Pos: " << inp->tell() << endl; - cout << "Eof: " << inp->eof() << "\nSeek to 6\n"; - inp->seek(6); - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << endl; - cout << "Over-reading: " << inp->read(data, 200) << endl; - cout << "Result: " << data << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << endl; - inp->seek(0); - cout << "Finally, reading the entire string: " << inp->read(data,11) << endl; - cout << "Result: " << data << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << endl; - - return 0; -} diff --git a/libs/mangle/stream/tests/file_server_test.cpp b/libs/mangle/stream/tests/file_server_test.cpp deleted file mode 100644 index 3e1e3cfa5..000000000 --- a/libs/mangle/stream/tests/file_server_test.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "../servers/file_stream.hpp" -#include - -using namespace Mangle::Stream; -using namespace std; - -int main() -{ - StreamPtr inp(new FileStream("file_server_test.cpp")); - - char buf[21]; - buf[20] = 0; - cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl; - inp->read(buf, 20); - cout << "First 20 bytes: " << buf << endl; - cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl; - return 0; -} diff --git a/libs/mangle/stream/tests/file_write_test.cpp b/libs/mangle/stream/tests/file_write_test.cpp deleted file mode 100644 index c6c61fcca..000000000 --- a/libs/mangle/stream/tests/file_write_test.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "../servers/outfile_stream.hpp" -#include - -using namespace Mangle::Stream; -using namespace std; - -void print(Stream &str) -{ - cout << "size=" << str.size() - << " pos=" << str.tell() - << " eof=" << str.eof() - << endl; -} - -int main() -{ - { - cout << "\nCreating file\n"; - OutFileStream out("test.file"); - print(out); - out.write("hello",5); - print(out); - } - - { - cout << "\nAppending to file\n"; - OutFileStream out("test.file", true); - print(out); - out.write(" again\n",7); - print(out); - } - - { - cout << "\nOverwriting file\n"; - OutFileStream out("test.file"); - print(out); - out.write("overwrite!\n",11); - print(out); - } - return 0; -} diff --git a/libs/mangle/stream/tests/iostream_test.cpp b/libs/mangle/stream/tests/iostream_test.cpp deleted file mode 100644 index 60648c6c5..000000000 --- a/libs/mangle/stream/tests/iostream_test.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include -#include "../clients/io_stream.hpp" -#include "../servers/memory_stream.hpp" - -using namespace Mangle::Stream; -using namespace std; - -void test1() -{ - cout << "Testing ASCII reading from memory:\n"; - StreamPtr input(new MemoryStream("hello you world you", 19)); - MangleIStream inp(input); - - string str; - while(!inp.eof()) - { - inp >> str; - cout << "Got: " << str << endl; - } -} - -class Dummy : public Stream -{ - int count; - -public: - - Dummy() : count(0) - { - } - - size_t read(void *ptr, size_t num) - { - char *p = (char*)ptr; - char *start = p; - for(; (count < 2560) && (p-start < (int)num); count++) - { - *p = count / 10; - p++; - } - return p-start; - } - - bool eof() const { return count == 2560; } -}; - -void test2() -{ - cout << "\nTesting binary reading from non-memory:\n"; - - StreamPtr input(new Dummy); - MangleIStream inp(input); - - int x = 0; - while(!inp.eof()) - { - unsigned char buf[5]; - inp.read((char*)buf,5); - - // istream doesn't set eof() until we read _beyond_ the end of - // the stream, so we need an extra check. - if(inp.gcount() == 0) break; - - /* - for(int i=0;i<5;i++) - cout << (int)buf[i] << " "; - cout << endl; - */ - - assert(buf[4] == buf[0]); - assert(buf[0] == x/2); - x++; - } - cout << " Done\n"; -} - -struct Dummy2 : Stream -{ - Dummy2() - { - isWritable = true; - isReadable = false; - } - - size_t write(const void *ptr, size_t num) - { - const char *p = (const char*)ptr; - cout << " Got: "; - for(unsigned i=0;iwrite("testing", 7); - - cout << " Running through MangleOStream:\n"; - MangleOStream out(output); - out << "hello"; - out << " - are you ok?"; - cout << " Flushing:\n"; - out.flush(); - - cout << " Writing a hell of a lot of characters:\n"; - for(int i=0; i<127; i++) - out << "xxxxxxxx"; // 127 * 8 = 1016 - out << "fffffff"; // +7 = 1023 - cout << " Just one more:\n"; - out << "y"; - cout << " And oooone more:\n"; - out << "z"; - - cout << " Flushing again:\n"; - out.flush(); - cout << " Writing some more and exiting:\n"; - out << "blah bleh blob"; -} - -struct Dummy3 : Stream -{ - int pos; - - Dummy3() : pos(0) - { - hasPosition = true; - isSeekable = true; - } - - size_t read(void*, size_t num) - { - cout << " Reading " << num << " bytes from " << pos << endl; - pos += num; - return num; - } - - void seek(size_t npos) { pos = npos; } - size_t tell() const { return pos; } -}; - -void test4() -{ - cout << "\nTesting seeking;\n"; - StreamPtr input(new Dummy3); - - cout << " Direct reading:\n"; - input->read(0,10); - input->read(0,5); - - MangleIStream inp(input); - - cout << " Reading from istream:\n"; - char buf[20]; - inp.read(buf, 20); - inp.read(buf, 20); - inp.read(buf, 20); - - cout << " Seeking to 30 and reading again:\n"; - inp.seekg(30); - inp.read(buf, 20); -} - -int main() -{ - test1(); - test2(); - test3(); - test4(); - return 0; -} diff --git a/libs/mangle/stream/tests/memory_server_test.cpp b/libs/mangle/stream/tests/memory_server_test.cpp deleted file mode 100644 index 24d3bb17e..000000000 --- a/libs/mangle/stream/tests/memory_server_test.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include - -#include "../servers/memory_stream.hpp" - -using namespace Mangle::Stream; -using namespace std; - -int main() -{ - Stream* inp = new MemoryStream("hello world\0", 12); - - cout << "Size: " << inp->size() << endl; - cout << "Pos: " << inp->tell() << "\nSeeking...\n"; - inp->seek(3); - cout << "Pos: " << inp->tell() << endl; - char data[12]; - memset(data, 0, 12); - cout << "Reading: " << inp->read(data, 4) << endl; - cout << "Four bytes: " << data << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << "\nSeeking again...\n"; - inp->seek(33); - cout << "Pos: " << inp->tell() << endl; - cout << "Eof: " << inp->eof() << "\nSeek to 6\n"; - inp->seek(6); - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << endl; - cout << "Over-reading: " << inp->read(data, 200) << endl; - cout << "Result: " << data << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << endl; - inp->seek(0); - cout << "Finally, reading the entire string: " << inp->read(data,11) << endl; - cout << "Result: " << data << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Pos: " << inp->tell() << endl; - - cout << "Entire stream from pointer: " << (char*)inp->getPtr() << endl; - - return 0; -} diff --git a/libs/mangle/stream/tests/ogre_client_test.cpp b/libs/mangle/stream/tests/ogre_client_test.cpp deleted file mode 100644 index c8d0442c0..000000000 --- a/libs/mangle/stream/tests/ogre_client_test.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "../servers/memory_stream.hpp" -#include "../clients/ogre_datastream.hpp" -#include - -using namespace Mangle::Stream; -using namespace Ogre; -using namespace std; - -int main() -{ - StreamPtr inp(new MemoryStream("hello world", 11)); - DataStreamPtr p(new Mangle2OgreStream("hello", inp)); - cout << "Name: " << p->getName() << endl; - cout << "As string: " << p->getAsString() << endl; - cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; - p->seek(0); - cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; - p->skip(5); - p->skip(-2); - cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; - return 0; -} diff --git a/libs/mangle/stream/tests/output/audiere_client_test.out b/libs/mangle/stream/tests/output/audiere_client_test.out deleted file mode 100644 index 2130db9fd..000000000 --- a/libs/mangle/stream/tests/output/audiere_client_test.out +++ /dev/null @@ -1,9 +0,0 @@ -pos=0 -2 bytes: he -pos=2 -pos=4 -3 bytes: o w -pos=6 -pos=7 -last 4 bytes: orld -entire stream: hello world diff --git a/libs/mangle/stream/tests/output/buffer_filter_test.out b/libs/mangle/stream/tests/output/buffer_filter_test.out deleted file mode 100644 index 0ca252f25..000000000 --- a/libs/mangle/stream/tests/output/buffer_filter_test.out +++ /dev/null @@ -1,22 +0,0 @@ -Size: 11 -Pos: 0 -Seeking... -Pos: 3 -Reading: 4 -Four bytes: lo w -Eof: 0 -Pos: 7 -Seeking again... -Pos: 11 -Eof: 1 -Seek to 6 -Eof: 0 -Pos: 6 -Over-reading: 5 -Result: world -Eof: 1 -Pos: 11 -Finally, reading the entire string: 11 -Result: hello world -Eof: 1 -Pos: 11 diff --git a/libs/mangle/stream/tests/output/file_server_test.out b/libs/mangle/stream/tests/output/file_server_test.out deleted file mode 100644 index 240f066a3..000000000 --- a/libs/mangle/stream/tests/output/file_server_test.out +++ /dev/null @@ -1,3 +0,0 @@ -pos=0 eof=0 -First 20 bytes: #include "../servers -pos=20 eof=0 diff --git a/libs/mangle/stream/tests/output/file_write_test.out b/libs/mangle/stream/tests/output/file_write_test.out deleted file mode 100644 index 34b07c49f..000000000 --- a/libs/mangle/stream/tests/output/file_write_test.out +++ /dev/null @@ -1,12 +0,0 @@ - -Creating file -size=0 pos=0 eof=0 -size=5 pos=5 eof=0 - -Appending to file -size=5 pos=5 eof=0 -size=12 pos=12 eof=0 - -Overwriting file -size=0 pos=0 eof=0 -size=11 pos=11 eof=0 diff --git a/libs/mangle/stream/tests/output/iostream_test.out b/libs/mangle/stream/tests/output/iostream_test.out deleted file mode 100644 index b6da80c80..000000000 --- a/libs/mangle/stream/tests/output/iostream_test.out +++ /dev/null @@ -1,32 +0,0 @@ -Testing ASCII reading from memory: -Got: hello -Got: you -Got: world -Got: you - -Testing binary reading from non-memory: - Done - -Writing to dummy stream: - Pure dummy test: - Got: t e s t i n g - Running through MangleOStream: - Flushing: - Got: h e l l o - a r e y o u o k ? - Writing a hell of a lot of characters: - Just one more: - And oooone more: - Got: x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x f f f f f f f y - Flushing again: - Got: z - Writing some more and exiting: - Got: b l a h b l e h b l o b - -Testing seeking; - Direct reading: - Reading 10 bytes from 0 - Reading 5 bytes from 10 - Reading from istream: - Reading 1024 bytes from 15 - Seeking to 30 and reading again: - Reading 1024 bytes from 30 diff --git a/libs/mangle/stream/tests/output/memory_server_test.out b/libs/mangle/stream/tests/output/memory_server_test.out deleted file mode 100644 index 7cd9533e7..000000000 --- a/libs/mangle/stream/tests/output/memory_server_test.out +++ /dev/null @@ -1,23 +0,0 @@ -Size: 12 -Pos: 0 -Seeking... -Pos: 3 -Reading: 4 -Four bytes: lo w -Eof: 0 -Pos: 7 -Seeking again... -Pos: 12 -Eof: 1 -Seek to 6 -Eof: 0 -Pos: 6 -Over-reading: 6 -Result: world -Eof: 1 -Pos: 12 -Finally, reading the entire string: 11 -Result: hello world -Eof: 0 -Pos: 11 -Entire stream from pointer: hello world diff --git a/libs/mangle/stream/tests/output/ogre_client_test.out b/libs/mangle/stream/tests/output/ogre_client_test.out deleted file mode 100644 index 62cd14604..000000000 --- a/libs/mangle/stream/tests/output/ogre_client_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Name: hello -As string: hello world -pos=11 eof=1 -pos=0 eof=0 -pos=3 eof=0 diff --git a/libs/mangle/stream/tests/output/slice_filter_test.out b/libs/mangle/stream/tests/output/slice_filter_test.out deleted file mode 100644 index 6d84704a7..000000000 --- a/libs/mangle/stream/tests/output/slice_filter_test.out +++ /dev/null @@ -1,36 +0,0 @@ - -Slice 1: --------- -Size: 6 -Pos: 0 -Seeking... -Reading 6 bytes -Result: hello -Pos: 6 -Eof: 1 -Seeking: -Pos: 2 -Eof: 0 -Reading 4 bytes -Result: llo -Pos: 6 -Eof: 1 -Entire stream as pointer: hello - -Slice 2: --------- -Size: 6 -Pos: 0 -Seeking... -Reading 6 bytes -Result: world -Pos: 6 -Eof: 1 -Seeking: -Pos: 2 -Eof: 0 -Reading 4 bytes -Result: rld -Pos: 6 -Eof: 1 -Entire stream as pointer: world diff --git a/libs/mangle/stream/tests/slice_filter_test.cpp b/libs/mangle/stream/tests/slice_filter_test.cpp deleted file mode 100644 index da90e24ad..000000000 --- a/libs/mangle/stream/tests/slice_filter_test.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include - -#include "../filters/slice_stream.hpp" -#include "../servers/memory_stream.hpp" - -using namespace Mangle::Stream; -using namespace std; - -void test(StreamPtr inp) -{ - cout << "Size: " << inp->size() << endl; - cout << "Pos: " << inp->tell() << "\nSeeking...\n"; - char data[6]; - memset(data, 0, 6); - cout << "Reading " << inp->read(data, 6) << " bytes\n"; - cout << "Result: " << data << endl; - cout << "Pos: " << inp->tell() << endl; - cout << "Eof: " << inp->eof() << endl; - inp->seek(2); - cout << "Seeking:\nPos: " << inp->tell() << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Reading " << inp->read(data, 6) << " bytes\n"; - cout << "Result: " << data << endl; - cout << "Pos: " << inp->tell() << endl; - cout << "Eof: " << inp->eof() << endl; - cout << "Entire stream as pointer: " << (char*)inp->getPtr() << endl; -} - -int main() -{ - StreamPtr orig (new MemoryStream("hello\0world\0", 12)); - StreamPtr slice1 (new SliceStream(orig,0,6)); - StreamPtr slice2 (new SliceStream(orig,6,6)); - - cout << "\nSlice 1:\n--------\n"; - test(slice1); - cout << "\nSlice 2:\n--------\n"; - test(slice2); - - return 0; -} diff --git a/libs/mangle/stream/tests/test.sh b/libs/mangle/stream/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/libs/mangle/stream/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done From b353cfd457e94ecfda4a78c1de2e0b473bd19b26 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 08:54:00 -0700 Subject: [PATCH 145/688] Remove unused Mangle::VFS --- libs/mangle/vfs/clients/ogre_archive.cpp | 85 ------------- libs/mangle/vfs/clients/ogre_archive.hpp | 59 --------- libs/mangle/vfs/servers/ogre_vfs.cpp | 61 --------- libs/mangle/vfs/servers/ogre_vfs.hpp | 70 ----------- libs/mangle/vfs/servers/physfs_vfs.hpp | 71 ----------- libs/mangle/vfs/tests/.gitignore | 1 - libs/mangle/vfs/tests/Makefile | 25 ---- libs/mangle/vfs/tests/dummy_test.cpp | 42 ------- libs/mangle/vfs/tests/dummy_vfs.cpp | 117 ------------------ libs/mangle/vfs/tests/ogre_client_test.cpp | 39 ------ libs/mangle/vfs/tests/ogre_resource_test.cpp | 61 --------- libs/mangle/vfs/tests/ogre_server_test.cpp | 38 ------ libs/mangle/vfs/tests/output/dummy_test.out | 31 ----- .../vfs/tests/output/ogre_client_test.out | 12 -- .../vfs/tests/output/ogre_resource_test.out | 20 --- .../vfs/tests/output/ogre_server_test.out | 11 -- .../vfs/tests/output/physfs_server_test.out | 11 -- libs/mangle/vfs/tests/physfs_server_test.cpp | 21 ---- libs/mangle/vfs/tests/server_common.cpp | 33 ----- libs/mangle/vfs/tests/test.sh | 18 --- libs/mangle/vfs/tests/test.zip | Bin 169 -> 0 bytes libs/mangle/vfs/vfs.hpp | 87 ------------- 22 files changed, 913 deletions(-) delete mode 100644 libs/mangle/vfs/clients/ogre_archive.cpp delete mode 100644 libs/mangle/vfs/clients/ogre_archive.hpp delete mode 100644 libs/mangle/vfs/servers/ogre_vfs.cpp delete mode 100644 libs/mangle/vfs/servers/ogre_vfs.hpp delete mode 100644 libs/mangle/vfs/servers/physfs_vfs.hpp delete mode 100644 libs/mangle/vfs/tests/.gitignore delete mode 100644 libs/mangle/vfs/tests/Makefile delete mode 100644 libs/mangle/vfs/tests/dummy_test.cpp delete mode 100644 libs/mangle/vfs/tests/dummy_vfs.cpp delete mode 100644 libs/mangle/vfs/tests/ogre_client_test.cpp delete mode 100644 libs/mangle/vfs/tests/ogre_resource_test.cpp delete mode 100644 libs/mangle/vfs/tests/ogre_server_test.cpp delete mode 100644 libs/mangle/vfs/tests/output/dummy_test.out delete mode 100644 libs/mangle/vfs/tests/output/ogre_client_test.out delete mode 100644 libs/mangle/vfs/tests/output/ogre_resource_test.out delete mode 100644 libs/mangle/vfs/tests/output/ogre_server_test.out delete mode 100644 libs/mangle/vfs/tests/output/physfs_server_test.out delete mode 100644 libs/mangle/vfs/tests/physfs_server_test.cpp delete mode 100644 libs/mangle/vfs/tests/server_common.cpp delete mode 100755 libs/mangle/vfs/tests/test.sh delete mode 100644 libs/mangle/vfs/tests/test.zip delete mode 100644 libs/mangle/vfs/vfs.hpp diff --git a/libs/mangle/vfs/clients/ogre_archive.cpp b/libs/mangle/vfs/clients/ogre_archive.cpp deleted file mode 100644 index 2d3f7c520..000000000 --- a/libs/mangle/vfs/clients/ogre_archive.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "ogre_archive.hpp" - -#include "../../stream/clients/ogre_datastream.hpp" - -using namespace Mangle::VFS; -using namespace Mangle::Stream; - -Ogre::DataStreamPtr MangleArchive::open(const Ogre::String& filename) const -{ - return Ogre::DataStreamPtr(new Mangle2OgreStream - (filename, vfs->open(filename))); -} - -static void fill(Ogre::FileInfoList &out, FileInfoList &in) -{ - int size = in.size(); - out.resize(size); - - for(int i=0; ihasList); - FileInfoListPtr lst = vfs->list("", recursive, dirs); - Ogre::StringVector *res = new Ogre::StringVector; - - fill(*res, *lst); - - return Ogre::StringVectorPtr(res); -} - -Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs) -{ - assert(vfs->hasList); - FileInfoListPtr lst = vfs->list("", recursive, dirs); - Ogre::FileInfoList *res = new Ogre::FileInfoList; - - fill(*res, *lst); - - return Ogre::FileInfoListPtr(res); -} - -// Find functions will only work if vfs->hasFind is set. -Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern, - bool recursive, - bool dirs) -{ - assert(vfs->hasFind); - FileInfoListPtr lst = vfs->find(pattern, recursive, dirs); - Ogre::StringVector *res = new Ogre::StringVector; - - fill(*res, *lst); - - return Ogre::StringVectorPtr(res); -} - -Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, - bool recursive, - bool dirs) -{ - assert(vfs->hasFind); - FileInfoListPtr lst = vfs->find(pattern, recursive, dirs); - Ogre::FileInfoList *res = new Ogre::FileInfoList; - - fill(*res, *lst); - - return Ogre::FileInfoListPtr(res); -} diff --git a/libs/mangle/vfs/clients/ogre_archive.hpp b/libs/mangle/vfs/clients/ogre_archive.hpp deleted file mode 100644 index 7b371c6ef..000000000 --- a/libs/mangle/vfs/clients/ogre_archive.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef MANGLE_VFS_OGRECLIENT_H -#define MANGLE_VFS_OGRECLIENT_H - -#include -#include -#include "../vfs.hpp" - -namespace Mangle { -namespace VFS { - -/** An OGRE Archive implementation that wraps a Mangle::VFS - filesystem. - - This has been built and tested against OGRE 1.6.2, and has been - extended for OGRE 1.7. You might have to make your own - modifications if you're working with newer (or older) versions. - */ -class MangleArchive : public Ogre::Archive -{ - VFSPtr vfs; - - public: - MangleArchive(VFSPtr _vfs, const std::string &name, - const std::string &archType = "Mangle") - : Ogre::Archive(name, archType) - , vfs(_vfs) - {} - - bool isCaseSensitive() const { return vfs->isCaseSensitive; } - - // These do nothing. You have to load / unload the archive in the - // constructor/destructor. - void load() {} - void unload() {} - - bool exists(const Ogre::String& filename) - { return vfs->isFile(filename); } - - time_t getModifiedTime(const Ogre::String& filename) - { return vfs->stat(filename)->time; } - - // With both these we support both OGRE 1.6 and 1.7 - Ogre::DataStreamPtr open(const Ogre::String& filename) const; - Ogre::DataStreamPtr open(const Ogre::String& filename, bool readOnly) const - { return open(filename); } - - Ogre::StringVectorPtr list(bool recursive = true, bool dirs = false); - Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false); - - // Find functions will only work if vfs->hasFind is set. - Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true, - bool dirs = false); - Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, - bool recursive = true, - bool dirs = false); -}; - -}} // namespaces -#endif diff --git a/libs/mangle/vfs/servers/ogre_vfs.cpp b/libs/mangle/vfs/servers/ogre_vfs.cpp deleted file mode 100644 index 7f728c1b0..000000000 --- a/libs/mangle/vfs/servers/ogre_vfs.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "ogre_vfs.hpp" -#include "../../stream/servers/ogre_datastream.hpp" - -using namespace Mangle::VFS; -using namespace Mangle::Stream; - -OgreVFS::OgreVFS(const std::string &_group) - : group(_group) -{ - hasList = true; - hasFind = true; - isCaseSensitive = true; - - // Get the group manager once - gm = Ogre::ResourceGroupManager::getSingletonPtr(); - - // Use the default group if none was specified - if(group.empty()) - group = gm->getWorldResourceGroupName(); -} - -StreamPtr OgreVFS::open(const std::string &name) -{ - Ogre::DataStreamPtr data = gm->openResource(name, group); - return StreamPtr(new OgreStream(data)); -} - -static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) -{ - int size = in.size(); - out.resize(size); - - for(int i=0; ilistResourceFileInfo(group, dirs); - FileInfoListPtr res(new FileInfoList); - fill(*res, *olist, dirs); - return res; -} - -FileInfoListPtr OgreVFS::find(const std::string& pattern, - bool recursive, - bool dirs) const -{ - Ogre::FileInfoListPtr olist = gm->findResourceFileInfo(group, pattern, dirs); - FileInfoListPtr res(new FileInfoList); - fill(*res, *olist, dirs); - return res; -} diff --git a/libs/mangle/vfs/servers/ogre_vfs.hpp b/libs/mangle/vfs/servers/ogre_vfs.hpp deleted file mode 100644 index 7ab64169d..000000000 --- a/libs/mangle/vfs/servers/ogre_vfs.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef MANGLE_VFS_OGRESERVER_H -#define MANGLE_VFS_OGRESERVER_H - -#include "../vfs.hpp" -#include - -namespace Mangle { -namespace VFS { - -/** @brief An interface into the OGRE VFS system. - - This class does NOT wrap a single Ogre::Archive, but rather an - entire resource group in Ogre. You can use this class to tap into - all paths, Zip files, custom archives on so on that have been - inserted into Ogre as resource locations. - - This has been built and tested against OGRE 1.6.2. You might have - to make your own modifications if you're working with newer (or - older) versions. - */ -class OgreVFS : public VFS -{ - std::string group; - Ogre::ResourceGroupManager *gm; - - public: - /** @brief Constructor - - OGRE must be initialized (ie. you must have created an - Ogre::Root object somewhere) before calling this. - - @param group Optional resource group name. If none is given, - OGRE's default (or 'World') resource group is used. - */ - OgreVFS(const std::string &_group = ""); - - /// Open a new data stream. Deleting the object should be enough to - /// close it. - virtual Stream::StreamPtr open(const std::string &name); - - /// Check for the existence of a file - virtual bool isFile(const std::string &name) const - { return gm->resourceExists(group, name); } - - /// This doesn't work, always returns false. - virtual bool isDir(const std::string &name) const - { return false; } - - /// This doesn't work. - virtual FileInfoPtr stat(const std::string &name) const - { return FileInfoPtr(); } - - /// List all entries in a given directory. A blank dir should be - /// interpreted as a the root/current directory of the archive. If - /// dirs is true, list directories instead of files. OGRE note: The - /// ogre resource systemd does not support recursive listing of - /// files. We might make a separate filter for this later. - virtual FileInfoListPtr list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const; - - /// Find files after a given pattern. Wildcards (*) are - /// supported. - virtual FileInfoListPtr find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const; -}; - -}} // namespaces -#endif diff --git a/libs/mangle/vfs/servers/physfs_vfs.hpp b/libs/mangle/vfs/servers/physfs_vfs.hpp deleted file mode 100644 index 8535088e0..000000000 --- a/libs/mangle/vfs/servers/physfs_vfs.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef MANGLE_VFS_PHYSFS_SERVER_H -#define MANGLE_VFS_PHYSFS_SERVER_H - -#include "../vfs.hpp" -#include "../../stream/servers/phys_stream.hpp" - -#include -#include - -namespace Mangle { -namespace VFS { - -/** @brief An interface into the PhysFS virtual file system library - - You have to set up PhysFS on your own before using this class. - */ -class PhysVFS : public VFS -{ - public: - PhysVFS() - { - hasList = true; - hasFind = false; - isCaseSensitive = true; - } - - /// Open a new data stream. Deleting the object should be enough to - /// close it. - virtual Stream::StreamPtr open(const std::string &name) - { return Stream::StreamPtr(new Stream::PhysFile(PHYSFS_openRead(name.c_str()))); } - - /// Check for the existence of a file - virtual bool isFile(const std::string &name) const - { return PHYSFS_exists(name.c_str()); } - - /// Checks for a directory - virtual bool isDir(const std::string &name) const - { return PHYSFS_isDirectory(name.c_str()); } - - /// This doesn't work - virtual FileInfoPtr stat(const std::string &name) const - { assert(0); return FileInfoPtr(); } - - virtual FileInfoListPtr list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const - { - char **files = PHYSFS_enumerateFiles(dir.c_str()); - FileInfoListPtr lst(new FileInfoList); - - // Add all teh files - int i = 0; - while(files[i] != NULL) - { - FileInfo fi; - fi.name = files[i]; - fi.isDir = false; - - lst->push_back(fi); - } - return lst; - } - - virtual FileInfoListPtr find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const - { assert(0); } -}; - -}} // namespaces -#endif diff --git a/libs/mangle/vfs/tests/.gitignore b/libs/mangle/vfs/tests/.gitignore deleted file mode 100644 index 814490404..000000000 --- a/libs/mangle/vfs/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/libs/mangle/vfs/tests/Makefile b/libs/mangle/vfs/tests/Makefile deleted file mode 100644 index a5d1d1712..000000000 --- a/libs/mangle/vfs/tests/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -GCC=g++ -I../ -Wall - -all: dummy_test ogre_client_test ogre_resource_test ogre_server_test physfs_server_test - -I_OGRE=$(shell pkg-config --cflags OGRE) -L_OGRE=$(shell pkg-config --libs OGRE) -L_PHYSFS=-lphysfs - -ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.hpp ../clients/ogre_archive.hpp ../clients/ogre_archive.cpp - $(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) - -ogre_resource_test: ogre_resource_test.cpp - $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) - -ogre_server_test: ogre_server_test.cpp ../vfs.hpp ../servers/ogre_vfs.hpp ../servers/ogre_vfs.cpp - $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../servers/ogre_vfs.cpp - -physfs_server_test: physfs_server_test.cpp ../vfs.hpp ../servers/physfs_vfs.hpp - $(GCC) $< -o $@ $(L_PHYSFS) - -dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.hpp - $(GCC) $< -o $@ - -clean: - rm *_test diff --git a/libs/mangle/vfs/tests/dummy_test.cpp b/libs/mangle/vfs/tests/dummy_test.cpp deleted file mode 100644 index 541010c6a..000000000 --- a/libs/mangle/vfs/tests/dummy_test.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "dummy_vfs.cpp" - -#include -#include - -using namespace std; - -void print(FileInfo &inf) -{ - cout << "name: " << inf.name << endl; - cout << "basename: " << inf.basename << endl; - cout << "isDir: " << inf.isDir << endl; - cout << "size: " << inf.size << endl; - cout << "time: " << inf.time << endl; -} -void print(FileInfoPtr inf) { print(*inf); } - -void print(FileInfoList &lst) -{ - for(unsigned i=0; isize() << endl; - - return 0; -} diff --git a/libs/mangle/vfs/tests/dummy_vfs.cpp b/libs/mangle/vfs/tests/dummy_vfs.cpp deleted file mode 100644 index 0448e96a5..000000000 --- a/libs/mangle/vfs/tests/dummy_vfs.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// This file is shared between several test programs -#include "vfs.hpp" -#include -#include - -#include "../../stream/servers/memory_stream.hpp" - -using namespace Mangle::VFS; -using namespace Mangle::Stream; - -class DummyVFS : public VFS -{ -public: - DummyVFS() - { - hasFind = false; - hasList = true; - isCaseSensitive = true; - } - - // We only support opening 'file1' at the moment. - StreamPtr open(const std::string &name) - { - assert(name == "file1"); - return StreamPtr(new MemoryStream("hello world", 11)); - } - - bool isFile(const std::string &name) const - { - return (name == "file1" || - name == "dir/file2"); - } - - bool isDir(const std::string &name) const - { - return name == "dir"; - } - - /// Get info about a single file - FileInfoPtr stat(const std::string &name) const - { - FileInfoPtr fi(new FileInfo); - fi->name = name; - fi->time = 0; - - if(isFile(name)) - { - if(name == "dir/file2") - { - fi->basename = "file2"; - fi->size = 2; - } - else - { - fi->basename = "file1"; - fi->size = 1; - } - fi->isDir = false; - } - else if(isDir(name)) - { - fi->basename = "dir"; - fi->isDir = true; - fi->size = 0; - } - else assert(0); - - return fi; - } - - /// List all entries in a given directory. A blank dir should be - /// interpreted as a the root/current directory of the archive. If - /// dirs is true, list directories instead of files. - virtual FileInfoListPtr list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const - { - assert(dir == ""); - - FileInfoListPtr fl(new FileInfoList); - - FileInfo fi; - - if(!dirs) - { - fi.name = "file1"; - fi.basename = "file1"; - fi.isDir = false; - fi.size = 1; - fi.time = 0; - fl->push_back(fi); - - if(recurse) - { - fi.name = "dir/file2"; - fi.basename = "file2"; - fi.size = 2; - fl->push_back(fi); - } - } - else - { - fi.name = "dir"; - fi.basename = "dir"; - fi.isDir = true; - fi.size = 0; - fi.time = 0; - fl->push_back(fi); - } - return fl; - } - - FileInfoListPtr find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const - { assert(0); } -}; diff --git a/libs/mangle/vfs/tests/ogre_client_test.cpp b/libs/mangle/vfs/tests/ogre_client_test.cpp deleted file mode 100644 index 8e1269b56..000000000 --- a/libs/mangle/vfs/tests/ogre_client_test.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "dummy_vfs.cpp" -#include "../clients/ogre_archive.hpp" -#include - -using namespace Ogre; -using namespace std; - -void print(StringVectorPtr lst) -{ - int s = lst->size(); - - for(int i=0; isize() << endl; - cout << "contents: " << file->getAsString() << endl; - - return 0; -} diff --git a/libs/mangle/vfs/tests/ogre_resource_test.cpp b/libs/mangle/vfs/tests/ogre_resource_test.cpp deleted file mode 100644 index 8965adaa1..000000000 --- a/libs/mangle/vfs/tests/ogre_resource_test.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include - -/* - This isn't really a test of our implementation, but a test of using - the Ogre resource system to find files. If the Ogre interface - changes and you have to change this test, you will have to change - the servers/ogre_vfs.cpp implementation equivalently. - - */ - -using namespace std; -using namespace Ogre; - -ResourceGroupManager *gm; -String group; - -void find(const std::string &fileName) -{ - cout << "\nFile: " << fileName << endl; - - if(!gm->resourceExists(group, fileName)) - { - cout << "Does not exist\n"; - return; - } - - DataStreamPtr data = gm->openResource(fileName, group); - - cout << "Size: " << data->size() << endl; - cout << "First line: " << data->getLine() << "\n"; -} - -int main() -{ - // Disable logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - // Set up Ogre - Root root("","",""); - - root.addResourceLocation("./", "FileSystem", "General"); - - gm = ResourceGroupManager::getSingletonPtr(); - group = gm->getWorldResourceGroupName(); - - find("Makefile"); - find("ogre_resource_test.cpp"); - find("bleh"); - - cout << "\nAll source files:\n"; - FileInfoListPtr list = gm->findResourceFileInfo(group, "*.cpp"); - FileInfoList::iterator it, end; - it = list->begin(); - end = list->end(); - for(; it != end; it++) - cout << " " << it->filename << endl; -} diff --git a/libs/mangle/vfs/tests/ogre_server_test.cpp b/libs/mangle/vfs/tests/ogre_server_test.cpp deleted file mode 100644 index b846eef96..000000000 --- a/libs/mangle/vfs/tests/ogre_server_test.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../servers/ogre_vfs.hpp" - -#include - -#include "server_common.cpp" - -Ogre::Root *root; - -void setupOgre() -{ - using namespace Ogre; - - // Disable logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - // Set up Root - root = new Root("","",""); - - // Add a zip file and the current directory - root->addResourceLocation("test.zip", "Zip", "General"); - root->addResourceLocation("./", "FileSystem", "General"); -} - -int main() -{ - // Set up the engine - setupOgre(); - - // This is our entry point into the resource file system - OgreVFS vfs("General"); - - // Run the test - testAll(vfs); - - return 0; -} diff --git a/libs/mangle/vfs/tests/output/dummy_test.out b/libs/mangle/vfs/tests/output/dummy_test.out deleted file mode 100644 index 61f1fda80..000000000 --- a/libs/mangle/vfs/tests/output/dummy_test.out +++ /dev/null @@ -1,31 +0,0 @@ -Listing all files: -name: file1 -basename: file1 -isDir: 0 -size: 1 -time: 0 -name: dir/file2 -basename: file2 -isDir: 0 -size: 2 -time: 0 - -Stat for single files: -name: file1 -basename: file1 -isDir: 0 -size: 1 -time: 0 - -name: dir/file2 -basename: file2 -isDir: 0 -size: 2 -time: 0 - -name: dir -basename: dir -isDir: 1 -size: 0 -time: 0 -filesize: 11 diff --git a/libs/mangle/vfs/tests/output/ogre_client_test.out b/libs/mangle/vfs/tests/output/ogre_client_test.out deleted file mode 100644 index bc10b1e5c..000000000 --- a/libs/mangle/vfs/tests/output/ogre_client_test.out +++ /dev/null @@ -1,12 +0,0 @@ -Case: 1 -Name: dummy -Type: Mangle -All files: - file1 - dir/file2 -Non-recursive: - file1 -Dirs: - dir -filesize: 11 -contents: hello world diff --git a/libs/mangle/vfs/tests/output/ogre_resource_test.out b/libs/mangle/vfs/tests/output/ogre_resource_test.out deleted file mode 100644 index 9bfc4ff5b..000000000 --- a/libs/mangle/vfs/tests/output/ogre_resource_test.out +++ /dev/null @@ -1,20 +0,0 @@ - -File: Makefile -Size: 828 -First line: GCC=g++ -I../ - -File: ogre_resource_test.cpp -Size: 1437 -First line: #include - -File: bleh -Does not exist - -All source files: - physfs_server_test.cpp - dummy_test.cpp - ogre_resource_test.cpp - server_common.cpp - dummy_vfs.cpp - ogre_client_test.cpp - ogre_server_test.cpp diff --git a/libs/mangle/vfs/tests/output/ogre_server_test.out b/libs/mangle/vfs/tests/output/ogre_server_test.out deleted file mode 100644 index 74f350844..000000000 --- a/libs/mangle/vfs/tests/output/ogre_server_test.out +++ /dev/null @@ -1,11 +0,0 @@ - -File: Makefile -Size: 828 -First 12 bytes: GCC=g++ -I.. - -File: testfile.txt -Size: 13 -First 12 bytes: hello world! - -File: blah_bleh -File doesn't exist diff --git a/libs/mangle/vfs/tests/output/physfs_server_test.out b/libs/mangle/vfs/tests/output/physfs_server_test.out deleted file mode 100644 index 74f350844..000000000 --- a/libs/mangle/vfs/tests/output/physfs_server_test.out +++ /dev/null @@ -1,11 +0,0 @@ - -File: Makefile -Size: 828 -First 12 bytes: GCC=g++ -I.. - -File: testfile.txt -Size: 13 -First 12 bytes: hello world! - -File: blah_bleh -File doesn't exist diff --git a/libs/mangle/vfs/tests/physfs_server_test.cpp b/libs/mangle/vfs/tests/physfs_server_test.cpp deleted file mode 100644 index 9e9d088cd..000000000 --- a/libs/mangle/vfs/tests/physfs_server_test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "../servers/physfs_vfs.hpp" - -#include "server_common.cpp" - -#include - -int main() -{ - // Set up the library and paths - PHYSFS_init("blah"); - PHYSFS_addToSearchPath("test.zip", 1); - PHYSFS_addToSearchPath("./", 1); - - // Create our interface - PhysVFS vfs; - - // Run the test - testAll(vfs); - - return 0; -} diff --git a/libs/mangle/vfs/tests/server_common.cpp b/libs/mangle/vfs/tests/server_common.cpp deleted file mode 100644 index 1834bc25a..000000000 --- a/libs/mangle/vfs/tests/server_common.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include - -using namespace Mangle::VFS; -using namespace Mangle::Stream; -using namespace std; - -void find(VFS &vfs, const std::string &file) -{ - cout << "\nFile: " << file << endl; - - if(!vfs.isFile(file)) - { - cout << "File doesn't exist\n"; - return; - } - - StreamPtr data = vfs.open(file); - - cout << "Size: " << data->size() << endl; - - char buf[13]; - buf[12] = 0; - data->read(buf, 12); - - cout << "First 12 bytes: " << buf << "\n"; -} - -void testAll(VFS &vfs) -{ - find(vfs, "Makefile"); // From the file system - find(vfs, "testfile.txt"); // From the zip - find(vfs, "blah_bleh"); // Doesn't exist -} diff --git a/libs/mangle/vfs/tests/test.sh b/libs/mangle/vfs/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/libs/mangle/vfs/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/vfs/tests/test.zip b/libs/mangle/vfs/tests/test.zip deleted file mode 100644 index ec82f8bc6be46902264847237d30818665e89072..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmWIWW@h1H0D;ZXv#htQzs|@DWP>mdgD68uYH>+gW=^VJNkvI$2qy#cq^G9dGk`d> zf`#D)^9$yT)SR4rh4TEOoD@Z_0B=Snab{emfy`uJUcwofH({Q*9Ik` diff --git a/libs/mangle/vfs/vfs.hpp b/libs/mangle/vfs/vfs.hpp deleted file mode 100644 index 258a6b0a0..000000000 --- a/libs/mangle/vfs/vfs.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef MANGLE_VFS_H -#define MANGLE_VFS_H - -#include "../stream/stream.hpp" -#include -#include - -namespace Mangle { -namespace VFS { - -/// Generic file info structure -struct FileInfo -{ - /// Full name, including path - std::string name; - - /// Base name, not including path - std::string basename; - - /// Is this a directory? - bool isDir; - - /// File size - size_t size; - - /// Last modification date - time_t time; -}; - -typedef std::vector FileInfoList; - -typedef boost::shared_ptr FileInfoPtr; -typedef boost::shared_ptr FileInfoListPtr; - -/** An interface to any file system or other provider of named data - streams -*/ -class VFS -{ - public: - // Feature options. These should be set in the constructor. - - /// If true, the list() function work - bool hasList; - - /// If true, the find() function work - bool hasFind; - - /// If true, the file system is case sensitive - bool isCaseSensitive; - - /// Virtual destructor - virtual ~VFS() {} - - /// Open a new data stream. Deleting the object (letting all the - /// pointers to it go out of scope) should be enough to close it. - virtual Stream::StreamPtr open(const std::string &name) = 0; - - /// Check for the existence of a file - virtual bool isFile(const std::string &name) const = 0; - - /// Check for the existence of a directory - virtual bool isDir(const std::string &name) const = 0; - - /// Get info about a single file - virtual FileInfoPtr stat(const std::string &name) const = 0; - - /// List all entries in a given directory. A blank dir should be - /// interpreted as a the root/current directory of the archive. If - /// dirs is true, list directories instead of files. - virtual FileInfoListPtr list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const = 0; - - /// Find files after a given pattern. Wildcards (*) are - /// supported. Only valid if 'hasFind' is true. Don't implement your - /// own pattern matching here if the backend doesn't support it - /// natively; use a filter instead. - virtual FileInfoListPtr find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const = 0; -}; - -typedef boost::shared_ptr VFSPtr; - -}} // namespaces -#endif From bc0a6bffcff90bc12c54ec465d9057aa507af43c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 09:03:35 -0700 Subject: [PATCH 146/688] Remove outdated comment --- components/bsa/bsa_file.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index afe0c739c..6f3ab3bce 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -115,9 +115,6 @@ public: /** Open a file contained in the archive. Throws an exception if the file doesn't exist. - - NOTE: All files opened from one archive will share a common file - handle. This is NOT thread safe. */ Ogre::DataStreamPtr getFile(const char *file); From 871b1d1c9bd3ceab8be326253f3d1d665915f3b4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Jul 2012 19:20:59 +0200 Subject: [PATCH 147/688] silenced a warning --- components/bsa/bsa_file.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index b5145a4e4..8f605b8ed 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -200,7 +200,7 @@ void BSAFile::readHeader() input.read(&stringBuf[0], stringBuf.size()); // Check our position - assert(input.tellg() == 12+dirsize); + assert(input.tellg() == static_cast (12+dirsize)); // Calculate the offset of the data buffer. All file offsets are // relative to this. 12 header bytes + directory + hash table From da916cecfb1f17e0d073572ce8d6dabafd9f40f7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Jul 2012 19:29:09 +0200 Subject: [PATCH 148/688] fixed a bug in a cmake script that resulted in some files being compiled twice --- apps/openmw/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b12c58f0d..a66cda71e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -79,7 +79,6 @@ ENDIF(WIN32) ENDIF(OGRE_STATIC) add_executable(openmw ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER} - ${COMPONENT_FILES} ${OPENMW_FILES} ${GAME} ${GAME_HEADER} ${APPLE_BUNDLE_RESOURCES} From 300730a834d49b25f52d7002b737062070e88b93 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 11:11:49 -0700 Subject: [PATCH 149/688] Create the skeleton resource from NIFs Note they are not loaded yet. --- components/nifogre/ogre_nif_loader.cpp | 64 ++++++++++++++++++++++++-- components/nifogre/ogre_nif_loader.hpp | 4 +- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 40740ac56..99ccb7fb1 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -128,6 +128,60 @@ public: }; +struct NIFSkeletonLoader : public Ogre::ManualResourceLoader { + +static void warn(const std::string &msg) +{ + std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl; +} + +static void fail(const std::string &msg) +{ + std::cerr << "NIFSkeletonLoader: Fail: "<< msg << std::endl; + abort(); +} + +void loadResource(Ogre::Resource *resource) +{ + warn("Found no records in NIF for "+resource->getName()); +} + +static bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node, Ogre::SkeletonPtr *skel) +{ + if(node->boneTrafo != NULL) + { + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + + Ogre::SkeletonPtr tmp = skelMgr.getByName(name); + if(tmp.isNull()) + { + static NIFSkeletonLoader loader; + tmp = skelMgr.create(name, group, true, &loader); + } + + if(skel) *skel = tmp; + return true; + } + + Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + { + if(createSkeleton(name, group, children[i].getPtr(), skel)) + return true; + } + } + } + return false; +} + +}; + + NIFLoader::LoaderMap NIFLoader::sLoaders; void NIFLoader::warn(const std::string &msg) @@ -325,7 +379,8 @@ void NIFLoader::loadResource(Ogre::Resource *resource) warn("Found no records in NIF for "+resource->getName()); } -void NIFLoader::createMeshes(const std::string &name, const std::string &group, Nif::Node *node, MeshPairList &meshes, int flags) + +void NIFLoader::createMeshes(const std::string &name, const std::string &group, bool hasSkel, Nif::Node *node, MeshPairList &meshes, int flags) { flags |= node->flags; @@ -342,6 +397,7 @@ void NIFLoader::createMeshes(const std::string &name, const std::string &group, NIFLoader *loader = &sLoaders[fullname]; loader->mName = name; loader->mShapeName = node->name; + loader->mHasSkel = hasSkel; mesh = meshMgr.createManual(fullname, group, loader); } @@ -359,11 +415,12 @@ void NIFLoader::createMeshes(const std::string &name, const std::string &group, for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createMeshes(name, group, children[i].getPtr(), meshes, flags); + createMeshes(name, group, hasSkel, children[i].getPtr(), meshes, flags); } } } + MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, const std::string &group) { MeshPairList meshes; @@ -390,7 +447,8 @@ MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, c return meshes; } - createMeshes(name, group, node, meshes); + bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node, skel); + createMeshes(name, group, hasSkel, node, meshes); return meshes; } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 092cc23a8..c90d65395 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -98,11 +98,13 @@ public: private: std::string mName; std::string mShapeName; + bool mHasSkel; static void warn(const std::string &msg); static void fail(const std::string &msg); - static void createMeshes(const std::string &name, const std::string &group, Nif::Node *node, MeshPairList &meshes, int flags=0); + static void createMeshes(const std::string &name, const std::string &group, bool hasSkel, + Nif::Node *node, MeshPairList &meshes, int flags=0); typedef std::map LoaderMap; static LoaderMap sLoaders; From 69ed73399ab367c445372d6a1610785e3e122502 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 11:37:36 -0700 Subject: [PATCH 150/688] Avoid exposing the NIF mesh resource loading class --- components/nifogre/ogre_nif_loader.cpp | 120 ++++++++++++++----------- components/nifogre/ogre_nif_loader.hpp | 18 +--- 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 99ccb7fb1..079e6425b 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -182,20 +182,6 @@ static bool createSkeleton(const std::string &name, const std::string &group, Ni }; -NIFLoader::LoaderMap NIFLoader::sLoaders; - -void NIFLoader::warn(const std::string &msg) -{ - std::cerr << "NIFLoader: Warn: " << msg << std::endl; -} - -void NIFLoader::fail(const std::string &msg) -{ - std::cerr << "NIFLoader: Fail: "<< msg << std::endl; - abort(); -} - - // Conversion of blend / test mode from NIF -> OGRE. // Not in use yet, so let's comment it out. /* @@ -374,51 +360,81 @@ void NIFLoader::createMaterial(const Ogre::String &name, #endif -void NIFLoader::loadResource(Ogre::Resource *resource) +class NIFMeshLoader : Ogre::ManualResourceLoader { - warn("Found no records in NIF for "+resource->getName()); -} + std::string mName; + std::string mGroup; + std::string mShapeName; + bool mHasSkel; - -void NIFLoader::createMeshes(const std::string &name, const std::string &group, bool hasSkel, Nif::Node *node, MeshPairList &meshes, int flags) -{ - flags |= node->flags; - - // TODO: Check for extra data - - if(node->recType == Nif::RC_NiTriShape) + void warn(const std::string &msg) { - Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - std::string fullname = name+"@"+node->name; - - Ogre::MeshPtr mesh = meshMgr.getByName(fullname); - if(mesh.isNull()) - { - NIFLoader *loader = &sLoaders[fullname]; - loader->mName = name; - loader->mShapeName = node->name; - loader->mHasSkel = hasSkel; - - mesh = meshMgr.createManual(fullname, group, loader); - } - - meshes.push_back(std::make_pair(mesh, (node->parent ? node->parent->name : std::string()))); + std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; } - else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && - node->recType != Nif::RC_NiRotatingParticles) - warn("Unhandled mesh node type: "+node->recName); - Nif::NiNode *ninode = dynamic_cast(node); - if(ninode) + void fail(const std::string &msg) { - Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();i++) + std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + abort(); + } + + + typedef std::map LoaderMap; + static LoaderMap sLoaders; + +public: + NIFMeshLoader() + : mHasSkel(false) + { } + NIFMeshLoader(const std::string &name, const std::string &group, bool hasSkel) + : mName(name), mGroup(group), mHasSkel(hasSkel) + { } + + virtual void loadResource(Ogre::Resource *resource) + { + warn("Found no records in NIF for "+resource->getName()); + } + + void createMeshes(Nif::Node *node, MeshPairList &meshes, int flags=0) + { + flags |= node->flags; + + // TODO: Check for extra data + + if(node->recType == Nif::RC_NiTriShape) { - if(!children[i].empty()) - createMeshes(name, group, hasSkel, children[i].getPtr(), meshes, flags); + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + std::string fullname = mName+"@"+node->name; + + Ogre::MeshPtr mesh = meshMgr.getByName(fullname); + if(mesh.isNull()) + { + NIFMeshLoader *loader = &sLoaders[fullname]; + *loader = *this; + loader->mShapeName = node->name; + + mesh = meshMgr.createManual(fullname, mGroup, loader); + } + + meshes.push_back(std::make_pair(mesh, (node->parent ? node->parent->name : std::string()))); + } + else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && + node->recType != Nif::RC_NiRotatingParticles) + warn("Unhandled mesh node type: "+node->recName); + + Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + createMeshes(children[i].getPtr(), meshes, flags); + } } } -} +}; +NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, const std::string &group) @@ -448,7 +464,9 @@ MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, c } bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node, skel); - createMeshes(name, group, hasSkel, node, meshes); + + NIFMeshLoader meshldr(name, group, hasSkel); + meshldr.createMeshes(node, meshes); return meshes; } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index c90d65395..431af1163 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -86,28 +86,12 @@ typedef std::vector< std::pair > MeshPairList; their parent nodes (as they pertain to the skeleton, which is optionally returned in the second argument if it exists). */ -class NIFLoader : Ogre::ManualResourceLoader +class NIFLoader { public: - virtual void loadResource(Ogre::Resource *resource); - static MeshPairList load(const std::string &name, Ogre::SkeletonPtr *skel=NULL, const std::string &group="General"); - -private: - std::string mName; - std::string mShapeName; - bool mHasSkel; - - static void warn(const std::string &msg); - static void fail(const std::string &msg); - - static void createMeshes(const std::string &name, const std::string &group, bool hasSkel, - Nif::Node *node, MeshPairList &meshes, int flags=0); - - typedef std::map LoaderMap; - static LoaderMap sLoaders; }; } From 9028cfe83c707a95c0c1e9a8582eb05936d931d8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 12:02:47 -0700 Subject: [PATCH 151/688] Look for the NiTriShape when the mesh resource wants to load It's still not loaded yet. --- components/nifogre/ogre_nif_loader.cpp | 32 +++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 079e6425b..6896ac8fb 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -379,6 +379,31 @@ class NIFMeshLoader : Ogre::ManualResourceLoader } + bool findTriShape(Ogre::Mesh *mesh, Nif::Node *node) + { + if(node->recType == Nif::RC_NiTriShape && mShapeName == node->name) + { + warn("Not loading shape \""+mShapeName+"\" in "+mName); + return true; + } + + Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + { + if(findTriShape(mesh, children[i].getPtr())) + return true; + } + } + } + return false; + } + + typedef std::map LoaderMap; static LoaderMap sLoaders; @@ -392,7 +417,12 @@ public: virtual void loadResource(Ogre::Resource *resource) { - warn("Found no records in NIF for "+resource->getName()); + Ogre::Mesh *mesh = dynamic_cast(resource); + assert(mesh && "Attempting to load a mesh into a non-mesh resource!"); + + Nif::NIFFile nif(mName); + Nif::Node *node = dynamic_cast(nif.getRecord(0)); + findTriShape(mesh, node); } void createMeshes(Nif::Node *node, MeshPairList &meshes, int flags=0) From fb1f8082d2191b0194f856016870f5a974e805bc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 15 Jul 2012 21:27:57 +0200 Subject: [PATCH 152/688] fix link error with recent glibc versions --- components/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 284ca3cce..efeb69cae 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -72,5 +72,11 @@ add_library(components STATIC ${COMPONENT_FILES}) target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) +# Fix for not visible pthreads functions for linker with glibc 2.15 +if (UNIX AND NOT APPLE) +target_link_libraries(components ${CMAKE_THREAD_LIBS_INIT}) +endif() + + # Make the variable accessible for other subdirectories set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) From 3029c221efb8cc6070196eef806712e8e538c388 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 12:59:39 -0700 Subject: [PATCH 153/688] Create materials when creating meshes --- components/nifogre/ogre_nif_loader.cpp | 148 ++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 17 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 6896ac8fb..b0e92d82f 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -226,26 +226,131 @@ static CompareFunction getTestMode(int mode) } */ -#if 0 -void NIFLoader::createMaterial(const Ogre::String &name, - const Ogre::Vector3 &ambient, - const Ogre::Vector3 &diffuse, - const Ogre::Vector3 &specular, - const Ogre::Vector3 &emissive, - float glossiness, float alpha, - int alphaFlags, float alphaTest, - const Ogre::String &texName) -{ - Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(name, resourceGroup); +class NIFMaterialLoader { + +static std::multimap MaterialMap; + +static void warn(const std::string &msg) +{ + std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; +} + +static void fail(const std::string &msg) +{ + std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + abort(); +} + +public: +static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) +{ + Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); + Ogre::MaterialPtr material = matMgr.getByName(name); + if(!material.isNull()) + return name; + + Ogre::Vector3 ambient(1.0f); + Ogre::Vector3 diffuse(1.0f); + Ogre::Vector3 specular(0.0f); + Ogre::Vector3 emissive(0.0f); + float glossiness = 0.0f; + float alpha = 1.0f; + int alphaFlags = -1; + ubyte alphaTest = 0; + Ogre::String texName; + + // These are set below if present + const NiTexturingProperty *t = NULL; + const NiMaterialProperty *m = NULL; + const NiAlphaProperty *a = NULL; + + // Scan the property list for material information + const PropertyList &list = shape->props; + for (size_t i = 0;i < list.length();i++) + { + // Entries may be empty + if (list[i].empty()) continue; + + const Property *pr = list[i].getPtr(); + if (pr->recType == RC_NiTexturingProperty) + t = static_cast(pr); + else if (pr->recType == RC_NiMaterialProperty) + m = static_cast(pr); + else if (pr->recType == RC_NiAlphaProperty) + a = static_cast(pr); + else + warn("Skipped property type: "+pr->recName); + } + + // Texture + if (t && t->textures[0].inUse) + { + NiSourceTexture *st = t->textures[0].texture.getPtr(); + if (st->external) + { + /* Bethesda at some at some point converted all their BSA + * textures from tga to dds for increased load speed, but all + * texture file name references were kept as .tga. + */ + texName = "textures\\" + st->filename; + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + { + Ogre::String::size_type pos = texName.rfind('.'); + texName.replace(pos, texName.length(), ".dds"); + } + } + else warn("Found internal texture, ignoring."); + } + + // Alpha modifiers + if (a) + { + alphaFlags = a->flags; + alphaTest = a->data.threshold; + } + + // Material + if(m) + { + ambient = m->data.ambient; + diffuse = m->data.diffuse; + specular = m->data.specular; + emissive = m->data.emissive; + glossiness = m->data.glossiness; + alpha = m->data.alpha; + } + + Ogre::String matname = name; + if (m || !texName.empty()) + { + // If we're here, then this mesh has a material. Thus we + // need to calculate a snappy material name. It should + // contain the mesh name (mesh->getName()) but also has to + // be unique. One mesh may use many materials. + std::multimap::iterator itr = MaterialMap.find(texName); + std::multimap::iterator lastElement; + lastElement = MaterialMap.upper_bound(texName); + if (itr != MaterialMap.end()) + { + for ( ; itr != lastElement; ++itr) + { + //std::cout << "OK!"; + //MaterialPtr mat = MaterialManager::getSingleton().getByName(itr->second,recourceGroup); + return itr->second; + //if( mat->getA + } + } + } + + // No existing material like this. Create a new one. + material = matMgr.create(matname, group, true); // This assigns the texture to this material. If the texture name is // a file name, and this file exists (in a resource directory), it // will automatically be loaded when needed. If not (such as for // internal NIF textures that we might support later), we should // already have inserted a manual loader for the texture. - - if (!texName.empty()) { Ogre::Pass *pass = material->getTechnique(0)->getPass(0); @@ -356,8 +461,13 @@ void NIFLoader::createMaterial(const Ogre::String &name, material->setSpecular(specular[0], specular[1], specular[2], alpha); material->setSelfIllumination(emissive[0], emissive[1], emissive[2]); material->setShininess(glossiness); + + MaterialMap.insert(std::make_pair(texName, matname)); + return matname; } -#endif + +}; +std::multimap NIFMaterialLoader::MaterialMap; class NIFMeshLoader : Ogre::ManualResourceLoader @@ -365,6 +475,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader std::string mName; std::string mGroup; std::string mShapeName; + std::string mMaterialName; bool mHasSkel; void warn(const std::string &msg) @@ -433,20 +544,23 @@ public: if(node->recType == Nif::RC_NiTriShape) { + NiTriShape *shape = dynamic_cast(node); + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - std::string fullname = mName+"@"+node->name; + std::string fullname = mName+"@"+shape->name; Ogre::MeshPtr mesh = meshMgr.getByName(fullname); if(mesh.isNull()) { NIFMeshLoader *loader = &sLoaders[fullname]; *loader = *this; - loader->mShapeName = node->name; + loader->mShapeName = shape->name; + loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup); mesh = meshMgr.createManual(fullname, mGroup, loader); } - meshes.push_back(std::make_pair(mesh, (node->parent ? node->parent->name : std::string()))); + meshes.push_back(std::make_pair(mesh, (shape->parent ? shape->parent->name : std::string()))); } else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && node->recType != Nif::RC_NiRotatingParticles) From 441a5c2da20f595da6fd5d8ad0b98c2f6c4b9f07 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 14:12:12 -0700 Subject: [PATCH 154/688] Load NiTriShapes into Ogre meshes --- components/nifogre/ogre_nif_loader.cpp | 173 ++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index b0e92d82f..884d8f64e 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -490,11 +490,182 @@ class NIFMeshLoader : Ogre::ManualResourceLoader } + // Convert NiTriShape to Ogre::SubMesh + void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape *shape) + { + const Nif::NiTriShapeData *data = shape->data.getPtr(); + const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); + std::vector srcVerts = data->vertices; + std::vector srcNorms = data->normals; + if(skin != NULL) + { +#if 0 + // Convert vertices and normals back to bone space + std::vector newVerts(srcVerts.size(), Vector3(0,0,0)); + std::vector newNorms(srcNorms.size(), Vector3(0,0,0)); + + NiSkinDataRef data = skin->GetSkinData(); + const std::vector &bones = skin->GetBones(); + for(size_t b = 0;b < bones.size();b++) + { + Matrix44 mat = data->GetBoneTransform(b) * bones[b]->GetWorldTransform(); + + const std::vector &weights = data->GetBoneWeights(b); + for(size_t i = 0;i < weights.size();i++) + { + size_t index = weights[i].index; + float weight = weights[i].weight; + + newVerts.at(index) += (mat*srcVerts[index]) * weight; + if(newNorms.size() > index) + { + for(size_t j = 0;j < 3;j++) + { + newNorms[index][j] += mat[0][j]*srcNorms[index][0] * weight; + newNorms[index][j] += mat[1][j]*srcNorms[index][1] * weight; + newNorms[index][j] += mat[2][j]*srcNorms[index][2] * weight; + } + } + } + } + + srcVerts = newVerts; + srcNorms = newNorms; +#endif + } + + // Set the bounding box first + BoundsFinder bounds; + bounds.add(&srcVerts[0][0], srcVerts.size()); + mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), + bounds.maxX(), bounds.maxY(), bounds.maxZ())); + mesh->_setBoundingSphereRadius(bounds.getRadius()); + + // This function is just one long stream of Ogre-barf, but it works + // great. + Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareVertexBufferSharedPtr vbuf; + Ogre::HardwareIndexBufferSharedPtr ibuf; + Ogre::VertexBufferBinding *bind; + Ogre::VertexDeclaration *decl; + int nextBuf = 0; + + Ogre::SubMesh *sub = mesh->createSubMesh(shape->name); + + // Add vertices + sub->useSharedVertices = false; + sub->vertexData = new Ogre::VertexData(); + sub->vertexData->vertexStart = 0; + sub->vertexData->vertexCount = srcVerts.size(); + + decl = sub->vertexData->vertexDeclaration; + bind = sub->vertexData->vertexBufferBinding; + if(srcVerts.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcVerts.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, + true); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex normals + if(srcNorms.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcNorms.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, + true); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex colors + const std::vector &colors = data->colors; + if(colors.size()) + { + Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); + std::vector colorsRGB(colors.size()); + for(size_t i = 0;i < colorsRGB.size();i++) + { + Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]); + rs->convertColourValue(clr, &colorsRGB[i]); + } + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, + true); + vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true); + decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); + bind->setBinding(nextBuf++, vbuf); + } + + // Texture UV coordinates + size_t numUVs = data->uvlist.size(); + if(numUVs) + { + size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); + vbuf = hwBufMgr->createVertexBuffer(elemSize, srcVerts.size()*numUVs, + Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); + for(size_t i = 0;i < numUVs;i++) + { + const std::vector &uvlist = data->uvlist[i]; + vbuf->writeData(i*srcVerts.size()*elemSize, elemSize*srcVerts.size(), &uvlist[0], true); + decl->addElement(nextBuf, i*srcVerts.size()*elemSize, Ogre::VET_FLOAT2, + Ogre::VES_TEXTURE_COORDINATES, i); + } + bind->setBinding(nextBuf++, vbuf); + } + + // Triangle faces + const std::vector &srcIdx = data->triangles; + if(srcIdx.size()) + { + ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(), + Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); + ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true); + sub->indexData->indexBuffer = ibuf; + sub->indexData->indexCount = srcIdx.size(); + sub->indexData->indexStart = 0; + } + + // Assign bone weights for this TriShape +#if 0 + if(skin != NULL) + { + // Get the skeleton resource, so weights can be applied + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + Ogre::SkeletonPtr skel = skelMgr->getByName(mesh->getSkeletonName()); + + NiSkinDataRef data = skin->GetSkinData(); + const std::vector &bones = skin->GetBones(); + for(size_t i = 0;i < bones.size();i++) + { + Ogre::VertexBoneAssignment boneInf; + boneInf.boneIndex = skel->getBone(bones[i]->GetName())->getHandle(); + + const std::vector &weights = data->GetBoneWeights(i); + for(size_t j = 0;j < weights.size();j++) + { + boneInf.vertexIndex = weights[j].index; + boneInf.weight = weights[j].weight; + sub->addBoneAssignment(boneInf); + } + } + } +#endif + + if(mMaterialName.length() > 0) + sub->setMaterialName(mMaterialName); + } + bool findTriShape(Ogre::Mesh *mesh, Nif::Node *node) { if(node->recType == Nif::RC_NiTriShape && mShapeName == node->name) { - warn("Not loading shape \""+mShapeName+"\" in "+mName); + handleNiTriShape(mesh, dynamic_cast(node)); return true; } From 61f32eca77bcf93e25d609987cddb72cc2bbc0f8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 15:23:49 -0700 Subject: [PATCH 155/688] Add methods to get a node's local and full transform as a 4x4 matrix --- components/nif/nif_file.cpp | 14 ++++++++++++++ components/nif/node.hpp | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 231349302..789bae5e3 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -210,3 +210,17 @@ void NiSkinInstance::post(NIFFile *nif) bones[i]->makeBone(i, data->bones[i]); } } + +Ogre::Matrix4 Node::getLocalTransform() +{ + Ogre::Matrix4 mat4; + mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation)); + return mat4; +} + +Ogre::Matrix4 Node::getWorldTransform() +{ + if(parent != NULL) + return getLocalTransform() * parent->getWorldTransform(); + return getLocalTransform(); +} diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 1f1b91a46..f7d3c6e96 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -24,6 +24,8 @@ #ifndef _NIF_NODE_H_ #define _NIF_NODE_H_ +#include + #include "controlled.hpp" #include "data.hpp" #include "property.hpp" @@ -108,6 +110,9 @@ public: boneTrafo = &bi.trafo; boneIndex = ind; } + + Ogre::Matrix4 getLocalTransform(); + Ogre::Matrix4 getWorldTransform(); }; struct NiNode : Node From b1f7fd9f7b39b9728839b84693ab8617be739fd5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 17:00:27 -0700 Subject: [PATCH 156/688] Pre-transform the mesh vertices when there's no skinning or skeleton --- components/nif/nif_file.cpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 789bae5e3..308196f32 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -213,7 +213,7 @@ void NiSkinInstance::post(NIFFile *nif) Ogre::Matrix4 Node::getLocalTransform() { - Ogre::Matrix4 mat4; + Ogre::Matrix4 mat4(Ogre::Matrix4::IDENTITY); mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation)); return mat4; } diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 884d8f64e..01551a702 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -533,12 +533,33 @@ class NIFMeshLoader : Ogre::ManualResourceLoader srcNorms = newNorms; #endif } + else if(!mHasSkel) + { + // No skinning and no skeleton, so just transform the vertices and + // normals into position. + Ogre::Matrix4 mat4 = shape->getWorldTransform(); + for(size_t i = 0;i < srcVerts.size();i++) + { + Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f); + vec4 = mat4*vec4; + srcVerts[i] = Ogre::Vector3(&vec4[0]); + } + for(size_t i = 0;i < srcNorms.size();i++) + { + Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); + vec4 = mat4*vec4; + srcNorms[i] = Ogre::Vector3(&vec4[0]); + } + } // Set the bounding box first BoundsFinder bounds; bounds.add(&srcVerts[0][0], srcVerts.size()); - mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ())); + // No idea why this offset is needed. It works fine without it if the + // vertices weren't transformed first, but otherwise it fails later on + // when the object is being inserted into the scene. + mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f, + bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f)); mesh->_setBoundingSphereRadius(bounds.getRadius()); // This function is just one long stream of Ogre-barf, but it works From ad75b47472dbf3856120fe2ee8b65d352395b34c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 15 Jul 2012 19:07:31 -0700 Subject: [PATCH 157/688] Build and set up a skeleton for meshes --- components/nifogre/ogre_nif_loader.cpp | 72 +++++++++++++++++++------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 01551a702..1db0b92a5 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -141,9 +141,35 @@ static void fail(const std::string &msg) abort(); } + +void buildBones(Ogre::Skeleton *skel, Nif::NiNode *node, Ogre::Bone *parent=NULL) +{ + Ogre::Bone *bone = skel->createBone(node->name); + if(parent) parent->addChild(bone); + + bone->setOrientation(node->trafo.rotation); + bone->setPosition(node->trafo.pos); + bone->setScale(Ogre::Vector3(node->trafo.scale)); + bone->setBindingPose(); + bone->setInitialState(); + + const Nif::NodeList &children = node->children; + for(size_t i = 0;i < children.length();i++) + { + Nif::NiNode *next; + if(!children[i].empty() && (next=dynamic_cast(children[i].getPtr()))) + buildBones(skel, next, bone); + } +} + void loadResource(Ogre::Resource *resource) { - warn("Found no records in NIF for "+resource->getName()); + Ogre::Skeleton *skel = dynamic_cast(resource); + OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); + + Nif::NIFFile nif(skel->getName()); + Nif::NiNode *node = dynamic_cast(nif.getRecord(0)); + buildBones(skel, node); } static bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node, Ogre::SkeletonPtr *skel) @@ -499,21 +525,29 @@ class NIFMeshLoader : Ogre::ManualResourceLoader std::vector srcNorms = data->normals; if(skin != NULL) { -#if 0 - // Convert vertices and normals back to bone space - std::vector newVerts(srcVerts.size(), Vector3(0,0,0)); - std::vector newNorms(srcNorms.size(), Vector3(0,0,0)); + // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be + // explicitly attached later. + mesh->setSkeletonName(mName); - NiSkinDataRef data = skin->GetSkinData(); - const std::vector &bones = skin->GetBones(); - for(size_t b = 0;b < bones.size();b++) + // Convert vertices and normals to bone space from bind position. It would be + // better to transform the bones into bind position, but there doesn't seem to + // be a reliable way to do that. + std::vector newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); + std::vector newNorms(srcNorms.size(), Ogre::Vector3(1.0f)); + + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t b = 0;b < bones.length();b++) { - Matrix44 mat = data->GetBoneTransform(b) * bones[b]->GetWorldTransform(); + Ogre::Matrix4 mat(Ogre::Matrix4::IDENTITY); + mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), + Ogre::Quaternion(data->bones[b].trafo.rotation)); + mat = mat * bones[b]->getWorldTransform(); - const std::vector &weights = data->GetBoneWeights(b); + const std::vector &weights = data->bones[b].weights; for(size_t i = 0;i < weights.size();i++) { - size_t index = weights[i].index; + size_t index = weights[i].vertex; float weight = weights[i].weight; newVerts.at(index) += (mat*srcVerts[index]) * weight; @@ -531,7 +565,6 @@ class NIFMeshLoader : Ogre::ManualResourceLoader srcVerts = newVerts; srcNorms = newNorms; -#endif } else if(!mHasSkel) { @@ -653,30 +686,29 @@ class NIFMeshLoader : Ogre::ManualResourceLoader } // Assign bone weights for this TriShape -#if 0 if(skin != NULL) { // Get the skeleton resource, so weights can be applied Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); Ogre::SkeletonPtr skel = skelMgr->getByName(mesh->getSkeletonName()); + skel->touch(); - NiSkinDataRef data = skin->GetSkinData(); - const std::vector &bones = skin->GetBones(); - for(size_t i = 0;i < bones.size();i++) + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t i = 0;i < bones.length();i++) { Ogre::VertexBoneAssignment boneInf; - boneInf.boneIndex = skel->getBone(bones[i]->GetName())->getHandle(); + boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle(); - const std::vector &weights = data->GetBoneWeights(i); + const std::vector &weights = data->bones[i].weights; for(size_t j = 0;j < weights.size();j++) { - boneInf.vertexIndex = weights[j].index; + boneInf.vertexIndex = weights[j].vertex; boneInf.weight = weights[j].weight; sub->addBoneAssignment(boneInf); } } } -#endif if(mMaterialName.length() > 0) sub->setMaterialName(mMaterialName); From e862b6b5a5cc7a8a001e3aa5df2983bfc3ef406b Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 16 Jul 2012 15:53:02 +0400 Subject: [PATCH 158/688] Fix comparison in cmake --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3175fa7d..9cc741c67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VE # Debug suffix for plugins set(DEBUG_SUFFIX "") if (DEFINED CMAKE_BUILD_TYPE) - if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(DEBUG_SUFFIX "_d") endif() endif() From a539e98274971f9495075b7092a5ba264c3e7994 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 10:48:48 -0700 Subject: [PATCH 159/688] Handle all meshes when inserting objects into the scene --- apps/openmw/mwrender/objects.cpp | 69 ++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index ea58ec716..2ff9de9a4 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -92,12 +92,19 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); - // FIXME: There can be more than one! + Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(mesh); - Ogre::Entity *ent = mRenderer.getScene()->createEntity(meshes[0].first->getName()); + std::vector entities(meshes.size()); + for(size_t i = 0;i < meshes.size();i++) + { + entities[i] = mRenderer.getScene()->createEntity(meshes[i].first->getName()); - - Ogre::Vector3 extents = ent->getBoundingBox().getSize(); + const Ogre::AxisAlignedBox &tmp = entities[i]->getBoundingBox(); + bounds.merge(Ogre::AxisAlignedBox(insert->_getDerivedPosition() + tmp.getMinimum(), + insert->_getDerivedPosition() + tmp.getMaximum()) + ); + } + Ogre::Vector3 extents = bounds.getSize(); extents *= insert->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); @@ -109,42 +116,42 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) if (mBounds.find(ptr.getCell()) == mBounds.end()) mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; - - Ogre::AxisAlignedBox bounds = ent->getBoundingBox(); - bounds = Ogre::AxisAlignedBox( - insert->_getDerivedPosition() + bounds.getMinimum(), - insert->_getDerivedPosition() + bounds.getMaximum() - ); - - bounds.scale(insert->getScale()); mBounds[ptr.getCell()].merge(bounds); bool transparent = false; - for (unsigned int i=0; igetNumSubEntities(); ++i) + for(size_t i = 0;i < entities.size();i++) { - Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) + Ogre::Entity *ent = entities[i]; + for (unsigned int i=0; igetNumSubEntities(); ++i) { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) + Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements()) { - Ogre::Pass* pass = passIt.getNext(); + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); - if (pass->getDepthWriteEnabled() == false) - transparent = true; + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } } } } if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || transparent) { - insert->attachObject(ent); + for(size_t i = 0;i < entities.size();i++) + { + Ogre::Entity *ent = entities[i]; + insert->attachObject(ent); - ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); - ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); - ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); + ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); + ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + } } else { @@ -184,15 +191,19 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) // - there will be too many batches. sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500)); - sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); - sg->setVisibilityFlags(small ? RV_StaticsSmall : RV_Statics); sg->setCastShadows(true); sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - mRenderer.getScene()->destroyEntity(ent); + for(size_t i = 0;i < entities.size();i++) + { + Ogre::Entity *ent = entities[i]; + sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); + + mRenderer.getScene()->destroyEntity(ent); + } } } From 75ce10c580cdf673586e8122f060b3c7876380d9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 11:31:45 -0700 Subject: [PATCH 160/688] Don't load data for hidden meshes --- components/nifogre/ogre_nif_loader.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 1db0b92a5..044c9cd72 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -755,6 +755,13 @@ public: Ogre::Mesh *mesh = dynamic_cast(resource); assert(mesh && "Attempting to load a mesh into a non-mesh resource!"); + if(!mShapeName.length()) + { + if(mHasSkel) + mesh->setSkeletonName(mName); + return; + } + Nif::NIFFile nif(mName); Nif::Node *node = dynamic_cast(nif.getRecord(0)); findTriShape(mesh, node); @@ -778,8 +785,11 @@ public: { NIFMeshLoader *loader = &sLoaders[fullname]; *loader = *this; - loader->mShapeName = shape->name; - loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup); + if(!(flags&0x01)) // Not hidden + { + loader->mShapeName = shape->name; + loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup); + } mesh = meshMgr.createManual(fullname, mGroup, loader); } From 12f17858828b6134a8716fb9971bd97d49aa57ae Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 11:43:16 -0700 Subject: [PATCH 161/688] Use default bone names for duplicate names --- components/nifogre/ogre_nif_loader.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 044c9cd72..9c0e2e10f 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -144,7 +144,11 @@ static void fail(const std::string &msg) void buildBones(Ogre::Skeleton *skel, Nif::NiNode *node, Ogre::Bone *parent=NULL) { - Ogre::Bone *bone = skel->createBone(node->name); + Ogre::Bone *bone; + if(!skel->hasBone(node->name)) + bone = skel->createBone(node->name); + else + bone = skel->createBone(); if(parent) parent->addChild(bone); bone->setOrientation(node->trafo.rotation); From 1c53add6c4d3d349b32ac7bb94d9a396142b2c1f Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 16 Jul 2012 23:53:33 +0400 Subject: [PATCH 162/688] Include boost/shared_ptr.hpp for boost:shared_ptr --- apps/openmw/mwsound/soundmanager.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index ff360122b..83195390c 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include From 0e934a52ca765c097b822440cd0d0635ca8e0c8b Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Mon, 16 Jul 2012 23:54:04 +0400 Subject: [PATCH 163/688] Include soundmanager.hpp for Play_Normal enum --- apps/openmw/mwsound/sound.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index a33892548..0cba6abca 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -3,6 +3,8 @@ #include +#include "soundmanager.hpp" + namespace MWSound { class Sound From d6bf2b7d294d7298b691677e84ac051c86030397 Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Tue, 17 Jul 2012 00:11:56 +0400 Subject: [PATCH 164/688] Proper way to find and use libtbb --- CMakeLists.txt | 1 + apps/esmtool/CMakeLists.txt | 1 + apps/launcher/CMakeLists.txt | 3 ++- apps/openmw/CMakeLists.txt | 1 + cmake/FindTBB.cmake | 14 ++++++++++++++ 5 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 cmake/FindTBB.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cc741c67..49684fd7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,6 +200,7 @@ find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread) find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) +find_package(TBB REQUIRED) IF(OGRE_STATIC) find_package(Cg REQUIRED) IF(WIN32) diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index af3dc090e..eb74aa992 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -10,6 +10,7 @@ add_executable(esmtool target_link_libraries(esmtool ${Boost_LIBRARIES} + ${TBB_LIBRARY} components ) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index ed3559fdc..57b7fb71a 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -75,8 +75,9 @@ add_executable(omwlauncher target_link_libraries(omwlauncher ${Boost_LIBRARIES} ${OGRE_LIBRARIES} - ${OGRE_STATIC_PLUGINS} + ${OGRE_STATIC_PLUGINS} ${QT_LIBRARIES} + ${TBB_LIBRARY} components ) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a66cda71e..feefa4fd2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -100,6 +100,7 @@ target_link_libraries(openmw ${BULLET_LIBRARIES} ${MYGUI_LIBRARIES} ${MYGUI_PLATFORM_LIBRARIES} + ${TBB_LIBRARY} components ) diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake new file mode 100644 index 000000000..f84f81f1c --- /dev/null +++ b/cmake/FindTBB.cmake @@ -0,0 +1,14 @@ +# Locate TBB +# This module defines +# TBB_LIBRARY +# TBB_FOUND, if false, do not try to link to TBB +# TBB_INCLUDE_DIR, where to find the headers + +FIND_PATH(TBB_INCLUDE_DIR tbb/tbb.h) + +FIND_LIBRARY(TBB_LIBRARY NAMES tbb) + +SET(TBB_FOUND "NO") +IF(TBB_LIBRARY AND TBB_INCLUDE_DIR) + SET(TBB_FOUND "YES") +ENDIF(TBB_LIBRARY AND TBB_INCLUDE_DIR) From 63e40d6e92e4b55b7c05984a10e1ae59a5f0e8ed Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 13:16:11 -0700 Subject: [PATCH 165/688] Fix world transform calculation --- components/nif/nif_file.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 308196f32..3313d89ab 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -221,6 +221,6 @@ Ogre::Matrix4 Node::getLocalTransform() Ogre::Matrix4 Node::getWorldTransform() { if(parent != NULL) - return getLocalTransform() * parent->getWorldTransform(); + return parent->getWorldTransform() * getLocalTransform(); return getLocalTransform(); } From df76c324a4af9681c39d95b187562fc2220c2024 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 13:34:56 -0700 Subject: [PATCH 166/688] Handle the MRK text string marker --- components/nifogre/ogre_nif_loader.cpp | 28 +++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 9c0e2e10f..7a7b9e572 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -771,15 +771,33 @@ public: findTriShape(mesh, node); } - void createMeshes(Nif::Node *node, MeshPairList &meshes, int flags=0) + void createMeshes(const Nif::Node *node, MeshPairList &meshes, int flags=0) { flags |= node->flags; - // TODO: Check for extra data + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + Nif::NiStringExtraData *sd = dynamic_cast(e.getPtr()); + if(sd != NULL) + { + // String markers may contain important information + // affecting the entire subtree of this obj + if(sd->string == "MRK") + { + // Marker objects. These are only visible in the + // editor. + flags |= 0x01; + } + } + else + warn("Unhandled extra data type "+e->recType); + e = e->extra; + } if(node->recType == Nif::RC_NiTriShape) { - NiTriShape *shape = dynamic_cast(node); + const NiTriShape *shape = dynamic_cast(node); Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); std::string fullname = mName+"@"+shape->name; @@ -804,10 +822,10 @@ public: node->recType != Nif::RC_NiRotatingParticles) warn("Unhandled mesh node type: "+node->recName); - Nif::NiNode *ninode = dynamic_cast(node); + const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { - Nif::NodeList &children = ninode->children; + const Nif::NodeList &children = ninode->children; for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) From bf26f029f92cac2857c886c246b3df11de75d773 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 13:47:19 -0700 Subject: [PATCH 167/688] Fix some skinning-related transformations --- components/nifogre/ogre_nif_loader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 7a7b9e572..cffab724e 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -546,7 +546,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader Ogre::Matrix4 mat(Ogre::Matrix4::IDENTITY); mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), Ogre::Quaternion(data->bones[b].trafo.rotation)); - mat = mat * bones[b]->getWorldTransform(); + mat = bones[b]->getWorldTransform() * mat; const std::vector &weights = data->bones[b].weights; for(size_t i = 0;i < weights.size();i++) @@ -559,9 +559,9 @@ class NIFMeshLoader : Ogre::ManualResourceLoader { for(size_t j = 0;j < 3;j++) { - newNorms[index][j] += mat[0][j]*srcNorms[index][0] * weight; - newNorms[index][j] += mat[1][j]*srcNorms[index][1] * weight; - newNorms[index][j] += mat[2][j]*srcNorms[index][2] * weight; + newNorms[index][j] += mat[j][0]*srcNorms[index][0] * weight; + newNorms[index][j] += mat[j][1]*srcNorms[index][1] * weight; + newNorms[index][j] += mat[j][2]*srcNorms[index][2] * weight; } } } From fefbf86531f84c07766bb4b61f95f6345e8c3c73 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 14:30:23 -0700 Subject: [PATCH 168/688] Use Ogre's matrix ops to transform normals --- components/nifogre/ogre_nif_loader.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index cffab724e..29ead0461 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -557,12 +557,9 @@ class NIFMeshLoader : Ogre::ManualResourceLoader newVerts.at(index) += (mat*srcVerts[index]) * weight; if(newNorms.size() > index) { - for(size_t j = 0;j < 3;j++) - { - newNorms[index][j] += mat[j][0]*srcNorms[index][0] * weight; - newNorms[index][j] += mat[j][1]*srcNorms[index][1] * weight; - newNorms[index][j] += mat[j][2]*srcNorms[index][2] * weight; - } + Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f); + vec4 = mat*vec4 * weight; + newNorms[index] += Ogre::Vector3(&vec4[0]); } } } From ada88596dc65457a8e4b10e4c1adfc1a45be0b45 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 15:30:09 -0700 Subject: [PATCH 169/688] Fix an abort at shutdown Ogre uses a special method to delete the stream object, so it needs to be allocated properly. --- components/esm/esm_reader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esm/esm_reader.cpp b/components/esm/esm_reader.cpp index 6b8409b45..a2cf69ddc 100644 --- a/components/esm/esm_reader.cpp +++ b/components/esm/esm_reader.cpp @@ -107,15 +107,15 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) void ESMReader::open(const std::string &file) { - std::ifstream *stream = new std::ifstream(file.c_str(), std::ios_base::binary); - // Ogre will delete the stream for us + std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); + // Ogre will delete the stream for us open(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); } void ESMReader::openRaw(const std::string &file) { - std::ifstream *stream = new std::ifstream(file.c_str(), std::ios_base::binary); - // Ogre will delete the stream for us + std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); + // Ogre will delete the stream for us openRaw(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); } From 65c20f128f8c6552da1707c072c23698a40d8364 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 16 Jul 2012 21:18:33 -0700 Subject: [PATCH 170/688] Build bones for non-NiNode nodes (NiTriShapes, etc) --- components/nifogre/ogre_nif_loader.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 29ead0461..c0f3c25d0 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -142,7 +142,7 @@ static void fail(const std::string &msg) } -void buildBones(Ogre::Skeleton *skel, Nif::NiNode *node, Ogre::Bone *parent=NULL) +void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL) { Ogre::Bone *bone; if(!skel->hasBone(node->name)) @@ -157,12 +157,15 @@ void buildBones(Ogre::Skeleton *skel, Nif::NiNode *node, Ogre::Bone *parent=NULL bone->setBindingPose(); bone->setInitialState(); - const Nif::NodeList &children = node->children; - for(size_t i = 0;i < children.length();i++) + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) { - Nif::NiNode *next; - if(!children[i].empty() && (next=dynamic_cast(children[i].getPtr()))) - buildBones(skel, next, bone); + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + buildBones(skel, children[i].getPtr(), bone); + } } } @@ -172,7 +175,7 @@ void loadResource(Ogre::Resource *resource) OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); Nif::NIFFile nif(skel->getName()); - Nif::NiNode *node = dynamic_cast(nif.getRecord(0)); + const Nif::Node *node = dynamic_cast(nif.getRecord(0)); buildBones(skel, node); } From 0549e949ba6cab8fb06919ebcc0ee4dee558ef59 Mon Sep 17 00:00:00 2001 From: guidoj Date: Tue, 17 Jul 2012 09:27:12 +0200 Subject: [PATCH 171/688] Mostly removal of unnecessary #include's and a little clean up --- .gitignore | 1 + apps/openmw/engine.cpp | 19 ----------------- apps/openmw/engine.hpp | 4 ---- apps/openmw/main.cpp | 15 -------------- apps/openmw/mwbase/world.hpp | 2 -- apps/openmw/mwclass/creature.hpp | 1 - apps/openmw/mwgui/class.cpp | 1 - apps/openmw/mwgui/dialogue.cpp | 1 - apps/openmw/mwgui/dialogue_history.cpp | 1 - apps/openmw/mwgui/race.cpp | 1 - apps/openmw/mwgui/window_manager.hpp | 4 ---- apps/openmw/mwrender/actors.hpp | 10 --------- apps/openmw/mwrender/animation.hpp | 6 +----- apps/openmw/mwrender/creatureanimation.hpp | 4 ---- apps/openmw/mwrender/npcanimation.hpp | 12 ++--------- apps/openmw/mwrender/renderingmanager.hpp | 12 ----------- apps/openmw/mwscript/scriptmanager.hpp | 1 - apps/openmw/mwworld/cells.cpp | 4 ---- apps/openmw/mwworld/cellstore.hpp | 3 --- apps/openmw/mwworld/containerstore.hpp | 2 -- apps/openmw/mwworld/physicssystem.hpp | 2 -- apps/openmw/mwworld/ptr.hpp | 4 ---- apps/openmw/mwworld/refdata.hpp | 2 -- apps/openmw/mwworld/scene.cpp | 2 -- apps/openmw/mwworld/scene.hpp | 8 -------- apps/openmw/mwworld/worldimp.cpp | 9 -------- apps/openmw/mwworld/worldimp.hpp | 15 -------------- components/compiler/streamerrorhandler.cpp | 3 ++- components/esm/esm_reader.hpp | 6 +----- components/files/collections.hpp | 2 -- components/files/configurationmanager.hpp | 1 - components/misc/tests/slice_test.cpp | 2 -- components/nif/nif_file.hpp | 3 +-- components/nifbullet/bullet_nif_loader.hpp | 8 +------- components/nifogre/ogre_nif_loader.hpp | 24 +++++++--------------- components/nifogre/tests/ogre_common.cpp | 1 - libs/openengine/gui/layout.hpp | 1 - libs/openengine/ogre/fader.cpp | 2 -- 38 files changed, 16 insertions(+), 183 deletions(-) diff --git a/.gitignore b/.gitignore index b3bb8d82d..26ba80e1a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ cmake_install.cmake Makefile makefile data +*.kdev4 diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 45b4ab514..7966639a6 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1,25 +1,13 @@ #include "engine.hpp" #include "components/esm/loadcell.hpp" -#include - -#include -#include - #include #include #include -#include -#include - -#include #include -#include -#include #include -#include #include #include @@ -31,16 +19,12 @@ #include "mwgui/cursorreplace.hpp" #include "mwscript/scriptmanager.hpp" -#include "mwscript/compilercontext.hpp" -#include "mwscript/interpretercontext.hpp" #include "mwscript/extensions.hpp" -#include "mwscript/globalscripts.hpp" #include "mwsound/soundmanager.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" -#include "mwworld/cellstore.hpp" #include "mwworld/worldimp.hpp" #include "mwclass/classes.hpp" @@ -50,9 +34,6 @@ #include "mwmechanics/mechanicsmanager.hpp" -#include "mwbase/environment.hpp" -#include "mwbase/world.hpp" - void OMW::Engine::executeLocalScripts() { diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index cf1ef3b9c..031cae551 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -1,10 +1,6 @@ #ifndef ENGINE_H #define ENGINE_H -#include - -#include - #include #include diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 68aa12fb3..fc905b79d 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -1,13 +1,5 @@ #include -#include -#include - -#include - -#include -#include -#include #include #include "engine.hpp" @@ -16,11 +8,6 @@ #include #include -# if !defined(_DEBUG) -# include -# include -# endif - // For OutputDebugString #include // makes __argc and __argv available on windows @@ -52,8 +39,6 @@ inline boost::filesystem::path lexical_cast mMap; }; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 51d0a4f8e..6937cbf3b 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -1,8 +1,6 @@ #ifndef GAME_MWBASE_WORLD_H #define GAME_MWBASE_WORLD_H -#include -#include #include #include diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 237f54e82..1274be09a 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -1,7 +1,6 @@ #ifndef GAME_MWCLASS_CREATURE_H #define GAME_MWCLASS_CREATURE_H -#include "../mwworld/class.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwrender/actors.hpp" diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index d0f21f945..9f291bae7 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -1,6 +1,5 @@ #include "class.hpp" -#include #include #include diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index acf0bf130..018fbb178 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -1,6 +1,5 @@ #include "dialogue.hpp" -#include #include #include diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp index cd34ee119..009f42044 100644 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ b/apps/openmw/mwgui/dialogue_history.cpp @@ -3,7 +3,6 @@ #include "widgets.hpp" #include "components/esm_store/store.hpp" -#include #include #include diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 9ae453016..9f4a4965d 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -1,6 +1,5 @@ #include "race.hpp" -#include #include #include diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 03ffa6b59..9d1516ccf 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -10,10 +10,6 @@ this class. **/ -#include -#include -#include - #include "MyGUI_UString.h" #include diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 63cd3baa1..4b0b2e572 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -1,18 +1,8 @@ #ifndef _GAME_RENDER_ACTORS_H #define _GAME_RENDER_ACTORS_H -#include -#include - -#include -#include "components/nifogre/ogre_nif_loader.hpp" - -#include "../mwworld/refdata.hpp" -#include "../mwworld/actiontalk.hpp" - #include "npcanimation.hpp" #include "creatureanimation.hpp" -#include namespace MWWorld { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 4ab60cff4..28ef9d706 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,17 +1,13 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H -#include + #include -#include "../mwworld/refdata.hpp" -#include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include -#include #include - namespace MWRender{ struct PosAndRot{ diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index f50b7904b..4fea1a9d9 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -2,11 +2,7 @@ #define _GAME_RENDER_CREATUREANIMATION_H #include "animation.hpp" -#include - -#include "../mwworld/refdata.hpp" -#include "../mwworld/ptr.hpp" #include "components/nifogre/ogre_nif_loader.hpp" diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 8f4f8181d..4233d2803 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -1,20 +1,12 @@ #ifndef _GAME_RENDER_NPCANIMATION_H #define _GAME_RENDER_NPCANIMATION_H -#include "animation.hpp" -#include -#include -#include -#include -#include -#include -#include "../mwworld/refdata.hpp" -#include "../mwworld/ptr.hpp" +#include "animation.hpp" + #include "components/nifogre/ogre_nif_loader.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwclass/npc.hpp" #include "../mwworld/containerstore.hpp" -#include "components/esm/loadarmo.hpp" namespace MWRender{ diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 9aaba3803..c30f52979 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -1,25 +1,14 @@ #ifndef _GAME_RENDERING_MANAGER_H #define _GAME_RENDERING_MANAGER_H - #include "sky.hpp" #include "terrain.hpp" #include "debugging.hpp" -#include "../mwworld/class.hpp" - -#include - -#include -#include #include -#include #include -#include -#include - #include #include "renderinginterface.hpp" @@ -45,7 +34,6 @@ namespace MWWorld namespace MWRender { - class Shadows; class ShaderHelper; class LocalMap; diff --git a/apps/openmw/mwscript/scriptmanager.hpp b/apps/openmw/mwscript/scriptmanager.hpp index 34cc0defe..a466f903d 100644 --- a/apps/openmw/mwscript/scriptmanager.hpp +++ b/apps/openmw/mwscript/scriptmanager.hpp @@ -2,7 +2,6 @@ #define GAME_SCRIPT_SCRIPTMANAGER_H #include -#include #include #include diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index cd7ebf79a..cffaf70ea 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -1,9 +1,5 @@ #include "cells.hpp" -#include - -#include - #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8253f3f0b..de3ac12ae 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -4,9 +4,6 @@ #include #include -#include -#include -#include #include #include "refdata.hpp" diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index f71b493bd..ae27fad3d 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -3,8 +3,6 @@ #include -#include "cellstore.hpp" -#include "refdata.hpp" #include "ptr.hpp" namespace ESM diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index b46ce117b..a6b679833 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -1,9 +1,7 @@ #ifndef GAME_MWWORLD_PHYSICSSYSTEM_H #define GAME_MWWORLD_PHYSICSSYSTEM_H -#include #include -#include #include "ptr.hpp" #include diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index b7469b8f5..f74fdd3ef 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -1,12 +1,8 @@ #ifndef GAME_MWWORLD_PTR_H #define GAME_MWWORLD_PTR_H -#include - #include -#include - #include "cellstore.hpp" namespace MWWorld diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 41521e47a..3a6e0fc9f 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -1,8 +1,6 @@ #ifndef GAME_MWWORLD_REFDATA_H #define GAME_MWWORLD_REFDATA_H -#include - #include #include "../mwscript/locals.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 39496def3..33c67aad8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -11,9 +11,7 @@ #include "../mwworld/manualref.hpp" /// FIXME -#include "ptr.hpp" #include "player.hpp" -#include "class.hpp" #include "localscripts.hpp" #include "cellfunctors.hpp" diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 64a8c9f8e..c0b93796a 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -1,15 +1,7 @@ #ifndef GAME_MWWORLD_SCENE_H #define GAME_MWWORLD_SCENE_H -#include -#include - -#include - -#include - #include "../mwrender/renderingmanager.hpp" -#include "../mwrender/renderinginterface.hpp" #include "physicssystem.hpp" #include "globals.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 24baac144..a68f08e34 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,8 +1,5 @@ #include "worldimp.hpp" -#include -#include - #include #include @@ -17,16 +14,10 @@ #include "../mwgui/window_manager.hpp" -#include "ptr.hpp" -#include "class.hpp" #include "player.hpp" -#include "weather.hpp" #include "manualref.hpp" -#include "refdata.hpp" -#include "globals.hpp" #include "cellfunctors.hpp" -#include using namespace Ogre; namespace diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8b39a78f1..43b178fe3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -1,32 +1,17 @@ #ifndef GAME_MWWORLD_WORLDIMP_H #define GAME_MWWORLD_WORLDIMP_H -#include -#include - -#include - #include -#include - #include "../mwrender/debugging.hpp" -#include "../mwrender/renderingmanager.hpp" -#include "refdata.hpp" #include "ptr.hpp" -#include "globals.hpp" #include "scene.hpp" #include "physicssystem.hpp" #include "cells.hpp" #include "localscripts.hpp" #include "timestamp.hpp" -#include -#include - -#include - #include "../mwbase/world.hpp" namespace Ogre diff --git a/components/compiler/streamerrorhandler.cpp b/components/compiler/streamerrorhandler.cpp index a1d12b708..8a74ad086 100644 --- a/components/compiler/streamerrorhandler.cpp +++ b/components/compiler/streamerrorhandler.cpp @@ -35,4 +35,5 @@ namespace Compiler << " " << message << std::endl; } - StreamErrorHandler::StreamErrorHandler (std::ostream& ErrorStream) : mStream (ErrorStream) {}} + StreamErrorHandler::StreamErrorHandler (std::ostream& ErrorStream) : mStream (ErrorStream) {} +} diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 17cca7a91..13f1f4a01 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -1,15 +1,11 @@ #ifndef _ESM_READER_H #define _ESM_READER_H -#include - -#include #include #include -#include +#include #include #include -#include #include diff --git a/components/files/collections.hpp b/components/files/collections.hpp index 70aaec55e..ed4aafa13 100644 --- a/components/files/collections.hpp +++ b/components/files/collections.hpp @@ -1,8 +1,6 @@ #ifndef COMPONENTS_FILES_COLLECTION_HPP #define COMPONENTS_FILES_COLLECTION_HPP -#include -#include #include #include "multidircollection.hpp" diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index af9d02b91..0c22c6f7d 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -8,7 +8,6 @@ #endif #include -#include #include #include diff --git a/components/misc/tests/slice_test.cpp b/components/misc/tests/slice_test.cpp index a0ea55311..0d9d7b4ab 100644 --- a/components/misc/tests/slice_test.cpp +++ b/components/misc/tests/slice_test.cpp @@ -2,8 +2,6 @@ using namespace std; -#include - #include "../slice_array.hpp" int main() diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 42e312b7f..4072b4307 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -32,8 +32,7 @@ #include #include -#include -#include +#include #include "record.hpp" #include "nif_types.hpp" diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index b2f023301..88e1ab189 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -25,22 +25,16 @@ #define _BULLET_NIF_LOADER_H_ #include -#include +#include #include #include #include #include -#include #include "openengine/bullet/BulletShapeLoader.h" -#include -#include // For warning messages #include -// float infinity -#include - namespace Nif { class Node; diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 0620ddf49..7b32d24d1 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -28,31 +28,20 @@ #include #include -#include #include -#include "../nif/nif_file.hpp" #include "../nif/node.hpp" -#include "../nif/data.hpp" -#include "../nif/property.hpp" -#include "../nif/controller.hpp" -#include "../nif/extra.hpp" + #include -#include -#include -// For warning messages -#include -using namespace boost::algorithm; - - class BoundsFinder; struct ciLessBoost : std::binary_function { - bool operator() (const std::string & s1, const std::string & s2) const { - //case insensitive version of is_less - return lexicographical_compare(s1, s2, is_iless()); + bool operator() (const std::string & s1, const std::string & s2) const + { + //case insensitive version of is_less + return boost::algorithm::lexicographical_compare(s1, s2, boost::algorithm::is_iless()); } }; @@ -63,7 +52,6 @@ namespace Nif class NiTriShape; } - namespace NifOgre { @@ -177,3 +165,5 @@ class NIFLoader : Ogre::ManualResourceLoader } #endif + + diff --git a/components/nifogre/tests/ogre_common.cpp b/components/nifogre/tests/ogre_common.cpp index 949c91c4f..657913f30 100644 --- a/components/nifogre/tests/ogre_common.cpp +++ b/components/nifogre/tests/ogre_common.cpp @@ -1,6 +1,5 @@ #include #include -#include using namespace std; using namespace Ogre; diff --git a/libs/openengine/gui/layout.hpp b/libs/openengine/gui/layout.hpp index e6feb3d0e..9040dfb90 100644 --- a/libs/openengine/gui/layout.hpp +++ b/libs/openengine/gui/layout.hpp @@ -1,7 +1,6 @@ #ifndef OENGINE_MYGUI_LAYOUT_H #define OENGINE_MYGUI_LAYOUT_H -#include #include namespace OEngine { diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 062559e00..41b7773ea 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -8,8 +8,6 @@ #include #include -#include - #define FADE_OVERLAY_NAME "FadeInOutOverlay" #define FADE_OVERLAY_PANEL_NAME "FadeInOutOverlayPanel" #define FADE_MATERIAL_NAME "FadeInOutMaterial" From a021165d9f2cc002a991f4f5abac014d06e229be Mon Sep 17 00:00:00 2001 From: guidoj Date: Tue, 17 Jul 2012 09:44:24 +0200 Subject: [PATCH 172/688] Changed standard C lib includes to C++ format --- apps/openmw/main.cpp | 2 +- components/bsa/bsa_file.cpp | 4 ++-- components/bsa/tests/bsatool_cmd.c | 4 ++-- components/bsa/tests/bsatool_cmd.h | 2 +- components/esm_store/reclists.hpp | 2 +- components/misc/tests/strops_test.cpp | 2 +- components/nifbullet/bullet_nif_loader.cpp | 2 +- components/terrain/heightmapbuf.hpp | 2 +- components/terrain/triangulator.hpp | 2 +- components/to_utf8/gen_iconv.cpp | 2 +- components/to_utf8/to_utf8.cpp | 2 +- libs/mangle/input/servers/ois_driver.cpp | 2 +- libs/mangle/rend2d/servers/sdl_driver.cpp | 2 +- libs/mangle/rend2d/servers/sdl_gl_driver.cpp | 2 +- libs/openengine/gui/events.cpp | 2 +- libs/openengine/gui/manager.cpp | 2 +- libs/openengine/input/dispatch_map.hpp | 2 +- libs/openengine/input/func_binder.hpp | 2 +- libs/openengine/misc/list.hpp | 2 +- libs/openengine/ogre/renderer.cpp | 2 +- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index fc905b79d..993ec6623 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -11,7 +11,7 @@ // For OutputDebugString #include // makes __argc and __argv available on windows -#include +#include #endif diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 8f605b8ed..053191c9b 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -24,8 +24,8 @@ #include "bsa_file.hpp" #include -#include -#include +#include +#include #include diff --git a/components/bsa/tests/bsatool_cmd.c b/components/bsa/tests/bsatool_cmd.c index f6e10d793..caa8cd720 100644 --- a/components/bsa/tests/bsatool_cmd.c +++ b/components/bsa/tests/bsatool_cmd.c @@ -13,8 +13,8 @@ #include "config.h" #endif -#include -#include +#include +#include #include #ifndef FIX_UNUSED diff --git a/components/bsa/tests/bsatool_cmd.h b/components/bsa/tests/bsatool_cmd.h index 65ebc3e96..98fe2633f 100644 --- a/components/bsa/tests/bsatool_cmd.h +++ b/components/bsa/tests/bsatool_cmd.h @@ -13,7 +13,7 @@ #include "config.h" #endif -#include /* for FILE */ +#include /* for FILE */ #ifdef __cplusplus extern "C" { diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 48bf050cd..ffecfc8de 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/components/misc/tests/strops_test.cpp b/components/misc/tests/strops_test.cpp index 2a1fdd77d..24ab8a298 100644 --- a/components/misc/tests/strops_test.cpp +++ b/components/misc/tests/strops_test.cpp @@ -1,4 +1,4 @@ -#include +#include #include "../stringops.hpp" diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 4105c4c79..ea94e7758 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -23,7 +23,7 @@ http://www.gnu.org/licenses/ . #include "bullet_nif_loader.hpp" #include -#include +#include #include "../nif/nif_file.hpp" #include "../nif/node.hpp" diff --git a/components/terrain/heightmapbuf.hpp b/components/terrain/heightmapbuf.hpp index 82154ce88..d147e6015 100644 --- a/components/terrain/heightmapbuf.hpp +++ b/components/terrain/heightmapbuf.hpp @@ -8,7 +8,7 @@ #include "heightmap.hpp" #include "land_factory.hpp" #include -#include +#include namespace Terrain { diff --git a/components/terrain/triangulator.hpp b/components/terrain/triangulator.hpp index cedf0c6a2..c5c0e699b 100644 --- a/components/terrain/triangulator.hpp +++ b/components/terrain/triangulator.hpp @@ -28,7 +28,7 @@ terrains of the same size, once instance can usually be shared. */ -#include +#include namespace Terrain { diff --git a/components/to_utf8/gen_iconv.cpp b/components/to_utf8/gen_iconv.cpp index b7298e304..cc7cc191a 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/to_utf8/gen_iconv.cpp @@ -5,7 +5,7 @@ using namespace std; #include -#include +#include void tab() { cout << " "; } diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 3fbbeb733..6bcbbd0e6 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -1,7 +1,7 @@ #include "to_utf8.hpp" #include -#include +#include /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library diff --git a/libs/mangle/input/servers/ois_driver.cpp b/libs/mangle/input/servers/ois_driver.cpp index b8e4f5eb9..07ba3e83a 100644 --- a/libs/mangle/input/servers/ois_driver.cpp +++ b/libs/mangle/input/servers/ois_driver.cpp @@ -1,6 +1,6 @@ #include "ois_driver.hpp" -#include +#include #include #include #include diff --git a/libs/mangle/rend2d/servers/sdl_driver.cpp b/libs/mangle/rend2d/servers/sdl_driver.cpp index aa1ff6c6d..84a17933f 100644 --- a/libs/mangle/rend2d/servers/sdl_driver.cpp +++ b/libs/mangle/rend2d/servers/sdl_driver.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include using namespace Mangle::Rend2D; diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp b/libs/mangle/rend2d/servers/sdl_gl_driver.cpp index 2bcb1d677..db519e091 100644 --- a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp +++ b/libs/mangle/rend2d/servers/sdl_gl_driver.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include using namespace Mangle::Rend2D; diff --git a/libs/openengine/gui/events.cpp b/libs/openengine/gui/events.cpp index bce70704b..35b01158b 100644 --- a/libs/openengine/gui/events.cpp +++ b/libs/openengine/gui/events.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include "events.hpp" diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 022c5efb5..9c6ca37eb 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include "manager.hpp" diff --git a/libs/openengine/input/dispatch_map.hpp b/libs/openengine/input/dispatch_map.hpp index f0d4cabe9..be13e7f01 100644 --- a/libs/openengine/input/dispatch_map.hpp +++ b/libs/openengine/input/dispatch_map.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace OEngine { namespace Input { diff --git a/libs/openengine/input/func_binder.hpp b/libs/openengine/input/func_binder.hpp index 7aa733edf..a815ba0ce 100644 --- a/libs/openengine/input/func_binder.hpp +++ b/libs/openengine/input/func_binder.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include namespace OEngine { namespace Input { diff --git a/libs/openengine/misc/list.hpp b/libs/openengine/misc/list.hpp index b08b57494..bda9cb8de 100644 --- a/libs/openengine/misc/list.hpp +++ b/libs/openengine/misc/list.hpp @@ -1,7 +1,7 @@ #ifndef MISC_LIST_H #define MISC_LIST_H -#include +#include namespace Misc{ diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 275b7385a..f2f4b4c81 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -9,7 +9,7 @@ #include "OgreTexture.h" #include "OgreHardwarePixelBuffer.h" -#include +#include #include using namespace Ogre; From e9b95d55cdc0d78c861869c92f31846c047cdc0b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Jul 2012 09:49:52 +0200 Subject: [PATCH 173/688] Revert "Proper way to find and use libtbb" This reverts commit d6bf2b7d294d7298b691677e84ac051c86030397. --- CMakeLists.txt | 1 - apps/esmtool/CMakeLists.txt | 1 - apps/launcher/CMakeLists.txt | 3 +-- apps/openmw/CMakeLists.txt | 1 - cmake/FindTBB.cmake | 14 -------------- 5 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 cmake/FindTBB.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 49684fd7c..9cc741c67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,7 +200,6 @@ find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread) find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) -find_package(TBB REQUIRED) IF(OGRE_STATIC) find_package(Cg REQUIRED) IF(WIN32) diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index eb74aa992..af3dc090e 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -10,7 +10,6 @@ add_executable(esmtool target_link_libraries(esmtool ${Boost_LIBRARIES} - ${TBB_LIBRARY} components ) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 57b7fb71a..ed3559fdc 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -75,9 +75,8 @@ add_executable(omwlauncher target_link_libraries(omwlauncher ${Boost_LIBRARIES} ${OGRE_LIBRARIES} - ${OGRE_STATIC_PLUGINS} + ${OGRE_STATIC_PLUGINS} ${QT_LIBRARIES} - ${TBB_LIBRARY} components ) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index feefa4fd2..a66cda71e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -100,7 +100,6 @@ target_link_libraries(openmw ${BULLET_LIBRARIES} ${MYGUI_LIBRARIES} ${MYGUI_PLATFORM_LIBRARIES} - ${TBB_LIBRARY} components ) diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake deleted file mode 100644 index f84f81f1c..000000000 --- a/cmake/FindTBB.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# Locate TBB -# This module defines -# TBB_LIBRARY -# TBB_FOUND, if false, do not try to link to TBB -# TBB_INCLUDE_DIR, where to find the headers - -FIND_PATH(TBB_INCLUDE_DIR tbb/tbb.h) - -FIND_LIBRARY(TBB_LIBRARY NAMES tbb) - -SET(TBB_FOUND "NO") -IF(TBB_LIBRARY AND TBB_INCLUDE_DIR) - SET(TBB_FOUND "YES") -ENDIF(TBB_LIBRARY AND TBB_INCLUDE_DIR) From a3652f16ce4fba2cb0bfe304717f5c380e6cb6b2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Jul 2012 12:18:43 +0200 Subject: [PATCH 174/688] Issue #342: factored out dynamic stats calculation into a separate function --- apps/openmw/mwmechanics/actors.cpp | 35 ++++++++++++++++-------------- apps/openmw/mwmechanics/actors.hpp | 2 ++ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 95747dbbf..60c96e5b9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -18,22 +18,7 @@ namespace MWMechanics { // magic effects adjustMagicEffects (ptr); - - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - - // calculate dynamic stats - int strength = creatureStats.mAttributes[0].getBase(); - int intelligence = creatureStats.mAttributes[1].getBase(); - int willpower = creatureStats.mAttributes[2].getBase(); - int agility = creatureStats.mAttributes[3].getBase(); - int endurance = creatureStats.mAttributes[5].getBase(); - - double magickaFactor = creatureStats.mMagicEffects.get (EffectKey (84)).mMagnitude*0.1 + 0.5; - - creatureStats.mDynamic[0].setBase (static_cast (0.5 * (strength + endurance))); - creatureStats.mDynamic[1].setBase (static_cast (intelligence + - magickaFactor * intelligence)); - creatureStats.mDynamic[2].setBase (strength+willpower+agility+endurance); + calculateDynamicStats (ptr); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) @@ -64,6 +49,24 @@ namespace MWMechanics // TODO apply diff to other stats } + void Actors::calculateDynamicStats (const MWWorld::Ptr& ptr) + { + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + + int strength = creatureStats.mAttributes[0].getBase(); + int intelligence = creatureStats.mAttributes[1].getBase(); + int willpower = creatureStats.mAttributes[2].getBase(); + int agility = creatureStats.mAttributes[3].getBase(); + int endurance = creatureStats.mAttributes[5].getBase(); + + double magickaFactor = creatureStats.mMagicEffects.get (EffectKey (84)).mMagnitude*0.1 + 0.5; + + creatureStats.mDynamic[0].setBase (static_cast (0.5 * (strength + endurance))); + creatureStats.mDynamic[1].setBase (static_cast (intelligence + + magickaFactor * intelligence)); + creatureStats.mDynamic[2].setBase (strength+willpower+agility+endurance); + } + Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 82f8a943c..7458562e1 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -27,6 +27,8 @@ namespace MWMechanics void adjustMagicEffects (const MWWorld::Ptr& creature); + void calculateDynamicStats (const MWWorld::Ptr& ptr); + public: Actors(); From 76de2f73605cf8cf686b95d7cf1536342209766e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Jul 2012 15:49:37 +0200 Subject: [PATCH 175/688] Issue #342: handle magic effects 79-82 and 17-20 --- apps/openmw/mwmechanics/actors.cpp | 24 ++++++++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 2 ++ apps/openmw/mwmechanics/stat.hpp | 9 ++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 60c96e5b9..d4dc5ea00 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -19,6 +19,7 @@ namespace MWMechanics // magic effects adjustMagicEffects (ptr); calculateDynamicStats (ptr); + calculateCreatureStatModifiers (ptr); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) @@ -67,6 +68,29 @@ namespace MWMechanics creatureStats.mDynamic[2].setBase (strength+willpower+agility+endurance); } + void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr) + { + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + + // attributes + for (int i=0; i<5; ++i) + { + int modifier = creatureStats.mMagicEffects.get (EffectKey (79, i)).mMagnitude + - creatureStats.mMagicEffects.get (EffectKey (17, i)).mMagnitude; + + creatureStats.mAttributes[0].setModifier (modifier); + } + + // dynamic stats + for (int i=0; i<3; ++i) + { + int modifier = creatureStats.mMagicEffects.get (EffectKey (80+i)).mMagnitude + - creatureStats.mMagicEffects.get (EffectKey (18+i)).mMagnitude; + + creatureStats.mDynamic[0].setModifier (modifier); + } + } + Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 7458562e1..d5dcef487 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -29,6 +29,8 @@ namespace MWMechanics void calculateDynamicStats (const MWWorld::Ptr& ptr); + void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr); + public: Actors(); diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 996036fc1..d576020c5 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -147,7 +147,7 @@ namespace MWMechanics void modify (const T& diff) { mStatic.modify (diff); - modifyCurrent (diff); + setCurrent (getCurrent()+diff); } void setCurrent (const T& value) @@ -159,6 +159,13 @@ namespace MWMechanics else if (mCurrent>getModified()) mCurrent = getModified(); } + + void setModifier (const T& modifier) + { + T diff = modifier - mStatic.getModifier(); + mStatic.setModifier (modifier); + setCurrent (getCurrent()+diff); + } }; template From beb18282bb76f34be8d5113c591dc582e7e7b704 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Jul 2012 16:44:55 +0200 Subject: [PATCH 176/688] Issue #342: various fixes --- apps/openmw/mwmechanics/actors.cpp | 6 +++--- apps/openmw/mwworld/actionapply.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d4dc5ea00..72f1be7a8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -18,8 +18,8 @@ namespace MWMechanics { // magic effects adjustMagicEffects (ptr); - calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr); + calculateDynamicStats (ptr); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) @@ -78,7 +78,7 @@ namespace MWMechanics int modifier = creatureStats.mMagicEffects.get (EffectKey (79, i)).mMagnitude - creatureStats.mMagicEffects.get (EffectKey (17, i)).mMagnitude; - creatureStats.mAttributes[0].setModifier (modifier); + creatureStats.mAttributes[i].setModifier (modifier); } // dynamic stats @@ -87,7 +87,7 @@ namespace MWMechanics int modifier = creatureStats.mMagicEffects.get (EffectKey (80+i)).mMagnitude - creatureStats.mMagicEffects.get (EffectKey (18+i)).mMagnitude; - creatureStats.mDynamic[0].setModifier (modifier); + creatureStats.mDynamic[i].setModifier (modifier); } } diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp index c5228d798..b330a70e7 100644 --- a/apps/openmw/mwworld/actionapply.cpp +++ b/apps/openmw/mwworld/actionapply.cpp @@ -22,7 +22,7 @@ namespace MWWorld void ActionApplyWithSkill::execute() { - if (MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor)) + if (MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor) && mUsageType!=-1) MWWorld::Class::get (mTarget).skillUsageSucceeded (mActor, mSkillIndex, mUsageType); } } From 67443756363ef853f6ebf812f06037cd56ad60cf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Jul 2012 18:35:01 +0200 Subject: [PATCH 177/688] fixed a logic error in the auto equip code --- apps/openmw/mwworld/inventorystore.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2fd702f6d..9e4381f07 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -148,7 +148,7 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) bool use = false; if (slots.at (*iter2)==end()) - use = true; // slot was empty before -> skill all further checks + use = true; // slot was empty before -> skip all further checks else { Ptr old = *slots.at (*iter2); @@ -159,7 +159,9 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) int oldSkill = MWWorld::Class::get (old).getEquipmentSkill (old); - if (testSkill!=-1 || oldSkill!=-1 || testSkill!=oldSkill) + if (testSkill!=-1 && oldSkill==-1) + use = true; + else if (testSkill!=-1 && oldSkill!=-1 && testSkill!=oldSkill) { if (stats.getSkill (oldSkill).getModified()>stats.getSkill (testSkill).getModified()) continue; // rejected, because old item better matched the NPC's skills. From 483b125aad586b3165537be7a6c55470503fc47c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 17 Jul 2012 18:37:20 +0200 Subject: [PATCH 178/688] avoid locking up in case actor updates repeatedly throw exceptions --- apps/openmw/mwmechanics/actors.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 72f1be7a8..ff3e91da8 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -126,15 +126,16 @@ namespace MWMechanics if (mDuration>=0.25) { + float totalDuration = mDuration; + mDuration = 0; + for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter) { - updateActor (*iter, mDuration); + updateActor (*iter, totalDuration); if (iter->getTypeName()==typeid (ESM::NPC).name()) - updateNpc (*iter, mDuration, paused); + updateNpc (*iter, totalDuration, paused); } - - mDuration = 0; } for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); From 93f0043afc098b7384b1b02e389c478052bc47bc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 10:04:21 -0700 Subject: [PATCH 179/688] Set the mesh's attach point as the NiTriShape's node --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index c0f3c25d0..fef7054f7 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -816,7 +816,7 @@ public: mesh = meshMgr.createManual(fullname, mGroup, loader); } - meshes.push_back(std::make_pair(mesh, (shape->parent ? shape->parent->name : std::string()))); + meshes.push_back(std::make_pair(mesh, shape->name)); } else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && node->recType != Nif::RC_NiRotatingParticles) From 0a4a141f2e94925f24f60b09e85e4a365ef336d5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 11:23:34 -0700 Subject: [PATCH 180/688] Support multiple meshes for creatures --- apps/openmw/mwrender/animation.cpp | 1 - apps/openmw/mwrender/animation.hpp | 4 +- apps/openmw/mwrender/creatureanimation.cpp | 43 ++++++++++++---------- apps/openmw/mwrender/npcanimation.hpp | 1 + 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 08176077c..17c3f62ac 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -23,7 +23,6 @@ Animation::Animation(OEngine::Render::OgreRenderer& _rend) , mShapeIndexI() , mTransformations(NULL) , mTextmappings(NULL) - , mBase(NULL) { } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 0e96b4d4d..ea18865da 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,6 +1,8 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H +#include + #include #include "../mwworld/actiontalk.hpp" #include @@ -37,7 +39,7 @@ protected: std::vector* mTransformations; std::map* mTextmappings; - Ogre::Entity* mBase; + std::vector mBase; void handleAnimationTransforms(); bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 99800f06c..b42feec68 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,32 +26,35 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O { std::string mesh = "meshes\\" + ref->base->model; - // FIXME: There can be more than one! NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(mesh); - mBase = mRend.getScene()->createEntity(meshes[0].first->getName()); - mBase->setVisibilityFlags(RV_Actors); - - bool transparent = false; - for (unsigned int i=0; i < mBase->getNumSubEntities(); ++i) + for(size_t i = 0;i < meshes.size();i++) { - Ogre::MaterialPtr mat = mBase->getSubEntity(i)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) - { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) - { - Ogre::Pass* pass = passIt.getNext(); + mBase.push_back(mRend.getScene()->createEntity(meshes[i].first->getName())); + Ogre::Entity *base = mBase.back(); + base->setVisibilityFlags(RV_Actors); - if (pass->getDepthWriteEnabled() == false) - transparent = true; + bool transparent = false; + for (unsigned int j=0;j < base->getNumSubEntities() && !transparent; ++j) + { + Ogre::MaterialPtr mat = base->getSubEntity(j)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements() && !transparent) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements() && !transparent) + { + Ogre::Pass* pass = passIt.getNext(); + + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } } } - } - mBase->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - mInsert->attachObject(mBase); + mInsert->attachObject(base); + } } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 82d847091..651f0ffe2 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -41,6 +41,7 @@ private: Ogre::Entity* head; Ogre::SceneNode* mInsert; + Ogre::Entity *mBase; // FIXME: Temporary! bool isBeast; bool isFemale; std::string headModel; From 89cfe778f04df460a03fa3d3f4e00eccec54ee19 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 11:38:20 -0700 Subject: [PATCH 181/688] Support multiple entities for the NPC base --- apps/openmw/mwrender/npcanimation.cpp | 91 ++++++++++++++------------- apps/openmw/mwrender/npcanimation.hpp | 2 +- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index cede30c96..e8e24473c 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -45,7 +45,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere lAnkle(0), groin(0), lfoot(0), - rfoot(0) + rfoot(0), + mSkelBase(0) { MWWorld::LiveCellRef *ref = ptr.get(); @@ -82,33 +83,39 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - // FIXME: There can be more than one! NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(smodel); - mBase = mRend.getScene()->createEntity(meshes[0].first->getName()); - - mBase->setVisibilityFlags(RV_Actors); - bool transparent = false; - for (unsigned int i=0; igetNumSubEntities(); ++i) + for(size_t i = 0;i < meshes.size();i++) { - Ogre::MaterialPtr mat = mBase->getSubEntity(i)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) + mBase.push_back(mRend.getScene()->createEntity(meshes[i].first->getName())); + Ogre::Entity *base = mBase.back(); + + if(!mSkelBase && base->hasSkeleton()) + mSkelBase = base; + + base->setVisibilityFlags(RV_Actors); + bool transparent = false; + for(unsigned int j=0;j < base->getNumSubEntities();++j) { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) + Ogre::MaterialPtr mat = base->getSubEntity(j)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements()) { - Ogre::Pass* pass = passIt.getNext(); - if (pass->getDepthWriteEnabled() == false) - transparent = true; + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } } } - } - mBase->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - mBase->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones - //stay in the same place when we skipanim, or open a gui window + base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones + //stay in the same place when we skipanim, or open a gui window - mInsert->attachObject(mBase); + mInsert->attachObject(base); + } if(isFemale) mInsert->scale(race->data.height.female, race->data.height.female, race->data.height.female); @@ -388,7 +395,7 @@ Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, const std Ogre::Entity* part = mRend.getScene()->createEntity(meshes[0].first->getName()); part->setVisibilityFlags(RV_Actors); - mBase->attachObjectToBone(bonename, part); + mSkelBase->attachObjectToBone(bonename, part); return part; } @@ -430,32 +437,32 @@ void NpcAnimation::removeIndividualPart(int type) if(type == ESM::PRT_Head && head) //0 { - mBase->detachObjectFromBone(head); + mSkelBase->detachObjectFromBone(head); head = 0; } else if(type == ESM::PRT_Hair && hair) //1 { - mBase->detachObjectFromBone(hair); + mSkelBase->detachObjectFromBone(hair); hair = 0; } else if(type == ESM::PRT_Neck && neck) //2 { - mBase->detachObjectFromBone(neck); + mSkelBase->detachObjectFromBone(neck); neck = 0; } else if(type == ESM::PRT_Groin && groin)//4 { - mBase->detachObjectFromBone(groin); + mSkelBase->detachObjectFromBone(groin); groin = 0; } else if(type == ESM::PRT_RWrist && rWrist)//8 { - mBase->detachObjectFromBone(rWrist); + mSkelBase->detachObjectFromBone(rWrist); rWrist = 0; } else if(type == ESM::PRT_LWrist && lWrist) //9 { - mBase->detachObjectFromBone(lWrist); + mSkelBase->detachObjectFromBone(lWrist); lWrist = 0; } else if(type == ESM::PRT_Shield) //10 @@ -463,72 +470,72 @@ void NpcAnimation::removeIndividualPart(int type) } else if(type == ESM::PRT_RForearm && rForearm) //11 { - mBase->detachObjectFromBone(rForearm); + mSkelBase->detachObjectFromBone(rForearm); rForearm = 0; } else if(type == ESM::PRT_LForearm && lForearm) //12 { - mBase->detachObjectFromBone(lForearm); + mSkelBase->detachObjectFromBone(lForearm); lForearm = 0; } else if(type == ESM::PRT_RUpperarm && rupperArm) //13 { - mBase->detachObjectFromBone(rupperArm); + mSkelBase->detachObjectFromBone(rupperArm); rupperArm = 0; } else if(type == ESM::PRT_LUpperarm && lupperArm) //14 { - mBase->detachObjectFromBone(lupperArm); + mSkelBase->detachObjectFromBone(lupperArm); lupperArm = 0; } else if(type == ESM::PRT_RFoot && rfoot) //15 { - mBase->detachObjectFromBone(rfoot); + mSkelBase->detachObjectFromBone(rfoot); rfoot = 0; } else if(type == ESM::PRT_LFoot && lfoot) //16 { - mBase->detachObjectFromBone(lfoot); + mSkelBase->detachObjectFromBone(lfoot); lfoot = 0; } else if(type == ESM::PRT_RAnkle && rAnkle) //17 { - mBase->detachObjectFromBone(rAnkle); + mSkelBase->detachObjectFromBone(rAnkle); rAnkle = 0; } else if(type == ESM::PRT_LAnkle && lAnkle) //18 { - mBase->detachObjectFromBone(lAnkle); + mSkelBase->detachObjectFromBone(lAnkle); lAnkle = 0; } else if(type == ESM::PRT_RKnee && rKnee) //19 { - mBase->detachObjectFromBone(rKnee); + mSkelBase->detachObjectFromBone(rKnee); rKnee = 0; } else if(type == ESM::PRT_LKnee && lKnee) //20 { - mBase->detachObjectFromBone(lKnee); + mSkelBase->detachObjectFromBone(lKnee); lKnee = 0; } else if(type == ESM::PRT_RLeg && rUpperLeg) //21 { - mBase->detachObjectFromBone(rUpperLeg); + mSkelBase->detachObjectFromBone(rUpperLeg); rUpperLeg = 0; } else if(type == ESM::PRT_LLeg && lUpperLeg) //22 { - mBase->detachObjectFromBone(lUpperLeg); + mSkelBase->detachObjectFromBone(lUpperLeg); lUpperLeg = 0; } else if(type == ESM::PRT_RPauldron && rclavicle) //23 { - mBase->detachObjectFromBone(rclavicle); + mSkelBase->detachObjectFromBone(rclavicle); rclavicle = 0; } else if(type == ESM::PRT_LPauldron && lclavicle) //24 { - mBase->detachObjectFromBone(lclavicle); + mSkelBase->detachObjectFromBone(lclavicle); lclavicle = 0; } else if(type == ESM::PRT_Weapon) //25 diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 651f0ffe2..a60c8d7fe 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -41,7 +41,7 @@ private: Ogre::Entity* head; Ogre::SceneNode* mInsert; - Ogre::Entity *mBase; // FIXME: Temporary! + Ogre::Entity *mSkelBase; // Entity with the base skeleton (temporary) bool isBeast; bool isFemale; std::string headModel; From c6cc82a51a6df78113dbc2a6e838dfc9231b5704 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 12:19:50 -0700 Subject: [PATCH 182/688] Handle multiple entities per NPC part --- apps/openmw/mwrender/npcanimation.cpp | 181 ++++++++------------------ apps/openmw/mwrender/npcanimation.hpp | 43 +++--- 2 files changed, 79 insertions(+), 145 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index e8e24473c..ce22da779 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -28,24 +28,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere boots(mInv.end()), leftglove(mInv.end()), rightglove(mInv.end()), skirtiter(mInv.end()), pants(mInv.end()), - lclavicle(0), - rclavicle(0), - rupperArm(0), - lupperArm(0), - rUpperLeg(0), - lUpperLeg(0), - lForearm(0), - rForearm(0), - lWrist(0), - rWrist(0), - rKnee(0), - lKnee(0), - neck(0), - rAnkle(0), - lAnkle(0), - groin(0), - lfoot(0), - rfoot(0), mSkelBase(0) { MWWorld::LiveCellRef *ref = ptr.get(); @@ -388,15 +370,19 @@ void NpcAnimation::updateParts() } } -Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) +std::vector NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) { - // FIXME: There can be more than one! NifOgre::MeshPairList meshes = NIFLoader::load(mesh); - Ogre::Entity* part = mRend.getScene()->createEntity(meshes[0].first->getName()); - part->setVisibilityFlags(RV_Actors); + std::vector parts; + for(size_t i = 0;i < meshes.size();i++) + { + parts.push_back(mRend.getScene()->createEntity(meshes[i].first->getName())); + Ogre::Entity *part = parts.back(); - mSkelBase->attachObjectToBone(bonename, part); - return part; + part->setVisibilityFlags(RV_Actors); + mSkelBase->attachObjectToBone(bonename, part); + } + return parts; } void NpcAnimation::runAnimation(float timepassed) @@ -430,114 +416,61 @@ void NpcAnimation::runAnimation(float timepassed) } } +void NpcAnimation::removeEntities(std::vector &entities) +{ + for(size_t i = 0;i < entities.size();i++) + mSkelBase->detachObjectFromBone(entities[i]); + entities.clear(); +} + void NpcAnimation::removeIndividualPart(int type) { mPartPriorities[type] = 0; mPartslots[type] = -1; - if(type == ESM::PRT_Head && head) //0 - { - mSkelBase->detachObjectFromBone(head); - head = 0; - } - else if(type == ESM::PRT_Hair && hair) //1 - { - mSkelBase->detachObjectFromBone(hair); - hair = 0; - } - else if(type == ESM::PRT_Neck && neck) //2 - { - mSkelBase->detachObjectFromBone(neck); - neck = 0; - } - else if(type == ESM::PRT_Groin && groin)//4 - { - mSkelBase->detachObjectFromBone(groin); - groin = 0; - } - else if(type == ESM::PRT_RWrist && rWrist)//8 - { - mSkelBase->detachObjectFromBone(rWrist); - rWrist = 0; - } - else if(type == ESM::PRT_LWrist && lWrist) //9 - { - mSkelBase->detachObjectFromBone(lWrist); - lWrist = 0; - } + if(type == ESM::PRT_Head) //0 + removeEntities(head); + else if(type == ESM::PRT_Hair) //1 + removeEntities(hair); + else if(type == ESM::PRT_Neck) //2 + removeEntities(neck); + else if(type == ESM::PRT_Groin)//4 + removeEntities(groin); + else if(type == ESM::PRT_RWrist)//8 + removeEntities(rWrist); + else if(type == ESM::PRT_LWrist) //9 + removeEntities(lWrist); else if(type == ESM::PRT_Shield) //10 { } - else if(type == ESM::PRT_RForearm && rForearm) //11 - { - mSkelBase->detachObjectFromBone(rForearm); - rForearm = 0; - } - else if(type == ESM::PRT_LForearm && lForearm) //12 - { - mSkelBase->detachObjectFromBone(lForearm); - lForearm = 0; - } - else if(type == ESM::PRT_RUpperarm && rupperArm) //13 - { - mSkelBase->detachObjectFromBone(rupperArm); - rupperArm = 0; - } - else if(type == ESM::PRT_LUpperarm && lupperArm) //14 - { - mSkelBase->detachObjectFromBone(lupperArm); - lupperArm = 0; - } - else if(type == ESM::PRT_RFoot && rfoot) //15 - { - mSkelBase->detachObjectFromBone(rfoot); - rfoot = 0; - } - else if(type == ESM::PRT_LFoot && lfoot) //16 - { - mSkelBase->detachObjectFromBone(lfoot); - lfoot = 0; - } - else if(type == ESM::PRT_RAnkle && rAnkle) //17 - { - mSkelBase->detachObjectFromBone(rAnkle); - rAnkle = 0; - } - else if(type == ESM::PRT_LAnkle && lAnkle) //18 - { - mSkelBase->detachObjectFromBone(lAnkle); - lAnkle = 0; - } - else if(type == ESM::PRT_RKnee && rKnee) //19 - { - mSkelBase->detachObjectFromBone(rKnee); - rKnee = 0; - } - else if(type == ESM::PRT_LKnee && lKnee) //20 - { - mSkelBase->detachObjectFromBone(lKnee); - lKnee = 0; - } - else if(type == ESM::PRT_RLeg && rUpperLeg) //21 - { - mSkelBase->detachObjectFromBone(rUpperLeg); - rUpperLeg = 0; - } - else if(type == ESM::PRT_LLeg && lUpperLeg) //22 - { - mSkelBase->detachObjectFromBone(lUpperLeg); - lUpperLeg = 0; - } - else if(type == ESM::PRT_RPauldron && rclavicle) //23 - { - mSkelBase->detachObjectFromBone(rclavicle); - rclavicle = 0; - } - else if(type == ESM::PRT_LPauldron && lclavicle) //24 - { - mSkelBase->detachObjectFromBone(lclavicle); - lclavicle = 0; - } + else if(type == ESM::PRT_RForearm) //11 + removeEntities(rForearm); + else if(type == ESM::PRT_LForearm) //12 + removeEntities(lForearm); + else if(type == ESM::PRT_RUpperarm) //13 + removeEntities(rupperArm); + else if(type == ESM::PRT_LUpperarm) //14 + removeEntities(lupperArm); + else if(type == ESM::PRT_RFoot) //15 + removeEntities(rfoot); + else if(type == ESM::PRT_LFoot) //16 + removeEntities(lfoot); + else if(type == ESM::PRT_RAnkle) //17 + removeEntities(rAnkle); + else if(type == ESM::PRT_LAnkle) //18 + removeEntities(lAnkle); + else if(type == ESM::PRT_RKnee) //19 + removeEntities(rKnee); + else if(type == ESM::PRT_LKnee) //20 + removeEntities(lKnee); + else if(type == ESM::PRT_RLeg) //21 + removeEntities(rUpperLeg); + else if(type == ESM::PRT_LLeg) //22 + removeEntities(lUpperLeg); + else if(type == ESM::PRT_RPauldron) //23 + removeEntities(rclavicle); + else if(type == ESM::PRT_LPauldron) //24 + removeEntities(lclavicle); else if(type == ESM::PRT_Weapon) //25 { } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index a60c8d7fe..7830b68cc 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -19,26 +19,26 @@ private: int mPartPriorities[27]; //Bounded Parts - Ogre::Entity* lclavicle; - Ogre::Entity* rclavicle; - Ogre::Entity* rupperArm; - Ogre::Entity* lupperArm; - Ogre::Entity* rUpperLeg; - Ogre::Entity* lUpperLeg; - Ogre::Entity* lForearm; - Ogre::Entity* rForearm; - Ogre::Entity* lWrist; - Ogre::Entity* rWrist; - Ogre::Entity* rKnee; - Ogre::Entity* lKnee; - Ogre::Entity* neck; - Ogre::Entity* rAnkle; - Ogre::Entity* lAnkle; - Ogre::Entity* groin; - Ogre::Entity* lfoot; - Ogre::Entity* rfoot; - Ogre::Entity* hair; - Ogre::Entity* head; + std::vector lclavicle; + std::vector rclavicle; + std::vector rupperArm; + std::vector lupperArm; + std::vector rUpperLeg; + std::vector lUpperLeg; + std::vector lForearm; + std::vector rForearm; + std::vector lWrist; + std::vector rWrist; + std::vector rKnee; + std::vector lKnee; + std::vector neck; + std::vector rAnkle; + std::vector lAnkle; + std::vector groin; + std::vector lfoot; + std::vector rfoot; + std::vector hair; + std::vector head; Ogre::SceneNode* mInsert; Ogre::Entity *mSkelBase; // Entity with the base skeleton (temporary) @@ -65,9 +65,10 @@ private: public: NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); virtual ~NpcAnimation(); - Ogre::Entity* insertBoundedPart(const std::string &mesh, const std::string &bonename); + std::vector insertBoundedPart(const std::string &mesh, const std::string &bonename); virtual void runAnimation(float timepassed); void updateParts(); + void removeEntities(std::vector &entities); void removeIndividualPart(int type); void reserveIndividualPart(int type, int group, int priority); From 3dedac5cb14623911a43de2027c8f86077586e8d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 13:40:03 -0700 Subject: [PATCH 183/688] Create mesh entities for objects when loading the NIF --- apps/openmw/mwrender/objects.cpp | 23 +++++++-------- components/nifogre/ogre_nif_loader.cpp | 39 ++++++++++++++++++++++++++ components/nifogre/ogre_nif_loader.hpp | 12 ++++++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 2ff9de9a4..bc1a4a521 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -93,13 +93,10 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) assert(insert); Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(mesh); - std::vector entities(meshes.size()); - for(size_t i = 0;i < meshes.size();i++) + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, mesh); + for(size_t i = 0;i < entities.mEntities.size();i++) { - entities[i] = mRenderer.getScene()->createEntity(meshes[i].first->getName()); - - const Ogre::AxisAlignedBox &tmp = entities[i]->getBoundingBox(); + const Ogre::AxisAlignedBox &tmp = entities.mEntities[i]->getBoundingBox(); bounds.merge(Ogre::AxisAlignedBox(insert->_getDerivedPosition() + tmp.getMinimum(), insert->_getDerivedPosition() + tmp.getMaximum()) ); @@ -119,9 +116,9 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) mBounds[ptr.getCell()].merge(bounds); bool transparent = false; - for(size_t i = 0;i < entities.size();i++) + for(size_t i = 0;i < entities.mEntities.size();i++) { - Ogre::Entity *ent = entities[i]; + Ogre::Entity *ent = entities.mEntities[i]; for (unsigned int i=0; igetNumSubEntities(); ++i) { Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); @@ -143,10 +140,9 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || transparent) { - for(size_t i = 0;i < entities.size();i++) + for(size_t i = 0;i < entities.mEntities.size();i++) { - Ogre::Entity *ent = entities[i]; - insert->attachObject(ent); + Ogre::Entity *ent = entities.mEntities[i]; ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); @@ -197,9 +193,10 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - for(size_t i = 0;i < entities.size();i++) + for(size_t i = 0;i < entities.mEntities.size();i++) { - Ogre::Entity *ent = entities[i]; + Ogre::Entity *ent = entities.mEntities[i]; + insert->detachObject(ent); sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); mRenderer.getScene()->destroyEntity(ent); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index fef7054f7..c90083619 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -871,6 +872,44 @@ MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, c return meshes; } +EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string &name, const std::string &group) +{ + EntityList entitylist; + + MeshPairList meshes = load(name, NULL, group); + if(meshes.size() == 0) + return entitylist; + + Ogre::SceneManager *sceneMgr = parent->getCreator(); + for(size_t i = 0;i < meshes.size();i++) + { + entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); + Ogre::Entity *entity = entitylist.mEntities.back(); + if(!entitylist.mSkelBase && entity->hasSkeleton()) + entitylist.mSkelBase = entity; + } + + if(entitylist.mSkelBase) + { + parent->attachObject(entitylist.mSkelBase); + for(size_t i = 0;i < entitylist.mEntities.size();i++) + { + Ogre::Entity *entity = entitylist.mEntities[i]; + if(entity != entitylist.mSkelBase && entity->hasSkeleton()) + entity->shareSkeletonInstanceWith(entitylist.mSkelBase); + else if(entity != entitylist.mSkelBase) + entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity); + } + } + else + { + for(size_t i = 0;i < entitylist.mEntities.size();i++) + parent->attachObject(entitylist.mEntities[i]); + } + + return entitylist; +} + /* More code currently not in use, from the old D source. This was used in the first attempt at loading NIF meshes, where each submesh diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 4e9ffc31e..bd6f99b2f 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -58,6 +58,14 @@ namespace Nif namespace NifOgre { +struct EntityList { + std::vector mEntities; + Ogre::Entity *mSkelBase; + + EntityList() : mSkelBase(0) + { } +}; + /** This holds a list of meshes along with the names of their parent nodes */ typedef std::vector< std::pair > MeshPairList; @@ -81,6 +89,10 @@ public: static MeshPairList load(const std::string &name, Ogre::SkeletonPtr *skel=NULL, const std::string &group="General"); + + static EntityList createEntities(Ogre::SceneNode *parent, + const std::string &name, + const std::string &group="General"); }; } From 3efd2030e206099f613d99aadee3ddce3e959a6b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 14:11:07 -0700 Subject: [PATCH 184/688] Create entities when loading NIFs for creatures --- apps/openmw/mwrender/creatureanimation.cpp | 17 +++++++---------- components/nifogre/ogre_nif_loader.cpp | 3 +++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index b42feec68..92930e8d4 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,17 +26,16 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O { std::string mesh = "meshes\\" + ref->base->model; - NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(mesh); - for(size_t i = 0;i < meshes.size();i++) + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mInsert, mesh); + mBase = entities.mEntities; + for(size_t i = 0;i < mBase.size();i++) { - mBase.push_back(mRend.getScene()->createEntity(meshes[i].first->getName())); - Ogre::Entity *base = mBase.back(); - base->setVisibilityFlags(RV_Actors); + mBase[i]->setVisibilityFlags(RV_Actors); bool transparent = false; - for (unsigned int j=0;j < base->getNumSubEntities() && !transparent; ++j) + for (unsigned int j=0;j < mBase[i]->getNumSubEntities() && !transparent; ++j) { - Ogre::MaterialPtr mat = base->getSubEntity(j)->getMaterial(); + Ogre::MaterialPtr mat = mBase[i]->getSubEntity(j)->getMaterial(); Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); while (techIt.hasMoreElements() && !transparent) { @@ -51,9 +50,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O } } } - base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - - mInsert->attachObject(base); + mBase[i]->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } } } diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index c90083619..79eaa6680 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -896,7 +896,10 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string { Ogre::Entity *entity = entitylist.mEntities[i]; if(entity != entitylist.mSkelBase && entity->hasSkeleton()) + { entity->shareSkeletonInstanceWith(entitylist.mSkelBase); + parent->attachObject(entity); + } else if(entity != entitylist.mSkelBase) entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity); } From a590db2cf46da03e5fd8642f629424a039c3434a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 15:43:40 -0700 Subject: [PATCH 185/688] Create entities when loading NIFs for the NPC base --- apps/openmw/mwrender/npcanimation.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ce22da779..86a58c849 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -65,14 +65,12 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load(smodel); - for(size_t i = 0;i < meshes.size();i++) + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mInsert, smodel); + mBase = entities.mEntities; + mSkelBase = entities.mSkelBase; + for(size_t i = 0;i < mBase.size();i++) { - mBase.push_back(mRend.getScene()->createEntity(meshes[i].first->getName())); - Ogre::Entity *base = mBase.back(); - - if(!mSkelBase && base->hasSkeleton()) - mSkelBase = base; + Ogre::Entity *base = mBase[i]; base->setVisibilityFlags(RV_Actors); bool transparent = false; @@ -95,8 +93,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones //stay in the same place when we skipanim, or open a gui window - - mInsert->attachObject(base); } if(isFemale) From 94f3e7a6c069cc9d4de5959a343bc06d0d66b320 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 16:00:03 -0700 Subject: [PATCH 186/688] Store the entity list in the object --- apps/openmw/mwrender/animation.hpp | 3 ++- apps/openmw/mwrender/creatureanimation.cpp | 14 +++++++------- apps/openmw/mwrender/npcanimation.cpp | 15 ++++++--------- apps/openmw/mwrender/npcanimation.hpp | 1 - 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ea18865da..def7f226c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -3,6 +3,7 @@ #include +#include #include #include "../mwworld/actiontalk.hpp" #include @@ -39,7 +40,7 @@ protected: std::vector* mTransformations; std::map* mTextmappings; - std::vector mBase; + NifOgre::EntityList mEntityList; void handleAnimationTransforms(); bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 92930e8d4..fd2855154 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,16 +26,16 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O { std::string mesh = "meshes\\" + ref->base->model; - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mInsert, mesh); - mBase = entities.mEntities; - for(size_t i = 0;i < mBase.size();i++) + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, mesh); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) { - mBase[i]->setVisibilityFlags(RV_Actors); + Ogre::Entity *ent = mEntityList.mEntities[i]; + ent->setVisibilityFlags(RV_Actors); bool transparent = false; - for (unsigned int j=0;j < mBase[i]->getNumSubEntities() && !transparent; ++j) + for (unsigned int j=0;j < ent->getNumSubEntities() && !transparent; ++j) { - Ogre::MaterialPtr mat = mBase[i]->getSubEntity(j)->getMaterial(); + Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial(); Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); while (techIt.hasMoreElements() && !transparent) { @@ -50,7 +50,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O } } } - mBase[i]->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 86a58c849..cdef5ce8b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -27,8 +27,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere leftpauldron(mInv.end()), rightpauldron(mInv.end()), boots(mInv.end()), leftglove(mInv.end()), rightglove(mInv.end()), skirtiter(mInv.end()), - pants(mInv.end()), - mSkelBase(0) + pants(mInv.end()) { MWWorld::LiveCellRef *ref = ptr.get(); @@ -65,12 +64,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mInsert, smodel); - mBase = entities.mEntities; - mSkelBase = entities.mSkelBase; - for(size_t i = 0;i < mBase.size();i++) + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, smodel); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) { - Ogre::Entity *base = mBase[i]; + Ogre::Entity *base = mEntityList.mEntities[i]; base->setVisibilityFlags(RV_Actors); bool transparent = false; @@ -376,7 +373,7 @@ std::vector NpcAnimation::insertBoundedPart(const std::string &me Ogre::Entity *part = parts.back(); part->setVisibilityFlags(RV_Actors); - mSkelBase->attachObjectToBone(bonename, part); + mEntityList.mSkelBase->attachObjectToBone(bonename, part); } return parts; } @@ -415,7 +412,7 @@ void NpcAnimation::runAnimation(float timepassed) void NpcAnimation::removeEntities(std::vector &entities) { for(size_t i = 0;i < entities.size();i++) - mSkelBase->detachObjectFromBone(entities[i]); + mEntityList.mSkelBase->detachObjectFromBone(entities[i]); entities.clear(); } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 7830b68cc..f27642720 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -41,7 +41,6 @@ private: std::vector head; Ogre::SceneNode* mInsert; - Ogre::Entity *mSkelBase; // Entity with the base skeleton (temporary) bool isBeast; bool isFemale; std::string headModel; From 496343b714e9b78dbb3297307122775cac57db03 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 16:36:43 -0700 Subject: [PATCH 187/688] Use the proper member for the NIF type string --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 79eaa6680..305e66df1 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -792,7 +792,7 @@ public: } } else - warn("Unhandled extra data type "+e->recType); + warn("Unhandled extra data type "+e->recName); e = e->extra; } From 1c544682d5bf12b152cb3e868b90e22085806af3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 16:45:01 -0700 Subject: [PATCH 188/688] Stub handling for NiTextKeyExtraData to suppress some spam --- components/nifogre/ogre_nif_loader.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 305e66df1..761924488 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -779,8 +779,9 @@ public: Nif::ExtraPtr e = node->extra; while(!e.empty()) { - Nif::NiStringExtraData *sd = dynamic_cast(e.getPtr()); - if(sd != NULL) + Nif::NiStringExtraData *sd; + Nif::NiTextKeyExtraData *td; + if((sd=dynamic_cast(e.getPtr())) != NULL) { // String markers may contain important information // affecting the entire subtree of this obj @@ -791,6 +792,10 @@ public: flags |= 0x01; } } + else if((td=dynamic_cast(e.getPtr())) != NULL) + { + // TODO: Read and store text keys somewhere + } else warn("Unhandled extra data type "+e->recName); e = e->extra; From 92546ca18d034300b12b179dc099e1cee37c005f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 20:23:09 -0700 Subject: [PATCH 189/688] Move the last bits of code to createEntities --- apps/openmw/mwrender/npcanimation.cpp | 14 ++++---------- apps/openmw/mwrender/sky.cpp | 22 +++++++++------------- components/nifogre/ogre_nif_loader.hpp | 2 +- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index cdef5ce8b..439f6fa2d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -365,16 +365,10 @@ void NpcAnimation::updateParts() std::vector NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) { - NifOgre::MeshPairList meshes = NIFLoader::load(mesh); - std::vector parts; - for(size_t i = 0;i < meshes.size();i++) - { - parts.push_back(mRend.getScene()->createEntity(meshes[i].first->getName())); - Ogre::Entity *part = parts.back(); - - part->setVisibilityFlags(RV_Actors); - mEntityList.mSkelBase->attachObjectToBone(bonename, part); - } + NifOgre::EntityList entities = NIFLoader::createEntities(mInsert, mesh); + std::vector &parts = entities.mEntities; + for(size_t i = 0;i < parts.size();i++) + parts[i]->setVisibilityFlags(RV_Actors); return parts; } diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 8f49e03df..682a96419 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -509,16 +509,14 @@ void SkyManager::create() /// \todo sky_night_02.nif (available in Bloodmoon) mAtmosphereNight = mRootNode->createChildSceneNode(); - NifOgre::MeshPairList meshes = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); - for(size_t i = 0;i < meshes.size();i++) + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, "meshes\\sky_night_01.nif"); + for(size_t i = 0;i < entities.mEntities.size();i++) { - Entity* night1_ent = mSceneMgr->createEntity(meshes[i].first->getName()); + Entity* night1_ent = entities.mEntities[i]; night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); night1_ent->setVisibilityFlags(RV_Sky); night1_ent->setCastShadows(false); - mAtmosphereNight->attachObject(night1_ent); - for (unsigned int i=0; igetNumSubEntities(); ++i) { MaterialPtr mp = night1_ent->getSubEntity(i)->getMaterial(); @@ -589,17 +587,16 @@ void SkyManager::create() fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); mAtmosphereDay = mRootNode->createChildSceneNode(); - meshes = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); - for(size_t i = 0;i < meshes.size();i++) + entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, "meshes\\sky_atmosphere.nif"); + for(size_t i = 0;i < entities.mEntities.size();i++) { - Entity* atmosphere_ent = mSceneMgr->createEntity(meshes[i].first->getName()); + Entity* atmosphere_ent = entities.mEntities[i]; atmosphere_ent->setCastShadows(false); ModVertexAlpha(atmosphere_ent, 0); atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); atmosphere_ent->setVisibilityFlags(RV_Sky); - mAtmosphereDay->attachObject(atmosphere_ent); mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); mAtmosphereMaterial = mAtmosphereMaterial->clone("Atmosphere"); @@ -677,13 +674,12 @@ void SkyManager::create() mCloudFragmentShader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); SceneNode* clouds_node = mRootNode->createChildSceneNode(); - meshes = NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); - for(size_t i = 0;i < meshes.size();i++) + entities = NifOgre::NIFLoader::createEntities(clouds_node, "meshes\\sky_clouds_01.nif"); + for(size_t i = 0;i < entities.mEntities.size();i++) { - Entity* clouds_ent = mSceneMgr->createEntity(meshes[i].first->getName()); + Entity* clouds_ent = entities.mEntities[i]; clouds_ent->setVisibilityFlags(RV_Sky); clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); - clouds_node->attachObject(clouds_ent); mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); mCloudMaterial = mCloudMaterial->clone("Clouds"); diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index bd6f99b2f..f21069b75 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -85,11 +85,11 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { -public: static MeshPairList load(const std::string &name, Ogre::SkeletonPtr *skel=NULL, const std::string &group="General"); +public: static EntityList createEntities(Ogre::SceneNode *parent, const std::string &name, const std::string &group="General"); From b04c3cbcac64988ec3a964a1b323946c6d56abbe Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 21:42:47 -0700 Subject: [PATCH 190/688] Store the entities' root node in the EntityList --- components/nifogre/ogre_nif_loader.cpp | 1 + components/nifogre/ogre_nif_loader.hpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 761924488..c74f1fe0d 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -885,6 +885,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string if(meshes.size() == 0) return entitylist; + entitylist.mRootNode = parent; Ogre::SceneManager *sceneMgr = parent->getCreator(); for(size_t i = 0;i < meshes.size();i++) { diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index f21069b75..70f467295 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -61,8 +61,9 @@ namespace NifOgre struct EntityList { std::vector mEntities; Ogre::Entity *mSkelBase; + Ogre::SceneNode *mRootNode; - EntityList() : mSkelBase(0) + EntityList() : mSkelBase(0), mRootNode(0) { } }; From a32740cf5ee1712299be24d00d8ba67d90fe2e29 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 22:41:26 -0700 Subject: [PATCH 191/688] Remove an unused parameter --- components/nifogre/ogre_nif_loader.cpp | 20 ++++++++------------ components/nifogre/ogre_nif_loader.hpp | 4 +--- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index c74f1fe0d..9c294f8c3 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -180,20 +180,19 @@ void loadResource(Ogre::Resource *resource) buildBones(skel, node); } -static bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node, Ogre::SkeletonPtr *skel) +static bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node) { if(node->boneTrafo != NULL) { Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - Ogre::SkeletonPtr tmp = skelMgr.getByName(name); - if(tmp.isNull()) + Ogre::SkeletonPtr skel = skelMgr.getByName(name); + if(skel.isNull()) { static NIFSkeletonLoader loader; - tmp = skelMgr.create(name, group, true, &loader); + skel = skelMgr.create(name, group, true, &loader); } - if(skel) *skel = tmp; return true; } @@ -205,7 +204,7 @@ static bool createSkeleton(const std::string &name, const std::string &group, Ni { if(!children[i].empty()) { - if(createSkeleton(name, group, children[i].getPtr(), skel)) + if(createSkeleton(name, group, children[i].getPtr())) return true; } } @@ -843,13 +842,10 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, const std::string &group) +MeshPairList NIFLoader::load(const std::string &name, const std::string &group) { MeshPairList meshes; - if(skel != NULL) - skel->setNull(); - Nif::NIFFile nif(name); if (nif.numRecords() < 1) { @@ -869,7 +865,7 @@ MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, c return meshes; } - bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node, skel); + bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node); NIFMeshLoader meshldr(name, group, hasSkel); meshldr.createMeshes(node, meshes); @@ -881,7 +877,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string { EntityList entitylist; - MeshPairList meshes = load(name, NULL, group); + MeshPairList meshes = load(name, group); if(meshes.size() == 0) return entitylist; diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 70f467295..3f58097c0 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -86,9 +86,7 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { - static MeshPairList load(const std::string &name, - Ogre::SkeletonPtr *skel=NULL, - const std::string &group="General"); + static MeshPairList load(const std::string &name, const std::string &group); public: static EntityList createEntities(Ogre::SceneNode *parent, From 4109d0d9232b13813f53a74e78afcffb7f772b45 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 17 Jul 2012 22:47:56 -0700 Subject: [PATCH 192/688] Destroy entities removed from the NPC --- apps/openmw/mwrender/npcanimation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 439f6fa2d..d740dd7db 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -405,8 +405,12 @@ void NpcAnimation::runAnimation(float timepassed) void NpcAnimation::removeEntities(std::vector &entities) { + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); for(size_t i = 0;i < entities.size();i++) + { mEntityList.mSkelBase->detachObjectFromBone(entities[i]); + sceneMgr->destroyEntity(entities[i]); + } entities.clear(); } From 02d39080c8e3f66d7ff77d5cc9617d8ba4174767 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 00:17:39 -0700 Subject: [PATCH 193/688] Destroy entities when they're done with. --- apps/openmw/mwrender/animation.cpp | 5 +++++ apps/openmw/mwrender/npcanimation.cpp | 20 ++++++++++++++++++++ apps/openmw/mwrender/npcanimation.hpp | 1 - 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 17c3f62ac..fddfe7b8a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace MWRender { @@ -28,6 +29,10 @@ Animation::Animation(OEngine::Render::OgreRenderer& _rend) Animation::~Animation() { + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) + sceneMgr->destroyEntity(mEntityList.mEntities[i]); + mEntityList.mEntities.clear(); } void Animation::startScript(std::string groupname, int mode, int loops) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d740dd7db..51bad3d0e 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -17,6 +17,26 @@ using namespace NifOgre; namespace MWRender{ NpcAnimation::~NpcAnimation() { + removeEntities(head); + removeEntities(hair); + removeEntities(neck); + removeEntities(groin); + removeEntities(rWrist); + removeEntities(lWrist); + removeEntities(rForearm); + removeEntities(lForearm); + removeEntities(rupperArm); + removeEntities(lupperArm); + removeEntities(rfoot); + removeEntities(lfoot); + removeEntities(rAnkle); + removeEntities(lAnkle); + removeEntities(rKnee); + removeEntities(lKnee); + removeEntities(rUpperLeg); + removeEntities(lUpperLeg); + removeEntities(rclavicle); + removeEntities(lclavicle); } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index f27642720..6fb549408 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -40,7 +40,6 @@ private: std::vector hair; std::vector head; - Ogre::SceneNode* mInsert; bool isBeast; bool isFemale; std::string headModel; From 6611b0b317986430bac19cb57058cefb1d8c4af4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 10:09:16 -0700 Subject: [PATCH 194/688] Use an array instead of a bunch of nearly-identical if blocks --- apps/openmw/mwrender/npcanimation.cpp | 100 ++++++++------------------ 1 file changed, 28 insertions(+), 72 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 51bad3d0e..eb5fceb0e 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -123,79 +123,35 @@ void NpcAnimation::updateParts() { bool apparelChanged = false; - //mInv.getSlot(MWWorld::InventoryStore::Slot_Robe); - if(robe != mInv.getSlot(MWWorld::InventoryStore::Slot_Robe)) + const struct { + MWWorld::ContainerStoreIterator *iter; + int slot; + } slotlist[] = { + { &robe, MWWorld::InventoryStore::Slot_Robe }, + { &skirtiter, MWWorld::InventoryStore::Slot_Skirt }, + { &helmet, MWWorld::InventoryStore::Slot_Helmet }, + { &cuirass, MWWorld::InventoryStore::Slot_Cuirass }, + { &greaves, MWWorld::InventoryStore::Slot_Greaves }, + { &leftpauldron, MWWorld::InventoryStore::Slot_LeftPauldron }, + { &rightpauldron, MWWorld::InventoryStore::Slot_RightPauldron }, + { &boots, MWWorld::InventoryStore::Slot_Boots }, // !isBeast + { &leftglove, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { &rightglove, MWWorld::InventoryStore::Slot_RightGauntlet }, + { &shirt, MWWorld::InventoryStore::Slot_Shirt }, + { &pants, MWWorld::InventoryStore::Slot_Pants }, + }; + for(size_t i = 0;i < sizeof(slotlist)/sizeof(slotlist[0]);i++) { - // A robe was added or removed - robe = mInv.getSlot(MWWorld::InventoryStore::Slot_Robe); - removePartGroup(MWWorld::InventoryStore::Slot_Robe); - apparelChanged = true; - } - if(skirtiter != mInv.getSlot(MWWorld::InventoryStore::Slot_Skirt)) - { - skirtiter = mInv.getSlot(MWWorld::InventoryStore::Slot_Skirt); - removePartGroup(MWWorld::InventoryStore::Slot_Skirt); - apparelChanged = true; - } - if(helmet != mInv.getSlot(MWWorld::InventoryStore::Slot_Helmet)) - { - helmet = mInv.getSlot(MWWorld::InventoryStore::Slot_Helmet); - removePartGroup(MWWorld::InventoryStore::Slot_Helmet); - apparelChanged = true; - } - if(cuirass != mInv.getSlot(MWWorld::InventoryStore::Slot_Cuirass)) - { - cuirass = mInv.getSlot(MWWorld::InventoryStore::Slot_Cuirass); - removePartGroup(MWWorld::InventoryStore::Slot_Cuirass); - apparelChanged = true; - } - if(greaves != mInv.getSlot(MWWorld::InventoryStore::Slot_Greaves)) - { - greaves = mInv.getSlot(MWWorld::InventoryStore::Slot_Greaves); - removePartGroup(MWWorld::InventoryStore::Slot_Greaves); - apparelChanged = true; - } - if(leftpauldron != mInv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron)) - { - leftpauldron = mInv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_LeftPauldron); - apparelChanged = true; - } - if(rightpauldron != mInv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron)) - { - rightpauldron = mInv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_RightPauldron); - apparelChanged = true; - } - if(!isBeast && boots != mInv.getSlot(MWWorld::InventoryStore::Slot_Boots)) - { - boots = mInv.getSlot(MWWorld::InventoryStore::Slot_Boots); - removePartGroup(MWWorld::InventoryStore::Slot_Boots); - apparelChanged = true; - } - if(leftglove != mInv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet)) - { - leftglove = mInv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet); - apparelChanged = true; - } - if(rightglove != mInv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet)) - { - rightglove = mInv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_RightGauntlet); - apparelChanged = true; - } - if(shirt != mInv.getSlot(MWWorld::InventoryStore::Slot_Shirt)) - { - shirt = mInv.getSlot(MWWorld::InventoryStore::Slot_Shirt); - removePartGroup(MWWorld::InventoryStore::Slot_Shirt); - apparelChanged = true; - } - if(pants != mInv.getSlot(MWWorld::InventoryStore::Slot_Pants)) - { - pants = mInv.getSlot(MWWorld::InventoryStore::Slot_Pants); - removePartGroup(MWWorld::InventoryStore::Slot_Pants); - apparelChanged = true; + if(slotlist[i].iter == &boots && isBeast) + continue; + + MWWorld::ContainerStoreIterator iter = mInv.getSlot(slotlist[i].slot); + if(*slotlist[i].iter != iter) + { + *slotlist[i].iter = iter; + removePartGroup(slotlist[i].slot); + apparelChanged = true; + } } if(apparelChanged) From 04e496a6ca4e1329c95b084b77e14572b0c61455 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 10:26:25 -0700 Subject: [PATCH 195/688] Store the entity lists for NPC parts --- apps/openmw/mwrender/npcanimation.cpp | 18 ++++++----- apps/openmw/mwrender/npcanimation.hpp | 44 +++++++++++++-------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index eb5fceb0e..b4e39ff15 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -339,13 +339,13 @@ void NpcAnimation::updateParts() } } -std::vector NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) +NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) { NifOgre::EntityList entities = NIFLoader::createEntities(mInsert, mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) parts[i]->setVisibilityFlags(RV_Actors); - return parts; + return entities; } void NpcAnimation::runAnimation(float timepassed) @@ -379,15 +379,19 @@ void NpcAnimation::runAnimation(float timepassed) } } -void NpcAnimation::removeEntities(std::vector &entities) +void NpcAnimation::removeEntities(NifOgre::EntityList &entities) { + assert(&entities != &mEntityList); + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < entities.size();i++) + for(size_t i = 0;i < entities.mEntities.size();i++) { - mEntityList.mSkelBase->detachObjectFromBone(entities[i]); - sceneMgr->destroyEntity(entities[i]); + mEntityList.mSkelBase->detachObjectFromBone(entities.mEntities[i]); + sceneMgr->destroyEntity(entities.mEntities[i]); } - entities.clear(); + entities.mEntities.clear(); + entities.mSkelBase = NULL; + entities.mRootNode = NULL; } void NpcAnimation::removeIndividualPart(int type) diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 6fb549408..1eec1294f 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -19,26 +19,26 @@ private: int mPartPriorities[27]; //Bounded Parts - std::vector lclavicle; - std::vector rclavicle; - std::vector rupperArm; - std::vector lupperArm; - std::vector rUpperLeg; - std::vector lUpperLeg; - std::vector lForearm; - std::vector rForearm; - std::vector lWrist; - std::vector rWrist; - std::vector rKnee; - std::vector lKnee; - std::vector neck; - std::vector rAnkle; - std::vector lAnkle; - std::vector groin; - std::vector lfoot; - std::vector rfoot; - std::vector hair; - std::vector head; + NifOgre::EntityList lclavicle; + NifOgre::EntityList rclavicle; + NifOgre::EntityList rupperArm; + NifOgre::EntityList lupperArm; + NifOgre::EntityList rUpperLeg; + NifOgre::EntityList lUpperLeg; + NifOgre::EntityList lForearm; + NifOgre::EntityList rForearm; + NifOgre::EntityList lWrist; + NifOgre::EntityList rWrist; + NifOgre::EntityList rKnee; + NifOgre::EntityList lKnee; + NifOgre::EntityList neck; + NifOgre::EntityList rAnkle; + NifOgre::EntityList lAnkle; + NifOgre::EntityList groin; + NifOgre::EntityList lfoot; + NifOgre::EntityList rfoot; + NifOgre::EntityList hair; + NifOgre::EntityList head; bool isBeast; bool isFemale; @@ -63,10 +63,10 @@ private: public: NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); virtual ~NpcAnimation(); - std::vector insertBoundedPart(const std::string &mesh, const std::string &bonename); + NifOgre::EntityList insertBoundedPart(const std::string &mesh, const std::string &bonename); virtual void runAnimation(float timepassed); void updateParts(); - void removeEntities(std::vector &entities); + void removeEntities(NifOgre::EntityList &entities); void removeIndividualPart(int type); void reserveIndividualPart(int type, int group, int priority); From db948969c98cd032915a7aae450f1d522ab48bba Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 11:14:13 -0700 Subject: [PATCH 196/688] Attach NPC parts to the proper bone --- apps/openmw/mwrender/npcanimation.cpp | 3 +- components/nifogre/ogre_nif_loader.cpp | 43 ++++++++++++++++++++++++++ components/nifogre/ogre_nif_loader.hpp | 6 ++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b4e39ff15..991ef827e 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -341,7 +341,8 @@ void NpcAnimation::updateParts() NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) { - NifOgre::EntityList entities = NIFLoader::createEntities(mInsert, mesh); + NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, + mInsert->getCreator(), mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) parts[i]->setVisibilityFlags(RV_Actors); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 9c294f8c3..44992eea4 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -915,6 +915,49 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string return entitylist; } +EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneManager *sceneMgr, + const std::string &name, + const std::string &group) +{ + EntityList entitylist; + + MeshPairList meshes = load(name, group); + if(meshes.size() == 0) + return entitylist; + + for(size_t i = 0;i < meshes.size();i++) + { + entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); + Ogre::Entity *entity = entitylist.mEntities.back(); + if(!entitylist.mSkelBase && entity->hasSkeleton()) + entitylist.mSkelBase = entity; + } + + if(entitylist.mSkelBase) + { + parent->attachObjectToBone(bonename, entitylist.mSkelBase); + for(size_t i = 0;i < entitylist.mEntities.size();i++) + { + Ogre::Entity *entity = entitylist.mEntities[i]; + if(entity != entitylist.mSkelBase && entity->hasSkeleton()) + { + entity->shareSkeletonInstanceWith(entitylist.mSkelBase); + parent->attachObjectToBone(bonename, entity); + } + else if(entity != entitylist.mSkelBase) + entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity); + } + } + else + { + for(size_t i = 0;i < entitylist.mEntities.size();i++) + parent->attachObjectToBone(bonename, entitylist.mEntities[i]); + } + + return entitylist; +} + /* More code currently not in use, from the old D source. This was used in the first attempt at loading NIF meshes, where each submesh diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 3f58097c0..2348a9b10 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -58,6 +58,7 @@ namespace Nif namespace NifOgre { +// FIXME: This should not be in NifOgre, it works agnostic of what model format is used struct EntityList { std::vector mEntities; Ogre::Entity *mSkelBase; @@ -89,6 +90,11 @@ class NIFLoader static MeshPairList load(const std::string &name, const std::string &group); public: + static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneManager *sceneMgr, + const std::string &name, + const std::string &group="General"); + static EntityList createEntities(Ogre::SceneNode *parent, const std::string &name, const std::string &group="General"); From 3b29d280b91c31113cde0860722fc4484769a164 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 12:47:16 -0700 Subject: [PATCH 197/688] Filter out skinned shapes that don't match the bone name --- components/nifogre/ogre_nif_loader.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 44992eea4..d72c45abd 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -926,12 +926,23 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(meshes.size() == 0) return entitylist; + std::string filter = "Tri "+bonename; for(size_t i = 0;i < meshes.size();i++) { - entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); - Ogre::Entity *entity = entitylist.mEntities.back(); - if(!entitylist.mSkelBase && entity->hasSkeleton()) - entitylist.mSkelBase = entity; + Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); + if(ent->hasSkeleton()) + { + if(meshes[i].second.length() < filter.length() || filter.compare(meshes[i].second) != 0) + { + sceneMgr->destroyEntity(ent); + meshes.erase(meshes.begin()+i); + i--; + continue; + } + if(!entitylist.mSkelBase) + entitylist.mSkelBase = ent; + } + entitylist.mEntities.push_back(ent); } if(entitylist.mSkelBase) @@ -946,7 +957,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo parent->attachObjectToBone(bonename, entity); } else if(entity != entitylist.mSkelBase) - entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity); + parent->attachObjectToBone(bonename, entity); } } else From e8ff304562ff6d87bd9781c3a6a6e6b1fd07963f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 12:53:45 -0700 Subject: [PATCH 198/688] Fix the initial normal vector for vertex fixups --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index d72c45abd..23e1e99d5 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -540,7 +540,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader // better to transform the bones into bind position, but there doesn't seem to // be a reliable way to do that. std::vector newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); - std::vector newNorms(srcNorms.size(), Ogre::Vector3(1.0f)); + std::vector newNorms(srcNorms.size(), Ogre::Vector3(0.0f)); const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; From 2890904fb543c8555578fcb2e486d9812279a332 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 15:27:33 -0700 Subject: [PATCH 199/688] Use lowercase names for the mesh and skeleton resources To reduce the risk of duplicates due to different capitalizations. --- components/nifogre/ogre_nif_loader.cpp | 5 ++++- components/nifogre/ogre_nif_loader.hpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 23e1e99d5..6e2aa1663 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -807,6 +807,7 @@ public: Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); std::string fullname = mName+"@"+shape->name; + std::transform(fullname.begin(), fullname.end(), fullname.begin(), ::tolower); Ogre::MeshPtr mesh = meshMgr.getByName(fullname); if(mesh.isNull()) { @@ -842,10 +843,12 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -MeshPairList NIFLoader::load(const std::string &name, const std::string &group) +MeshPairList NIFLoader::load(std::string name, const std::string &group) { MeshPairList meshes; + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + Nif::NIFFile nif(name); if (nif.numRecords() < 1) { diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 2348a9b10..b0b1ce27b 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -87,7 +87,7 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { - static MeshPairList load(const std::string &name, const std::string &group); + static MeshPairList load(std::string name, const std::string &group); public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, From 04b244cf9e63225e69ed707d44b9a71746471a6c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 17:18:21 -0700 Subject: [PATCH 200/688] Use the mesh's skeleton to transform shapes into "bind pose" instead of the NIF nodes --- components/nifogre/ogre_nif_loader.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 6e2aa1663..9fd3d44f1 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -526,6 +526,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader // Convert NiTriShape to Ogre::SubMesh void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape *shape) { + Ogre::SkeletonPtr skel; const Nif::NiTriShapeData *data = shape->data.getPtr(); const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); std::vector srcVerts = data->vertices; @@ -536,6 +537,11 @@ class NIFMeshLoader : Ogre::ManualResourceLoader // explicitly attached later. mesh->setSkeletonName(mName); + // Get the skeleton resource, so vertices can be transformed into the bones' initial state. + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + skel = skelMgr->getByName(mName); + skel->touch(); + // Convert vertices and normals to bone space from bind position. It would be // better to transform the bones into bind position, but there doesn't seem to // be a reliable way to do that. @@ -546,10 +552,13 @@ class NIFMeshLoader : Ogre::ManualResourceLoader const Nif::NodeList &bones = skin->bones; for(size_t b = 0;b < bones.length();b++) { - Ogre::Matrix4 mat(Ogre::Matrix4::IDENTITY); + Ogre::Bone *bone = skel->getBone(bones[b]->name); + Ogre::Matrix4 mat, mat2; mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), Ogre::Quaternion(data->bones[b].trafo.rotation)); - mat = bones[b]->getWorldTransform() * mat; + mat2.makeTransform(bone->_getDerivedPosition(), bone->_getDerivedScale(), + bone->_getDerivedOrientation()); + mat = mat2 * mat; const std::vector &weights = data->bones[b].weights; for(size_t i = 0;i < weights.size();i++) @@ -692,11 +701,6 @@ class NIFMeshLoader : Ogre::ManualResourceLoader // Assign bone weights for this TriShape if(skin != NULL) { - // Get the skeleton resource, so weights can be applied - Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - Ogre::SkeletonPtr skel = skelMgr->getByName(mesh->getSkeletonName()); - skel->touch(); - const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) From 626dcd54dc1da12647be1316a6010fe30e689da3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 17:26:51 -0700 Subject: [PATCH 201/688] Store the skeleton name with the mesh resource loader instead of a flag --- components/nifogre/ogre_nif_loader.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 9fd3d44f1..c4e1398f9 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -509,7 +509,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader std::string mGroup; std::string mShapeName; std::string mMaterialName; - bool mHasSkel; + std::string mSkelName; void warn(const std::string &msg) { @@ -535,7 +535,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader { // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be // explicitly attached later. - mesh->setSkeletonName(mName); + mesh->setSkeletonName(mSkelName); // Get the skeleton resource, so vertices can be transformed into the bones' initial state. Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); @@ -579,7 +579,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader srcVerts = newVerts; srcNorms = newNorms; } - else if(!mHasSkel) + else if(mSkelName.length() == 0) { // No skinning and no skeleton, so just transform the vertices and // normals into position. @@ -752,10 +752,9 @@ class NIFMeshLoader : Ogre::ManualResourceLoader public: NIFMeshLoader() - : mHasSkel(false) { } - NIFMeshLoader(const std::string &name, const std::string &group, bool hasSkel) - : mName(name), mGroup(group), mHasSkel(hasSkel) + NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName) + : mName(name), mGroup(group), mSkelName(skelName) { } virtual void loadResource(Ogre::Resource *resource) @@ -765,8 +764,8 @@ public: if(!mShapeName.length()) { - if(mHasSkel) - mesh->setSkeletonName(mName); + if(mSkelName.length() > 0) + mesh->setSkeletonName(mSkelName); return; } @@ -874,7 +873,7 @@ MeshPairList NIFLoader::load(std::string name, const std::string &group) bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node); - NIFMeshLoader meshldr(name, group, hasSkel); + NIFMeshLoader meshldr(name, group, (hasSkel ? name : std::string())); meshldr.createMeshes(node, meshes); return meshes; From 5154188110d7bcf223c4c73198b370de19d556bc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 18:29:25 -0700 Subject: [PATCH 202/688] Allow specifying an alternate skeleton for mesh skinning --- components/nifogre/ogre_nif_loader.cpp | 18 +++++++++++------- components/nifogre/ogre_nif_loader.hpp | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index c4e1398f9..8e73c2503 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -539,7 +539,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader // Get the skeleton resource, so vertices can be transformed into the bones' initial state. Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); - skel = skelMgr->getByName(mName); + skel = skelMgr->getByName(mSkelName); skel->touch(); // Convert vertices and normals to bone space from bind position. It would be @@ -808,7 +808,9 @@ public: const NiTriShape *shape = dynamic_cast(node); Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - std::string fullname = mName+"@"+shape->name; + std::string fullname = mName+"@shape="+shape->name; + if(mSkelName.length() > 0 && mName != mSkelName) + fullname += "@skel="+mSkelName; std::transform(fullname.begin(), fullname.end(), fullname.begin(), ::tolower); Ogre::MeshPtr mesh = meshMgr.getByName(fullname); @@ -846,11 +848,12 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -MeshPairList NIFLoader::load(std::string name, const std::string &group) +MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) { MeshPairList meshes; std::transform(name.begin(), name.end(), name.begin(), ::tolower); + std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower); Nif::NIFFile nif(name); if (nif.numRecords() < 1) @@ -873,7 +876,7 @@ MeshPairList NIFLoader::load(std::string name, const std::string &group) bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node); - NIFMeshLoader meshldr(name, group, (hasSkel ? name : std::string())); + NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); return meshes; @@ -883,7 +886,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string { EntityList entitylist; - MeshPairList meshes = load(name, group); + MeshPairList meshes = load(name, name, group); if(meshes.size() == 0) return entitylist; @@ -928,7 +931,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo { EntityList entitylist; - MeshPairList meshes = load(name, group); + MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group); if(meshes.size() == 0) return entitylist; @@ -953,13 +956,14 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(entitylist.mSkelBase) { + entitylist.mSkelBase->shareSkeletonInstanceWith(parent); parent->attachObjectToBone(bonename, entitylist.mSkelBase); for(size_t i = 0;i < entitylist.mEntities.size();i++) { Ogre::Entity *entity = entitylist.mEntities[i]; if(entity != entitylist.mSkelBase && entity->hasSkeleton()) { - entity->shareSkeletonInstanceWith(entitylist.mSkelBase); + entity->shareSkeletonInstanceWith(parent); parent->attachObjectToBone(bonename, entity); } else if(entity != entitylist.mSkelBase) diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index b0b1ce27b..49d19fdb8 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -87,7 +87,7 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { - static MeshPairList load(std::string name, const std::string &group); + static MeshPairList load(std::string name, std::string skelName, const std::string &group); public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, From d9b64b77ec170935744d57db669d354c9a39dba5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 18:38:55 -0700 Subject: [PATCH 203/688] Attach skinned parts to the scene node instead of the named bone --- apps/openmw/mwrender/npcanimation.cpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 7 ++++--- components/nifogre/ogre_nif_loader.hpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 991ef827e..00583d427 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -342,7 +342,7 @@ void NpcAnimation::updateParts() NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) { NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, - mInsert->getCreator(), mesh); + mInsert, mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) parts[i]->setVisibilityFlags(RV_Actors); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 8e73c2503..646ec6c0b 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -925,7 +925,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string } EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneManager *sceneMgr, + Ogre::SceneNode *parentNode, const std::string &name, const std::string &group) { @@ -935,6 +935,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(meshes.size() == 0) return entitylist; + Ogre::SceneManager *sceneMgr = parentNode->getCreator(); std::string filter = "Tri "+bonename; for(size_t i = 0;i < meshes.size();i++) { @@ -957,14 +958,14 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(entitylist.mSkelBase) { entitylist.mSkelBase->shareSkeletonInstanceWith(parent); - parent->attachObjectToBone(bonename, entitylist.mSkelBase); + parentNode->attachObject(entitylist.mSkelBase); for(size_t i = 0;i < entitylist.mEntities.size();i++) { Ogre::Entity *entity = entitylist.mEntities[i]; if(entity != entitylist.mSkelBase && entity->hasSkeleton()) { entity->shareSkeletonInstanceWith(parent); - parent->attachObjectToBone(bonename, entity); + parentNode->attachObject(entity); } else if(entity != entitylist.mSkelBase) parent->attachObjectToBone(bonename, entity); diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 49d19fdb8..76e94975c 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -91,7 +91,7 @@ class NIFLoader public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneManager *sceneMgr, + Ogre::SceneNode *parentNode, const std::string &name, const std::string &group="General"); From 4af1bce659722bd06c89a492bfb291f231891d06 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 20:14:23 -0700 Subject: [PATCH 204/688] Restore and fix some missing parts --- apps/openmw/mwrender/npcanimation.cpp | 16 ++++++++++++++++ apps/openmw/mwrender/npcanimation.hpp | 4 ++++ components/nifogre/ogre_nif_loader.cpp | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 00583d427..62e9fd580 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -20,7 +20,10 @@ NpcAnimation::~NpcAnimation() removeEntities(head); removeEntities(hair); removeEntities(neck); + removeEntities(chest); removeEntities(groin); + removeEntities(rHand); + removeEntities(lHand); removeEntities(rWrist); removeEntities(lWrist); removeEntities(rForearm); @@ -37,6 +40,7 @@ NpcAnimation::~NpcAnimation() removeEntities(lUpperLeg); removeEntities(rclavicle); removeEntities(lclavicle); + removeEntities(tail); } @@ -406,8 +410,14 @@ void NpcAnimation::removeIndividualPart(int type) removeEntities(hair); else if(type == ESM::PRT_Neck) //2 removeEntities(neck); + else if(type == ESM::PRT_Cuirass)//3 + removeEntities(chest); else if(type == ESM::PRT_Groin)//4 removeEntities(groin); + else if(type == ESM::PRT_RHand)//6 + removeEntities(rHand); + else if(type == ESM::PRT_LHand)//7 + removeEntities(lHand); else if(type == ESM::PRT_RWrist)//8 removeEntities(rWrist); else if(type == ESM::PRT_LWrist) //9 @@ -446,6 +456,8 @@ void NpcAnimation::removeIndividualPart(int type) else if(type == ESM::PRT_Weapon) //25 { } + else if(type == ESM::PRT_Tail) //26 + removeEntities(tail); } void NpcAnimation::reserveIndividualPart(int type, int group, int priority) @@ -487,6 +499,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, neck = insertBoundedPart(mesh, "Neck"); break; case ESM::PRT_Cuirass: //3 + chest = insertBoundedPart(mesh, "Chest"); break; case ESM::PRT_Groin: //4 groin = insertBoundedPart(mesh, "Groin"); @@ -494,8 +507,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, case ESM::PRT_Skirt: //5 break; case ESM::PRT_RHand: //6 + rHand = insertBoundedPart(mesh, "Right Hand"); break; case ESM::PRT_LHand: //7 + lHand = insertBoundedPart(mesh, "Left Hand"); break; case ESM::PRT_RWrist: //8 rWrist = insertBoundedPart(mesh, "Right Wrist"); @@ -550,6 +565,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, case ESM::PRT_Weapon: //25 break; case ESM::PRT_Tail: //26 + tail = insertBoundedPart(mesh, "Tail"); break; } return true; diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 1eec1294f..9dda169e2 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -38,7 +38,11 @@ private: NifOgre::EntityList lfoot; NifOgre::EntityList rfoot; NifOgre::EntityList hair; + NifOgre::EntityList rHand; + NifOgre::EntityList lHand; NifOgre::EntityList head; + NifOgre::EntityList chest; + NifOgre::EntityList tail; bool isBeast; bool isFemale; diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 646ec6c0b..e8bc6133a 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -942,7 +942,8 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); if(ent->hasSkeleton()) { - if(meshes[i].second.length() < filter.length() || filter.compare(meshes[i].second) != 0) + if(meshes[i].second.length() < filter.length() || + meshes[i].second.compare(0, filter.length(), filter) != 0) { sceneMgr->destroyEntity(ent); meshes.erase(meshes.begin()+i); From bd74ab027a16801da7db778f62c9f4a898517cda Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 21:21:49 -0700 Subject: [PATCH 205/688] Mirror left-sided parts --- components/nifogre/ogre_nif_loader.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index e8bc6133a..c470363b0 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -956,6 +957,10 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo entitylist.mEntities.push_back(ent); } + Ogre::Vector3 scale(1.0f); + if(bonename.find("Left") != std::string::npos) + scale.x *= -1.0f; + if(entitylist.mSkelBase) { entitylist.mSkelBase->shareSkeletonInstanceWith(parent); @@ -969,13 +974,19 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo parentNode->attachObject(entity); } else if(entity != entitylist.mSkelBase) - parent->attachObjectToBone(bonename, entity); + { + Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity); + tag->setScale(scale); + } } } else { for(size_t i = 0;i < entitylist.mEntities.size();i++) - parent->attachObjectToBone(bonename, entitylist.mEntities[i]); + { + Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entitylist.mEntities[i]); + tag->setScale(scale); + } } return entitylist; From b505d4ace0977522aab257b86caca4cad1d320af Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 21:39:50 -0700 Subject: [PATCH 206/688] Fix feet and entity part detachment --- apps/openmw/mwrender/npcanimation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 62e9fd580..138a8eb86 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -391,7 +391,7 @@ void NpcAnimation::removeEntities(NifOgre::EntityList &entities) Ogre::SceneManager *sceneMgr = mInsert->getCreator(); for(size_t i = 0;i < entities.mEntities.size();i++) { - mEntityList.mSkelBase->detachObjectFromBone(entities.mEntities[i]); + entities.mEntities[i]->detachFromParent(); sceneMgr->destroyEntity(entities.mEntities[i]); } entities.mEntities.clear(); @@ -533,10 +533,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, lupperArm = insertBoundedPart(mesh, "Left Upper Arm"); break; case ESM::PRT_RFoot: //15 - lupperArm = insertBoundedPart(mesh, "Right Foot"); + rfoot = insertBoundedPart(mesh, "Right Foot"); break; case ESM::PRT_LFoot: //16 - lupperArm = insertBoundedPart(mesh, "Left Foot"); + lfoot = insertBoundedPart(mesh, "Left Foot"); break; case ESM::PRT_RAnkle: //17 rAnkle = insertBoundedPart(mesh, "Right Ankle"); From c9b1f72d81b00f9e809b278885795d5300557a16 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 22:31:07 -0700 Subject: [PATCH 207/688] Use a case-insensitive compare for the part filter --- components/nifogre/ogre_nif_loader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index c470363b0..639ebaa12 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -944,7 +944,8 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(ent->hasSkeleton()) { if(meshes[i].second.length() < filter.length() || - meshes[i].second.compare(0, filter.length(), filter) != 0) + !boost::algorithm::lexicographical_compare(meshes[i].second.substr(0, filter.length()), + filter, boost::algorithm::is_iequal())) { sceneMgr->destroyEntity(ent); meshes.erase(meshes.begin()+i); From 6caa39629d85f22320eff0abd5def679a043a0c4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 18 Jul 2012 22:32:26 -0700 Subject: [PATCH 208/688] Reimplement the skirt part --- apps/openmw/mwrender/npcanimation.cpp | 4 ++++ apps/openmw/mwrender/npcanimation.hpp | 1 + 2 files changed, 5 insertions(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 138a8eb86..9f28dbf13 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -22,6 +22,7 @@ NpcAnimation::~NpcAnimation() removeEntities(neck); removeEntities(chest); removeEntities(groin); + removeEntities(skirt); removeEntities(rHand); removeEntities(lHand); removeEntities(rWrist); @@ -414,6 +415,8 @@ void NpcAnimation::removeIndividualPart(int type) removeEntities(chest); else if(type == ESM::PRT_Groin)//4 removeEntities(groin); + else if(type == ESM::PRT_Skirt)//5 + removeEntities(skirt); else if(type == ESM::PRT_RHand)//6 removeEntities(rHand); else if(type == ESM::PRT_LHand)//7 @@ -505,6 +508,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, groin = insertBoundedPart(mesh, "Groin"); break; case ESM::PRT_Skirt: //5 + skirt = insertBoundedPart(mesh, "Groin"); break; case ESM::PRT_RHand: //6 rHand = insertBoundedPart(mesh, "Right Hand"); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 9dda169e2..d4b2a5b9e 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -35,6 +35,7 @@ private: NifOgre::EntityList rAnkle; NifOgre::EntityList lAnkle; NifOgre::EntityList groin; + NifOgre::EntityList skirt; NifOgre::EntityList lfoot; NifOgre::EntityList rfoot; NifOgre::EntityList hair; From 739455e6f81c53f16e6cde4087996fac862775cf Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jul 2012 16:23:30 +0200 Subject: [PATCH 209/688] new water WIP, caustics, chromatic abberation, accurate fresnel, underwater reflection, etc --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/main.cpp | 6 +- apps/openmw/mwrender/renderconst.hpp | 14 +- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/terrain.cpp | 38 +--- apps/openmw/mwrender/terrainmaterial.cpp | 6 + apps/openmw/mwrender/water.cpp | 92 +++++---- apps/openmw/mwrender/water.hpp | 8 +- extern/shiny | 2 +- files/gbuffer/gbuffer.compositor | 4 +- files/materials/caustics.h | 117 +++++++++++ files/materials/core.h | 2 +- files/materials/objects.mat | 5 + files/materials/objects.shader | 52 ++++- files/materials/terrain.shader | 46 ++++- files/materials/water.mat | 13 +- files/materials/water.shader | 228 ++++++++++++++++++++++ files/materials/water.shaderset | 15 ++ files/water/underwater_dome.mesh | Bin 0 -> 22585 bytes files/water/water.compositor | 1 - files/water/water.material | 2 +- files/water/water_nm.png | Bin 0 -> 24405 bytes 22 files changed, 556 insertions(+), 98 deletions(-) create mode 100644 files/materials/caustics.h create mode 100644 files/water/underwater_dome.mesh create mode 100644 files/water/water_nm.png diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 32faafcc8..8e424ad56 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -102,6 +102,7 @@ target_link_libraries(openmw ${MYGUI_PLATFORM_LIBRARIES} "shiny" "shiny.OgrePlatform" + components ) # Fix for not visible pthreads functions for linker with glibc 2.15 diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 993ec6623..86584cc22 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -261,7 +261,7 @@ int main(int argc, char**argv) boost::filesystem::current_path(bundlePath); #endif - try + //try { Files::ConfigurationManager cfgMgr; OMW::Engine engine(cfgMgr); @@ -271,11 +271,11 @@ int main(int argc, char**argv) engine.go(); } } - catch (std::exception &e) + /*catch (std::exception &e) { std::cout << "\nERROR: " << e.what() << std::endl; return 1; - } + }*/ return 0; } diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index c4aa093c0..457b6e601 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -14,14 +14,14 @@ enum RenderQueueGroups RQG_Main = Ogre::RENDER_QUEUE_MAIN, + RQG_Alpha = Ogre::RENDER_QUEUE_MAIN+1, + + RQG_OcclusionQuery = Ogre::RENDER_QUEUE_6, + + RQG_UnderWater = Ogre::RENDER_QUEUE_7, + RQG_Water = Ogre::RENDER_QUEUE_7+1, - RQG_Alpha = Ogre::RENDER_QUEUE_MAIN, - - RQG_UnderWater = Ogre::RENDER_QUEUE_7+1, - - RQG_OcclusionQuery = Ogre::RENDER_QUEUE_8, - // Sky late (sun & sun flare) RQG_SkiesLate = Ogre::RENDER_QUEUE_SKIES_LATE }; @@ -54,7 +54,7 @@ enum VisibilityFlags RV_OcclusionQuery = 256, - RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water, + RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water /// \todo markers (normally hidden) }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 15660ade5..916ccb0c0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -264,7 +264,7 @@ void RenderingManager::update (float duration){ checkUnderwater(); - mWater->update(); + mWater->update(duration); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ if(store->cell->data.flags & store->cell->HasWater diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index b1d9dee12..691e7c4af 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -44,7 +44,6 @@ namespace MWRender mTerrainGlobals->setMaxPixelError(8); mTerrainGlobals->setLayerBlendMapSize(32); - mTerrainGlobals->setDefaultGlobalColourMapSize(65); //10 (default) didn't seem to be quite enough mTerrainGlobals->setSkirtSize(128); @@ -53,28 +52,6 @@ namespace MWRender //this seemed the distance where it wasn't too noticeable mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); - /* - mActiveProfile->setLightmapEnabled(false); - mActiveProfile->setLayerSpecularMappingEnabled(false); - mActiveProfile->setLayerNormalMappingEnabled(false); - mActiveProfile->setLayerParallaxMappingEnabled(false); - - bool shadows = Settings::Manager::getBool("enabled", "Shadows"); - mActiveProfile->setReceiveDynamicShadowsEnabled(shadows); - mActiveProfile->setReceiveDynamicShadowsDepth(shadows); - if (Settings::Manager::getBool("split", "Shadows")) - mActiveProfile->setReceiveDynamicShadowsPSSM(mRendering->getShadows()->getPSSMSetup()); - else - mActiveProfile->setReceiveDynamicShadowsPSSM(0); - - mActiveProfile->setShadowFar(mRendering->getShadows()->getShadowFar()); - mActiveProfile->setShadowFadeStart(mRendering->getShadows()->getFadeStart()); - - //composite maps lead to a drastic increase in loading time so are - //disabled - mActiveProfile->setCompositeMapEnabled(false); - */ - mTerrainGroup.setOrigin(Vector3(mWorldSize/2, 0, -mWorldSize/2)); @@ -181,10 +158,9 @@ namespace MWRender terrain->setVisibilityFlags(RV_Terrain); terrain->setRenderQueueGroup(RQG_Main); + // disable or enable global colour map (depends on available vertex colours) if ( land->landData->usingColours ) { - // disable or enable global colour map (depends on available vertex colours) - //mActiveProfile->setGlobalColourMapEnabled(true); TexturePtr vertex = getVertexColours(land, cellX, cellY, x*(mLandSize-1), @@ -193,17 +169,9 @@ namespace MWRender mActiveProfile->setGlobalColourMapEnabled(true); mActiveProfile->setGlobalColourMap (terrain, vertex->getName()); - - //this is a hack to get around the fact that Ogre seems to - //corrupt the global colour map leading to rendering errors - //MaterialPtr mat = terrain->getMaterial(); - /// \todo - //mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); - - - //mat = terrain->_getCompositeMapMaterial(); - //mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); } + else + mActiveProfile->setGlobalColourMapEnabled (false); } } } diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 89d895358..1a2d96a14 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -117,6 +117,10 @@ namespace MWRender shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); } + // caustics + sh::MaterialInstanceTextureUnit* caustics = p->createTextureUnit ("causticMap"); + caustics->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue("water_nm.png"))); + p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty(new sh::StringValue( Ogre::StringConverter::toString(numBlendTextures + numLayers + 2)))); @@ -149,6 +153,8 @@ namespace MWRender --freeTextureUnits; freeTextureUnits -= 3; // shadow PSSM + --freeTextureUnits; // caustics + // each layer needs 1.25 units (1xdiffusespec, 0.25xblend) return static_cast(freeTextureUnits / (1.25f)); } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index b8642a754..90967c886 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -8,11 +8,16 @@ #include #include #include +#include +#include +#include #include "sky.hpp" #include "renderingmanager.hpp" #include "compositors.hpp" +#include + using namespace Ogre; namespace MWRender @@ -22,10 +27,16 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mCamera (camera), mSceneManager (camera->getSceneManager()), mIsUnderwater(false), mVisibilityFlags(0), mReflectionTarget(0), mActive(1), mToggled(1), - mReflectionRenderActive(false), mRendering(rend) + mReflectionRenderActive(false), mRendering(rend), + mOldFarClip(0), + mWaterTimer(0.f) { mSky = rend->getSkyManager(); + sh::Factory::getInstance ().setSharedParameter ("windDir_windSpeed", sh::makeProperty(new sh::Vector3(0.5, -0.8, 0.2))); + sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(0))); + sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(1, 0.6))); + mTop = cell->water; mIsUnderwater = false; @@ -40,7 +51,6 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mWater->setCastShadows(false); mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode(); - mWaterNode->setPosition(0, mTop, 0); mReflectionCamera = mSceneManager->createCamera("ReflectionCamera"); @@ -59,21 +69,30 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mUnderwaterEffect = Settings::Manager::getBool("underwater effect", "Water"); + Ogre::Entity* underwaterDome = mSceneManager->createEntity ("underwater_dome.mesh"); + underwaterDome->setRenderQueueGroup (RQG_UnderWater); + mUnderwaterDome = mSceneManager->getRootSceneNode ()->createChildSceneNode (); + mUnderwaterDome->attachObject (underwaterDome); + mUnderwaterDome->setScale(100,100,100); + mUnderwaterDome->setVisible(false); + mSceneManager->addRenderQueueListener(this); assignTextures(); + setHeight(mTop); + // ---------------------------------------------------------------------------------------------- // ---------------------------------- reflection debug overlay ---------------------------------- // ---------------------------------------------------------------------------------------------- - /* +/* if (Settings::Manager::getBool("shader", "Water")) { OverlayManager& mgr = OverlayManager::getSingleton(); Overlay* overlay; // destroy if already exists - if (overlay = mgr.getByName("ReflectionDebugOverlay")) + if ((overlay = mgr.getByName("ReflectionDebugOverlay"))) mgr.destroy(overlay); overlay = mgr.create("ReflectionDebugOverlay"); @@ -85,18 +104,17 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false); - TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName()); - t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(mReflectionTexture->getName()); OverlayContainer* debugPanel; // destroy container if exists try { - if (debugPanel = + if ((debugPanel = static_cast( mgr.getOverlayElement("Ogre/ReflectionDebugTexPanel" - ))) + )))) mgr.destroyOverlayElement(debugPanel); } catch (Ogre::Exception&) {} @@ -110,7 +128,7 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel overlay->add2D(debugPanel); overlay->show(); } - */ +*/ } void Water::setActive(bool active) @@ -146,6 +164,7 @@ void Water::setHeight(const float height) mTop = height; mWaterPlane = Plane(Vector3::UNIT_Y, height); mWaterNode->setPosition(0, height, 0); + sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(height))); } void Water::toggle() @@ -164,13 +183,15 @@ void Water::checkUnderwater(float y) if ((mIsUnderwater && y > mTop) || !mWater->isVisible() || mCamera->getPolygonMode() != Ogre::PM_SOLID) { - mRendering->getCompositors()->setCompositorEnabled(mCompositorName, false); + //mRendering->getCompositors()->setCompositorEnabled(mCompositorName, false); // tell the shader we are not underwater + +/* Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0); if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false)) pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(0)); - +*/ mWater->setRenderQueueGroup(RQG_Water); mIsUnderwater = false; @@ -178,15 +199,16 @@ void Water::checkUnderwater(float y) if (!mIsUnderwater && y < mTop && mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID) { - if (mUnderwaterEffect) - mRendering->getCompositors()->setCompositorEnabled(mCompositorName, true); + //if (mUnderwaterEffect) + //mRendering->getCompositors()->setCompositorEnabled(mCompositorName, true); // tell the shader we are underwater +/* Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0); if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false)) pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(1)); - - mWater->setRenderQueueGroup(RQG_UnderWater); +*/ + //mWater->setRenderQueueGroup(RQG_UnderWater); mIsUnderwater = true; } @@ -211,12 +233,11 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) mReflectionCamera->setFOVy(mCamera->getFOVy()); mReflectionRenderActive = true; - /// \todo For some reason this camera is delayed for 1 frame, which causes ugly sky reflection behaviour.. - /// to circumvent this we just scale the sky up, so it's not that noticable + /// \todo the reflection render (and probably all renderingmanager-updates) lag behind 1 camera frame for some reason Vector3 pos = mCamera->getRealPosition(); pos.y = mTop*2 - pos.y; mSky->setSkyPosition(pos); - mSky->scaleSky(mCamera->getFarClipDistance() / 5000.f); + mSky->scaleSky(mCamera->getFarClipDistance() / 50.f); mReflectionCamera->enableReflection(mWaterPlane); } } @@ -242,7 +263,7 @@ void Water::createMaterial() else { mMaterial = MaterialManager::getSingleton().getByName("Water"); - mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTexture(mReflectionTexture); + sh::Factory::getInstance ().setTextureAlias ("WaterReflection", mReflectionTexture->getName()); } // these have to be set in code @@ -251,30 +272,23 @@ void Water::createMaterial() { textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds"; } - Ogre::Technique* tech; - if (mReflectionTarget == 0) - tech = mMaterial->getTechnique(0); - else - tech = mMaterial->getTechnique(1); - tech->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2); + if (mReflectionTarget == 0) + mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2); } void Water::assignTextures() { if (Settings::Manager::getBool("shader", "Water")) { + CompositorInstance* compositor = CompositorManager::getSingleton().getCompositorChain(mRendering->getViewport())->getCompositor("gbuffer"); TexturePtr colorTexture = compositor->getTextureInstance("mrt_output", 0); - TextureUnitState* tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("refractionMap"); - if (tus != 0) - tus->setTexture(colorTexture); + sh::Factory::getInstance ().setTextureAlias ("WaterRefraction", colorTexture->getName()); TexturePtr depthTexture = compositor->getTextureInstance("mrt_output", 1); - tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("depthMap"); - if (tus != 0) - tus->setTexture(depthTexture); + sh::Factory::getInstance ().setTextureAlias ("SceneDepth", depthTexture->getName()); } } @@ -288,7 +302,7 @@ void Water::updateVisible() { mWater->setVisible(mToggled && mActive); if (mReflectionTarget) - mReflectionTarget->setActive(mToggled && mActive && !mIsUnderwater); + mReflectionTarget->setActive(mToggled && mActive); } void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) @@ -296,7 +310,9 @@ void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &in // We don't want the sky to get clipped by custom near clip plane (the water plane) if (queueGroupId < 20 && mReflectionRenderActive) { + mOldFarClip = mReflectionCamera->getFarClipDistance (); mReflectionCamera->disableCustomNearClipPlane(); + mReflectionCamera->setFarClipDistance (1000000000); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } } @@ -305,13 +321,21 @@ void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invo { if (queueGroupId < 20 && mReflectionRenderActive) { - mReflectionCamera->enableCustomNearClipPlane(mWaterPlane); + mReflectionCamera->setFarClipDistance (mOldFarClip); + if (!mIsUnderwater) + mReflectionCamera->enableCustomNearClipPlane(mWaterPlane); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } } -void Water::update() +void Water::update(float dt) { + Ogre::Vector3 pos = mCamera->getDerivedPosition (); + pos.y = -mWaterPlane.d; + mUnderwaterDome->setPosition (pos); + + mWaterTimer += dt; + sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(mWaterTimer))); } void Water::applyRTT() diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 71f261f2b..ac837ca0d 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -39,11 +39,17 @@ namespace MWRender { Ogre::SceneNode *mWaterNode; Ogre::Entity *mWater; + Ogre::SceneNode* mUnderwaterDome; + bool mIsUnderwater; bool mActive; bool mToggled; int mTop; + int mOldFarClip; + + float mWaterTimer; + bool mReflectionRenderActive; Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY); @@ -83,7 +89,7 @@ namespace MWRender { void setActive(bool active); void toggle(); - void update(); + void update(float dt); void assignTextures(); diff --git a/extern/shiny b/extern/shiny index 5a9bda601..bf003238a 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 5a9bda6010413555736479ef03103f764fecb91d +Subproject commit bf003238a27d94be43724e6774d25c38b4d578c8 diff --git a/files/gbuffer/gbuffer.compositor b/files/gbuffer/gbuffer.compositor index 6ca35df87..19b890fec 100644 --- a/files/gbuffer/gbuffer.compositor +++ b/files/gbuffer/gbuffer.compositor @@ -18,7 +18,7 @@ compositor gbuffer { // Renders everything except water first_render_queue 0 - last_render_queue 70 + last_render_queue 50 } } @@ -66,7 +66,7 @@ compositor gbufferFinalizer } pass render_scene { - first_render_queue 71 + first_render_queue 51 last_render_queue 100 } } diff --git a/files/materials/caustics.h b/files/materials/caustics.h new file mode 100644 index 000000000..d6be680f0 --- /dev/null +++ b/files/materials/caustics.h @@ -0,0 +1,117 @@ +#define VISIBILITY 1500.0 // how far you can look through water + +#define BIG_WAVES_X 0.3 // strength of big waves +#define BIG_WAVES_Y 0.3 + +#define MID_WAVES_X 0.3 // strength of middle sized waves +#define MID_WAVES_Y 0.15 + +#define SMALL_WAVES_X 0.15 // strength of small waves +#define SMALL_WAVES_Y 0.1 + +#define WAVE_CHOPPYNESS 0.15 // wave choppyness +#define WAVE_SCALE 0.01 // overall wave scale + +#define ABBERATION 0.001 // chromatic abberation amount + +float3 intercept(float3 lineP, + float3 lineN, + float3 planeN, + float planeD) +{ + + float distance = (planeD - dot(planeN, lineP)) / dot(lineN, planeN); + return lineP + lineN * distance; +} + +float3 perturb1(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) +{ + float2 nCoord = float2(0.0); + bend *= WAVE_CHOPPYNESS; + nCoord = coords * (WAVE_SCALE * 0.05) + windDir * timer * (windSpeed*0.04); + float3 normal0 = 2.0 * shSample(tex, nCoord + float2(-timer*0.015,-timer*0.05)).rgb - 1.0; + nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.08)-normal0.xy*bend; + float3 normal1 = 2.0 * shSample(tex, nCoord + float2(+timer*0.020,+timer*0.015)).rgb - 1.0; + + nCoord = coords * (WAVE_SCALE * 0.25) + windDir * timer * (windSpeed*0.07)-normal1.xy*bend; + float3 normal2 = 2.0 * shSample(tex, nCoord + float2(-timer*0.04,-timer*0.03)).rgb - 1.0; + nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.09)-normal2.xy*bend; + float3 normal3 = 2.0 * shSample(tex, nCoord + float2(+timer*0.03,+timer*0.04)).rgb - 1.0; + + nCoord = coords * (WAVE_SCALE* 1.0) + windDir * timer * (windSpeed*0.4)-normal3.xy*bend; + float3 normal4 = 2.0 * shSample(tex, nCoord + float2(-timer*0.2,+timer*0.1)).rgb - 1.0; + nCoord = coords * (WAVE_SCALE * 2.0) + windDir * timer * (windSpeed*0.7)-normal4.xy*bend; + float3 normal5 = 2.0 * shSample(tex, nCoord + float2(+timer*0.1,-timer*0.06)).rgb - 1.0; + + + float3 normal = normalize(normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + + normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + + normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y); + return normal; +} + +float3 perturb(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) +{ + bend *= WAVE_CHOPPYNESS; + float3 col = float3(0.0); + float2 nCoord = float2(0.0); //normal coords + + nCoord = coords * (WAVE_SCALE * 0.025) + windDir * timer * (windSpeed*0.03); + col += shSample(tex,nCoord + float2(-timer*0.005,-timer*0.01)).rgb*0.20; + nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.05)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(+timer*0.01,+timer*0.005)).rgb*0.20; + + nCoord = coords * (WAVE_SCALE * 0.2) + windDir * timer * (windSpeed*0.1)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(-timer*0.02,-timer*0.03)).rgb*0.20; + nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.2)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(+timer*0.03,+timer*0.02)).rgb*0.15; + + nCoord = coords * (WAVE_SCALE* 0.8) + windDir * timer * (windSpeed*1.0)-(col.xy/col.zz)*bend; + col += shSample(tex, nCoord + float2(-timer*0.06,+timer*0.08)).rgb*0.15; + nCoord = coords * (WAVE_SCALE * 1.0) + windDir * timer * (windSpeed*1.3)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(+timer*0.08,-timer*0.06)).rgb*0.10; + + return col; +} + + +float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 eyePosWS, float3 worldNormal, float3 lightDirectionWS0, float waterLevel, float waterTimer, float3 windDir_windSpeed) +{ + float3 waterEyePos = intercept(worldPos.xyz, eyePosWS - worldPos, float3(0,1,0), waterLevel); + float waterDepth = shSaturate((waterEyePos.y - worldPos.y) / 50.0); + + float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,1,0), waterLevel); + + ///\ todo clean this up + float causticdepth = length(causticPos-worldPos.xyz); + causticdepth = 1.0-shSaturate(causticdepth / VISIBILITY); + causticdepth = shSaturate(causticdepth); + + // NOTE: the original shader calculated a tangent space basis here, + // but using only the world normal is cheaper and i couldn't see a visual difference + // also, if this effect gets moved to screen-space some day, it's unlikely to have tangent information + float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xzy * 2 - 1; + + //float fresnel = pow(clamp(dot(LV,causticnorm),0.0,1.0),2.0); + + float NdotL = max(dot(worldNormal.xyz, lightDirectionWS0.xyz),0.0); + + float causticR = 1.0-perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + + /// \todo sunFade + + // float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*sunFade*causticdepth; + float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth; + float causticG = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticB = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + //caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*sunFade*causticdepth; + caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*causticdepth; + + caustics *= 3; + + // shore transition + caustics = shLerp (float3(1,1,1), caustics, waterDepth); + + return caustics; +} + diff --git a/files/materials/core.h b/files/materials/core.h index 34095f93d..c4d287761 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -69,7 +69,7 @@ #define shSampler2D(name) uniform sampler2D name; @shUseSampler(name) - #define shMatrixMult(m, v) m * v + #define shMatrixMult(m, v) (m * v) // automatically recognized by ogre when the input name equals this #define shInputPosition vertex diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 96c6f8422..9b0357f01 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -61,5 +61,10 @@ material openmw_objects_base tex_address_mode clamp filtering none } + + texture_unit causticMap + { + direct_texture water_nm.png + } } } diff --git a/files/materials/objects.shader b/files/materials/objects.shader index eabdb8ca3..0c1867d66 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -9,13 +9,17 @@ #define SHADOWS LIGHTING && @shGlobalSettingBool(shadows) #if SHADOWS || SHADOWS_PSSM -#include "shadows.h" + #include "shadows.h" #endif #if FOG || MRT || SHADOWS_PSSM #define NEED_DEPTH #endif + +#define UNDERWATER LIGHTING + + #define HAS_VERTEXCOLOR @shPropertyBool(has_vertex_colour) #ifdef SH_VERTEX_SHADER @@ -89,6 +93,10 @@ // ----------------------------------- FRAGMENT ------------------------------------------ +#if UNDERWATER + #include "caustics.h" +#endif + SH_BEGIN_PROGRAM shSampler2D(diffuseMap) shInput(float2, UV) @@ -145,6 +153,20 @@ #if SHADOWS || SHADOWS_PSSM shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) #endif + +#if UNDERWATER + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) + shUniform(float, waterLevel) @shSharedParameter(waterLevel) + shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) + shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0) + + shSampler2D(causticMap) + + shUniform(float, waterTimer) @shSharedParameter(waterTimer) + + shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) +#endif + SH_START_PROGRAM { shOutputColour(0) = shSample(diffuseMap, UV); @@ -174,20 +196,42 @@ #if !SHADOWS && !SHADOWS_PSSM float shadow = 1.0; #endif + + + + float3 caustics = float3(1,1,1); +#if UNDERWATER + float4 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)); + if (worldPos.y < waterLevel) + { + float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); + caustics = getCaustics(causticMap, worldPos.xyz, cameraPos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + } +#endif + @shForeach(@shGlobalSettingString(num_lights)) - + /// \todo use the array auto params for lights, and use a real for-loop with auto param "light_count" iterations lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePositionPassthrough.xyz * lightPosObjSpace@shIterator.w); d = length(lightDir); lightDir = normalize(lightDir); -#if @shIterator == 0 && (SHADOWS || SHADOWS_PSSM) - diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow; +#if @shIterator == 0 + + #if (SHADOWS || SHADOWS_PSSM) + diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow * caustics; + + #else + diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * caustics; + + #endif + #else diffuse += materialDiffuse.xyz * lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); #endif + @shEndForeach #if HAS_VERTEXCOLOR diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 2b7091838..722108a58 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -22,6 +22,8 @@ #define NEED_DEPTH 1 #endif +#define UNDERWATER LIGHTING + #if NEED_DEPTH @shAllocatePassthrough(1, depth) @@ -122,6 +124,10 @@ // ----------------------------------- FRAGMENT ------------------------------------------ +#if UNDERWATER + #include "caustics.h" +#endif + SH_BEGIN_PROGRAM @@ -180,6 +186,20 @@ #endif +#if UNDERWATER + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) + shUniform(float, waterLevel) @shSharedParameter(waterLevel) + shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position) + shUniform(float4, lightDirectionWS0) @shAutoConstant(lightDirectionWS0, light_position, 0) + + shSampler2D(causticMap) + + shUniform(float, waterTimer) @shSharedParameter(waterTimer) + + shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) +#endif + + SH_START_PROGRAM { @@ -198,6 +218,19 @@ + float3 caustics = float3(1,1,1); +#if UNDERWATER + + float4 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)); + if (worldPos.y < waterLevel) + { + float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); + caustics = getCaustics(causticMap, worldPos.xyz, cameraPos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + } + +#endif + + // Layer calculations @shForeach(@shPropertyString(num_blendmaps)) float4 blendValues@shIterator = shSample(blendMap@shIterator, UV); @@ -272,11 +305,20 @@ lightDir = normalize(lightDir); -#if @shIterator == 0 && (SHADOWS || SHADOWS_PSSM) - diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow; +#if @shIterator == 0 + + #if (SHADOWS || SHADOWS_PSSM) + diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * shadow * caustics; + + #else + diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0) * caustics; + + #endif + #else diffuse += lightDiffuse@shIterator.xyz * (1.0 / ((lightAttenuation@shIterator.y) + (lightAttenuation@shIterator.z * d) + (lightAttenuation@shIterator.w * d * d))) * max(dot(normal, lightDir), 0); #endif + @shEndForeach shOutputColour(0).xyz *= (lightAmbient.xyz + diffuse); diff --git a/files/materials/water.mat b/files/materials/water.mat index c701e5ffe..d74305c73 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -1,30 +1,33 @@ -// note: the fixed function water is created manually, not here - -material openmw_water +material Water { pass { vertex_program water_vertex fragment_program water_fragment + cull_hardware none + texture_unit reflectionMap { texture_alias WaterReflection + tex_address_mode clamp } texture_unit refractionMap { texture_alias WaterRefraction + tex_address_mode clamp } texture_unit depthMap { - + texture_alias SceneDepth + tex_address_mode clamp } texture_unit normalMap { - texture + direct_texture water_nm.png } } } diff --git a/files/materials/water.shader b/files/materials/water.shader index e69de29bb..4945770a5 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -0,0 +1,228 @@ +#include "core.h" + +// Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html ) + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shInput(float2, uv0) + shOutput(float2, UV) + + shOutput(float3, screenCoordsPassthrough) + shOutput(float4, position) + shOutput(float, depthPassthrough) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + + + #if !SH_GLSL + float4x4 scalemat = float4x4( 0.5, 0, 0, 0.5, + 0, -0.5, 0, 0.5, + 0, 0, 0.5, 0.5, + 0, 0, 0, 1 ); + #else + mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, -0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + #endif + + float4 texcoordProj = shMatrixMult(scalemat, shOutputPosition); + screenCoordsPassthrough = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w); + + position = shInputPosition; + + depthPassthrough = shOutputPosition.z; + } + +#else + + // tweakables ---------------------------------------------------- + + #define BIG_WAVES_X 0.3 // strength of big waves + #define BIG_WAVES_Y 0.3 + + #define MID_WAVES_X 0.3 // strength of middle sized waves + #define MID_WAVES_Y 0.15 + + #define SMALL_WAVES_X 0.15 // strength of small waves + #define SMALL_WAVES_Y 0.1 + + #define WAVE_CHOPPYNESS 0.15 // wave choppyness + #define WAVE_SCALE 150 // overall wave scale + + #define ABBERATION 0.001 // chromatic abberation amount + #define BUMP 1.5 // overall water surface bumpiness + #define REFL_BUMP 0.11 // reflection distortion amount + #define REFR_BUMP 0.08 // refraction distortion amount + + #define SCATTER_AMOUNT 3.0 // amount of sunlight scattering + #define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering + + #define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction + + #define SPEC_HARDNESS 256 // specular highlights hardness + + + // --------------------------------------------------------------- + + + + float fresnel_dielectric(float3 Incoming, float3 Normal, float eta) + { + /* compute fresnel reflectance without explicitly computing + the refracted direction */ + float c = abs(dot(Incoming, Normal)); + float g = eta * eta - 1.0 + c * c; + float result; + + if(g > 0.0) { + g = sqrt(g); + float A =(g - c)/(g + c); + float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0); + result = 0.5 * A * A *(1.0 + B * B); + } + else + result = 1.0; /* TIR (no refracted component) */ + + return result; + } + + SH_BEGIN_PROGRAM + shInput(float2, UV) + shInput(float3, screenCoordsPassthrough) + shInput(float4, position) + shInput(float, depthPassthrough) + + shUniform(float, far) @shAutoConstant(far, far_clip_distance) + + shSampler2D(reflectionMap) + shSampler2D(refractionMap) + shSampler2D(depthMap) + shSampler2D(normalMap) + + shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) + #define WIND_SPEED windDir_windSpeed.z + #define WIND_DIR windDir_windSpeed.xy + + shUniform(float, waterTimer) @shSharedParameter(waterTimer) + shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight) + + shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0) + shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0) + + + + shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping) + + + shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) + shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) + + shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space) + + + SH_START_PROGRAM + { + + float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; + screenCoords.y = (1-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y; + + float depth = shSample(depthMap, screenCoords).x * far - depthPassthrough; + float shoreFade = shSaturate(depth / 50.0); + + float2 nCoord = float2(0.0); + + nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04); + float3 normal0 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0; + nCoord = UV * (WAVE_SCALE * 0.1) + WIND_DIR * waterTimer * (WIND_SPEED*0.08)-(normal0.xy/normal0.zz)*WAVE_CHOPPYNESS; + float3 normal1 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.020,+waterTimer*0.015)).rgb - 1.0; + + nCoord = UV * (WAVE_SCALE * 0.25) + WIND_DIR * waterTimer * (WIND_SPEED*0.07)-(normal1.xy/normal1.zz)*WAVE_CHOPPYNESS; + float3 normal2 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.04,-waterTimer*0.03)).rgb - 1.0; + nCoord = UV * (WAVE_SCALE * 0.5) + WIND_DIR * waterTimer * (WIND_SPEED*0.09)-(normal2.xy/normal2.z)*WAVE_CHOPPYNESS; + float3 normal3 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.03,+waterTimer*0.04)).rgb - 1.0; + + nCoord = UV * (WAVE_SCALE* 1.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.4)-(normal3.xy/normal3.zz)*WAVE_CHOPPYNESS; + float3 normal4 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.02,+waterTimer*0.1)).rgb - 1.0; + nCoord = UV * (WAVE_SCALE * 2.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.7)-(normal4.xy/normal4.zz)*WAVE_CHOPPYNESS; + float3 normal5 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.1,-waterTimer*0.06)).rgb - 1.0; + + + + float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + + normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + + normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y).xzy; + + normal = normalize(float3(normal.x * BUMP, normal.y, normal.z * BUMP)); + + // normal for sunlight scattering + float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 + + normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 + + normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xzy; + lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y, lNormal.z * BUMP)); + + + float3 lVec = normalize(sunPosition.xyz); + float3 vVec = normalize(position.xyz - cameraPos.xyz); + + + float isUnderwater = (cameraPos.y > 0) ? 0.0 : 1.0; + + // sunlight scattering + float3 pNormal = float3(0,1,0); + vec3 lR = reflect(lVec, lNormal); + vec3 llR = reflect(lVec, pNormal); + + float s = shSaturate(dot(lR, vVec)*2.0-1.2); + float lightScatter = shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); + float3 scatterColour = shLerp(vec3(SCATTER_COLOUR)*vec3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + + // fresnel + float ior = (cameraPos.y>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air + float fresnel = fresnel_dielectric(-vVec, normal, ior); + + fresnel = shSaturate(fresnel); + + // reflection + float3 reflection = shSample(reflectionMap, screenCoords+(normal.xz*REFL_BUMP)).rgb; + + // refraction + float3 R = reflect(vVec, normal); + + float3 refraction = float3(0,0,0); + refraction.r = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0).r; + refraction.g = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION)).g; + refraction.b = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION*2.0)).b; + + // brighten up the refraction underwater + refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; + + float waterSunGradient = dot(-vVec, -lVec); + waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); + float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; + + float waterGradient = dot(-vVec, float3(0.0,-1.0,0.0)); + waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); + float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; + float3 waterext = float3(0.6, 0.9, 1.0);//water extinction + watercolour = mix(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + + // specular + float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS); + + shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz; + + // smooth transition to shore (above water only) + shOutputColour(0).xyz = shLerp(shOutputColour(0).xyz, refraction, (1-shoreFade) * (1-isUnderwater)); + + // fog + float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); + } + +#endif diff --git a/files/materials/water.shaderset b/files/materials/water.shaderset index e69de29bb..754ac8e49 100644 --- a/files/materials/water.shaderset +++ b/files/materials/water.shaderset @@ -0,0 +1,15 @@ +shader_set water_vertex +{ + source water.shader + type vertex + profiles_cg vs_2_0 vp40 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set water_fragment +{ + source water.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/water/underwater_dome.mesh b/files/water/underwater_dome.mesh new file mode 100644 index 0000000000000000000000000000000000000000..64ca569c22a04110e7913505c427f0087cef51c4 GIT binary patch literal 22585 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 literal 0 HcmV?d00001 diff --git a/files/water/water.compositor b/files/water/water.compositor index 8d9c3cb39..94b778773 100644 --- a/files/water/water.compositor +++ b/files/water/water.compositor @@ -39,7 +39,6 @@ compositor Underwater { material Water/Compositor input 0 rt0 - input 3 scene 1 } } } diff --git a/files/water/water.material b/files/water/water.material index d1f7fcf49..7376d10cc 100644 --- a/files/water/water.material +++ b/files/water/water.material @@ -44,7 +44,7 @@ fragment_program Water_FP cg profiles ps_2_x arbfp1 } -material Water +material __Water { technique { diff --git a/files/water/water_nm.png b/files/water/water_nm.png new file mode 100644 index 0000000000000000000000000000000000000000..361431a0efc35882b56bab8ea407d245f27c879d GIT binary patch literal 24405 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 literal 0 HcmV?d00001 From a86ed46ec495329b5fafc08e5893bdaa43afac2b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jul 2012 11:00:26 -0700 Subject: [PATCH 210/688] Use the race ID specified in the ref base's record instead of the race record The latter is localized and doesn't match with international versions. --- apps/openmw/mwrender/npcanimation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 9f28dbf13..a693a6b46 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -74,7 +74,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere isFemale = !!(ref->base->flags&ESM::NPC::Female); isBeast = !!(race->data.flags&ESM::Race::Beast); - bodyRaceID = "b_n_"+race->name; + bodyRaceID = "b_n_"+ref->base->race; std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); /*std::cout << "Race: " << ref->base->race ; From 9d7470e14cb2e57c6300ce9e633ed7db635e0b7c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jul 2012 20:18:34 +0200 Subject: [PATCH 211/688] temp commit --- extern/shiny | 2 +- files/materials/objects.shader | 46 ++++++++++--- files/materials/terrain.shader | 10 +-- files/materials/underwater.h | 116 +++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 files/materials/underwater.h diff --git a/extern/shiny b/extern/shiny index bf003238a..4853ea735 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit bf003238a27d94be43724e6774d25c38b4d578c8 +Subproject commit 4853ea7351edced75a48662da5f3e857961e0b47 diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 0c1867d66..fdf2b3676 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -42,7 +42,7 @@ #if HAS_VERTEXCOLOR shColourInput(float4) - shOutput(float4, colorPassthrough) + shOutput(float4, colourPassthrough) #endif #if SHADOWS @@ -75,7 +75,7 @@ #endif #if HAS_VERTEXCOLOR - colorPassthrough = colour; + colourPassthrough = colour; #endif #if SHADOWS @@ -128,12 +128,12 @@ #endif #if FOG - shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) + shUniform(float3, fogColour) @shAutoConstant(fogColour, fog_colour) shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) #endif #ifdef HAS_VERTEXCOLOR - shInput(float4, colorPassthrough) + shInput(float4, colourPassthrough) #endif #if SHADOWS @@ -201,11 +201,13 @@ float3 caustics = float3(1,1,1); #if UNDERWATER - float4 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)); + float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)).xyz; + float3 waterEyePos = float3(1,1,1); if (worldPos.y < waterLevel) { float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - caustics = getCaustics(causticMap, worldPos.xyz, cameraPos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); + caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); } #endif @@ -235,7 +237,7 @@ @shEndForeach #if HAS_VERTEXCOLOR - ambient *= colorPassthrough.xyz; + ambient *= colourPassthrough.xyz; #endif shOutputColour(0).xyz *= (ambient + diffuse + materialEmissive.xyz); @@ -243,16 +245,40 @@ #if HAS_VERTEXCOLOR && !LIGHTING - shOutputColour(0).xyz *= colorPassthrough.xyz; + shOutputColour(0).xyz *= colourPassthrough.xyz; #endif #if FOG float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); + + #if UNDERWATER + // regular fog only if fragment is above water + if (worldPos.y > waterLevel) + #endif + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); #endif - // prevent negative color output (for example with negative lights) + // prevent negative colour output (for example with negative lights) shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); + +#if UNDERWATER + float fogAmount = (worldPos.y > waterLevel) + ? shSaturate(length(waterEyePos-worldPos) / VISIBILITY) + : shSaturate(length(cameraPos.xyz-worldPos)/ VISIBILITY); + + float3 eyeVec = normalize(cameraPos.xyz-worldPos); + float waterSunGradient = dot(eyeVec, -normalize(lightDirectionWS0.xyz)); + waterSunGradient = clamp(pow(waterSunGradient*0.7+0.3,2.0),0.0,1.0); + + float waterGradient = dot(eyeVec, vec3(0.0,-1.0,0.0)); + waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); + + float3 waterSunColour = vec3(0.0,1.0,0.85)*waterSunGradient; + waterSunColour = (cameraPos.z < waterLevel) ? waterSunColour*0.5:waterSunColour*0.25;//below or above water? + + float3 waterColour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; + shOutputColour(0).xyz = waterColour; +#endif #if MRT shOutputColour(1) = float4(depthPassthrough / far,1,1,1); diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 722108a58..77ba01494 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -147,7 +147,7 @@ @shEndForeach #if FOG - shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) + shUniform(float3, fogColour) @shAutoConstant(fogColour, fog_colour) shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) #endif @@ -221,11 +221,13 @@ float3 caustics = float3(1,1,1); #if UNDERWATER - float4 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)); + float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)).xyz; + float3 waterEyePos = float3(1,1,1); if (worldPos.y < waterLevel) { float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - caustics = getCaustics(causticMap, worldPos.xyz, cameraPos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + waterEyePos = intercept(worldPos, eyePosWS - worldPos, float3(0,1,0), waterLevel); + caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); } #endif @@ -329,7 +331,7 @@ #if FOG float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); #endif diff --git a/files/materials/underwater.h b/files/materials/underwater.h new file mode 100644 index 000000000..fa744ac39 --- /dev/null +++ b/files/materials/underwater.h @@ -0,0 +1,116 @@ +#define VISIBILITY 1500.0 // how far you can look through water + +#define BIG_WAVES_X 0.3 // strength of big waves +#define BIG_WAVES_Y 0.3 + +#define MID_WAVES_X 0.3 // strength of middle sized waves +#define MID_WAVES_Y 0.15 + +#define SMALL_WAVES_X 0.15 // strength of small waves +#define SMALL_WAVES_Y 0.1 + +#define WAVE_CHOPPYNESS 0.15 // wave choppyness +#define WAVE_SCALE 0.01 // overall wave scale + +#define ABBERATION 0.001 // chromatic abberation amount + +float3 intercept(float3 lineP, + float3 lineN, + float3 planeN, + float planeD) +{ + + float distance = (planeD - dot(planeN, lineP)) / dot(lineN, planeN); + return lineP + lineN * distance; +} + +float3 perturb1(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) +{ + float2 nCoord = float2(0.0); + bend *= WAVE_CHOPPYNESS; + nCoord = coords * (WAVE_SCALE * 0.05) + windDir * timer * (windSpeed*0.04); + float3 normal0 = 2.0 * shSample(tex, nCoord + float2(-timer*0.015,-timer*0.05)).rgb - 1.0; + nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.08)-normal0.xy*bend; + float3 normal1 = 2.0 * shSample(tex, nCoord + float2(+timer*0.020,+timer*0.015)).rgb - 1.0; + + nCoord = coords * (WAVE_SCALE * 0.25) + windDir * timer * (windSpeed*0.07)-normal1.xy*bend; + float3 normal2 = 2.0 * shSample(tex, nCoord + float2(-timer*0.04,-timer*0.03)).rgb - 1.0; + nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.09)-normal2.xy*bend; + float3 normal3 = 2.0 * shSample(tex, nCoord + float2(+timer*0.03,+timer*0.04)).rgb - 1.0; + + nCoord = coords * (WAVE_SCALE* 1.0) + windDir * timer * (windSpeed*0.4)-normal3.xy*bend; + float3 normal4 = 2.0 * shSample(tex, nCoord + float2(-timer*0.2,+timer*0.1)).rgb - 1.0; + nCoord = coords * (WAVE_SCALE * 2.0) + windDir * timer * (windSpeed*0.7)-normal4.xy*bend; + float3 normal5 = 2.0 * shSample(tex, nCoord + float2(+timer*0.1,-timer*0.06)).rgb - 1.0; + + + float3 normal = normalize(normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + + normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + + normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y); + return normal; +} + +float3 perturb(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) +{ + bend *= WAVE_CHOPPYNESS; + float3 col = float3(0.0); + float2 nCoord = float2(0.0); //normal coords + + nCoord = coords * (WAVE_SCALE * 0.025) + windDir * timer * (windSpeed*0.03); + col += shSample(tex,nCoord + float2(-timer*0.005,-timer*0.01)).rgb*0.20; + nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.05)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(+timer*0.01,+timer*0.005)).rgb*0.20; + + nCoord = coords * (WAVE_SCALE * 0.2) + windDir * timer * (windSpeed*0.1)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(-timer*0.02,-timer*0.03)).rgb*0.20; + nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.2)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(+timer*0.03,+timer*0.02)).rgb*0.15; + + nCoord = coords * (WAVE_SCALE* 0.8) + windDir * timer * (windSpeed*1.0)-(col.xy/col.zz)*bend; + col += shSample(tex, nCoord + float2(-timer*0.06,+timer*0.08)).rgb*0.15; + nCoord = coords * (WAVE_SCALE * 1.0) + windDir * timer * (windSpeed*1.3)-(col.xy/col.zz)*bend; + col += shSample(tex,nCoord + float2(+timer*0.08,-timer*0.06)).rgb*0.10; + + return col; +} + + +float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 waterEyePos, float3 worldNormal, float3 lightDirectionWS0, float waterLevel, float waterTimer, float3 windDir_windSpeed) +{ + float waterDepth = shSaturate((waterEyePos.y - worldPos.y) / 50.0); + + float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,1,0), waterLevel); + + ///\ todo clean this up + float causticdepth = length(causticPos-worldPos.xyz); + causticdepth = 1.0-shSaturate(causticdepth / VISIBILITY); + causticdepth = shSaturate(causticdepth); + + // NOTE: the original shader calculated a tangent space basis here, + // but using only the world normal is cheaper and i couldn't see a visual difference + // also, if this effect gets moved to screen-space some day, it's unlikely to have tangent information + float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xzy * 2 - 1; + + //float fresnel = pow(clamp(dot(LV,causticnorm),0.0,1.0),2.0); + + float NdotL = max(dot(worldNormal.xyz, lightDirectionWS0.xyz),0.0); + + float causticR = 1.0-perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + + /// \todo sunFade + + // float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*sunFade*causticdepth; + float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth; + float causticG = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticB = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + //caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*sunFade*causticdepth; + caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*causticdepth; + + caustics *= 3; + + // shore transition + caustics = shLerp (float3(1,1,1), caustics, waterDepth); + + return caustics; +} + From e7602199530baab8c74664af037ff71564b24733 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jul 2012 11:36:36 -0700 Subject: [PATCH 212/688] Use a unique loader for each skeleton resource --- components/nifogre/ogre_nif_loader.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 639ebaa12..66b711737 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -130,7 +130,7 @@ public: }; -struct NIFSkeletonLoader : public Ogre::ManualResourceLoader { +class NIFSkeletonLoader : public Ogre::ManualResourceLoader { static void warn(const std::string &msg) { @@ -171,6 +171,11 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent= } } + +typedef std::map LoaderMap; +static LoaderMap sLoaders; + +public: void loadResource(Ogre::Resource *resource) { Ogre::Skeleton *skel = dynamic_cast(resource); @@ -181,7 +186,7 @@ void loadResource(Ogre::Resource *resource) buildBones(skel, node); } -static bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node) +bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node) { if(node->boneTrafo != NULL) { @@ -190,8 +195,8 @@ static bool createSkeleton(const std::string &name, const std::string &group, Ni Ogre::SkeletonPtr skel = skelMgr.getByName(name); if(skel.isNull()) { - static NIFSkeletonLoader loader; - skel = skelMgr.create(name, group, true, &loader); + NIFSkeletonLoader *loader = &sLoaders[name]; + skel = skelMgr.create(name, group, true, loader); } return true; @@ -214,6 +219,7 @@ static bool createSkeleton(const std::string &name, const std::string &group, Ni } }; +NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; // Conversion of blend / test mode from NIF -> OGRE. @@ -875,7 +881,8 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: return meshes; } - bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node); + NIFSkeletonLoader skelldr; + bool hasSkel = skelldr.createSkeleton(name, group, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); From f6c837468f1c250d75f2dba1a2fc297c518899fc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jul 2012 11:44:08 -0700 Subject: [PATCH 213/688] Load the proper NIF skeleton --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 66b711737..8795efbdf 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -882,7 +882,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: } NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(name, group, node); + bool hasSkel = skelldr.createSkeleton(skelName, group, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); From e077814a8c4563afdf92f21788d4a913c1bba319 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jul 2012 22:23:07 +0200 Subject: [PATCH 214/688] water --- apps/openmw/main.cpp | 6 +- apps/openmw/mwgui/settingswindow.cpp | 2 +- apps/openmw/mwrender/renderconst.hpp | 6 +- apps/openmw/mwrender/renderingmanager.cpp | 5 - apps/openmw/mwrender/sky.cpp | 15 ++- apps/openmw/mwrender/sky.hpp | 1 + apps/openmw/mwrender/water.cpp | 55 +++++----- apps/openmw/mwrender/water.hpp | 3 + files/gbuffer/gbuffer.cg | 18 ---- files/gbuffer/gbuffer.compositor | 6 +- files/gbuffer/gbuffer.material | 63 ----------- files/materials/caustics.h | 117 --------------------- files/materials/objects.mat | 3 - files/materials/objects.shader | 31 ++++-- files/materials/quad.mat | 22 ++++ files/materials/quad.shader | 25 +++++ files/materials/quad.shaderset | 15 +++ files/materials/terrain.shader | 42 +++++++- files/materials/underwater.h | 2 + files/materials/water.mat | 8 ++ files/materials/water.shader | 45 +++++--- files/water/WaterNormal2.tga | Bin 196652 -> 0 bytes files/water/caustic_0.png | Bin 35147 -> 0 bytes files/water/perlinvolume.dds | Bin 2097280 -> 0 bytes files/water/underwater.cg | 61 ----------- files/water/water.cg | 121 ---------------------- files/water/water.compositor | 45 -------- 27 files changed, 213 insertions(+), 504 deletions(-) delete mode 100644 files/gbuffer/gbuffer.cg delete mode 100644 files/gbuffer/gbuffer.material delete mode 100644 files/materials/caustics.h create mode 100644 files/materials/quad.mat create mode 100644 files/materials/quad.shader create mode 100644 files/materials/quad.shaderset delete mode 100644 files/water/WaterNormal2.tga delete mode 100644 files/water/caustic_0.png delete mode 100644 files/water/perlinvolume.dds delete mode 100644 files/water/underwater.cg delete mode 100644 files/water/water.cg delete mode 100644 files/water/water.compositor diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 86584cc22..993ec6623 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -261,7 +261,7 @@ int main(int argc, char**argv) boost::filesystem::current_path(bundlePath); #endif - //try + try { Files::ConfigurationManager cfgMgr; OMW::Engine engine(cfgMgr); @@ -271,11 +271,11 @@ int main(int argc, char**argv) engine.go(); } } - /*catch (std::exception &e) + catch (std::exception &e) { std::cout << "\nERROR: " << e.what() << std::endl; return 1; - }*/ + } return 0; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 3ef89d422..96cdef4cc 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -209,7 +209,7 @@ namespace MWGui mVoiceVolumeSlider->setScrollPosition(Settings::Manager::getFloat("voice volume", "Sound") * (mVoiceVolumeSlider->getScrollRange()-1)); mWaterShaderButton->setCaptionWithReplacing(Settings::Manager::getBool("shader", "Water") ? "#{sOn}" : "#{sOff}"); - mReflectObjectsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect objects", "Water") ? "#{sOn}" : "#{sOff}"); + mReflectObjectsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect statics", "Water") ? "#{sOn}" : "#{sOff}"); mReflectActorsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect actors", "Water") ? "#{sOn}" : "#{sOff}"); mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 457b6e601..9f57833bb 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -18,7 +18,7 @@ enum RenderQueueGroups RQG_OcclusionQuery = Ogre::RENDER_QUEUE_6, - RQG_UnderWater = Ogre::RENDER_QUEUE_7, + RQG_UnderWater = Ogre::RENDER_QUEUE_4, RQG_Water = Ogre::RENDER_QUEUE_7+1, @@ -49,8 +49,8 @@ enum VisibilityFlags RV_Sky = 64, - // Sun glare (not visible in reflection) - RV_Glare = 128, + // not visible in reflection + RV_NoReflection = 128, RV_OcclusionQuery = 256, diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 916ccb0c0..e1ff91a24 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -725,14 +725,9 @@ void RenderingManager::applyCompositors() { mCompositors->addCompositor("gbuffer", 0); mCompositors->setCompositorEnabled("gbuffer", true); - mCompositors->addCompositor("Underwater", 1); mCompositors->addCompositor("gbufferFinalizer", 2); mCompositors->setCompositorEnabled("gbufferFinalizer", true); } - else - { - mCompositors->addCompositor("UnderwaterNoMRT", 0); - } if (mWater) mWater->assignTextures(); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index f84c12b4e..25928cf3f 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -307,7 +307,7 @@ void SkyManager::create() mSun->setRenderQueue(RQG_SkiesEarly+4); mSunGlare = new BillboardObject("textures\\tx_sun_flash_grey_05.dds", 3, Vector3(0.4, 0.4, 0.4), mRootNode, "openmw_sun"); mSunGlare->setRenderQueue(RQG_SkiesLate); - mSunGlare->setVisibilityFlags(RV_Glare); + mSunGlare->setVisibilityFlags(RV_NoReflection); // Stars MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); @@ -389,7 +389,6 @@ void SkyManager::update(float duration) sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", sh::makeProperty(new sh::FloatValue(mCloudAnimationTimer))); - /// \todo improve this mMasser->setPhase( static_cast( (int) ((mDay % 32)/4.f)) ); mSecunda->setPhase ( static_cast( (int) ((mDay % 32)/4.f)) ); @@ -559,6 +558,10 @@ void SkyManager::setSunDirection(const Vector3& direction) if (!mCreated) return; mSun->setPosition(direction); mSunGlare->setPosition(direction); + + float height = direction.z; + float fade = ( height > 0.5) ? 1.0 : height * 2; + sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(fade, height))); } void SkyManager::setMasserDirection(const Vector3& direction) @@ -607,6 +610,7 @@ void SkyManager::setLightningStrength(const float factor) void SkyManager::setLightningDirection(const Ogre::Vector3& dir) { + if (!mCreated) return; mLightning->setDirection (dir); } @@ -653,3 +657,10 @@ void SkyManager::scaleSky(float scale) { mRootNode->setScale(scale, scale, scale); } + +void SkyManager::setGlareEnabled (bool enabled) +{ + if (!mCreated) + return; + mSunGlare->setVisible (mSunEnabled && enabled); +} diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 4a0a7b36b..d785c6eb6 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -164,6 +164,7 @@ namespace MWRender void setLightningDirection(const Ogre::Vector3& dir); void setGlare(const float glare); + void setGlareEnabled(bool enabled); Ogre::Vector3 getRealSunPos(); void setSkyPosition(const Ogre::Vector3& position); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 90967c886..bdf61e96d 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -28,7 +28,7 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mIsUnderwater(false), mVisibilityFlags(0), mReflectionTarget(0), mActive(1), mToggled(1), mReflectionRenderActive(false), mRendering(rend), - mOldFarClip(0), + mOldFarClip(0), mOldFarClip2(0), mWaterTimer(0.f) { mSky = rend->getSkyManager(); @@ -73,8 +73,9 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel underwaterDome->setRenderQueueGroup (RQG_UnderWater); mUnderwaterDome = mSceneManager->getRootSceneNode ()->createChildSceneNode (); mUnderwaterDome->attachObject (underwaterDome); - mUnderwaterDome->setScale(100,100,100); + mUnderwaterDome->setScale(10000,10000,10000); mUnderwaterDome->setVisible(false); + underwaterDome->setMaterialName("Underwater_Dome"); mSceneManager->addRenderQueueListener(this); @@ -162,7 +163,12 @@ void Water::changeCell(const ESM::Cell* cell) void Water::setHeight(const float height) { mTop = height; + mWaterPlane = Plane(Vector3::UNIT_Y, height); + + // small error due to reflection texture size & reflection distortion + mErrorPlane = Plane(Vector3::UNIT_Y, height - 5); + mWaterNode->setPosition(0, height, 0); sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(height))); } @@ -177,39 +183,16 @@ void Water::checkUnderwater(float y) { if (!mActive) { - mRendering->getCompositors()->setCompositorEnabled(mCompositorName, false); return; } if ((mIsUnderwater && y > mTop) || !mWater->isVisible() || mCamera->getPolygonMode() != Ogre::PM_SOLID) { - //mRendering->getCompositors()->setCompositorEnabled(mCompositorName, false); - - // tell the shader we are not underwater - -/* - Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0); - if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false)) - pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(0)); -*/ - mWater->setRenderQueueGroup(RQG_Water); - mIsUnderwater = false; } if (!mIsUnderwater && y < mTop && mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID) { - //if (mUnderwaterEffect) - //mRendering->getCompositors()->setCompositorEnabled(mCompositorName, true); - - // tell the shader we are underwater -/* - Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0); - if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false)) - pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(1)); -*/ - //mWater->setRenderQueueGroup(RQG_UnderWater); - mIsUnderwater = true; } @@ -308,24 +291,36 @@ void Water::updateVisible() void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) { // We don't want the sky to get clipped by custom near clip plane (the water plane) - if (queueGroupId < 20 && mReflectionRenderActive) + if (((queueGroupId < 20) || queueGroupId == RQG_UnderWater) && mReflectionRenderActive) { mOldFarClip = mReflectionCamera->getFarClipDistance (); mReflectionCamera->disableCustomNearClipPlane(); mReflectionCamera->setFarClipDistance (1000000000); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } + else if (queueGroupId == RQG_UnderWater) + {/* + mOldFarClip2 = mCamera->getFarClipDistance (); + mCamera->setFarClipDistance (1000000000); + Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); + */} } void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) { - if (queueGroupId < 20 && mReflectionRenderActive) + if (((queueGroupId < 20) || queueGroupId == RQG_UnderWater) && mReflectionRenderActive) { mReflectionCamera->setFarClipDistance (mOldFarClip); if (!mIsUnderwater) - mReflectionCamera->enableCustomNearClipPlane(mWaterPlane); + mReflectionCamera->enableCustomNearClipPlane(mErrorPlane); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } + if (queueGroupId == RQG_UnderWater) + { + /* + mCamera->setFarClipDistance (mOldFarClip2); + Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); + */} } void Water::update(float dt) @@ -336,6 +331,8 @@ void Water::update(float dt) mWaterTimer += dt; sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(mWaterTimer))); + + mRendering->getSkyManager ()->setGlareEnabled (!mIsUnderwater); } void Water::applyRTT() @@ -366,8 +363,6 @@ void Water::applyRTT() mReflectionTarget = rtt; } - - mCompositorName = RenderingManager::useMRT() ? "Underwater" : "UnderwaterNoMRT"; } void Water::applyVisibilityMask() diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index ac837ca0d..6d0001119 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -36,6 +36,8 @@ namespace MWRender { Ogre::SceneManager *mSceneManager; Ogre::Plane mWaterPlane; + Ogre::Plane mErrorPlane; + Ogre::SceneNode *mWaterNode; Ogre::Entity *mWater; @@ -47,6 +49,7 @@ namespace MWRender { int mTop; int mOldFarClip; + int mOldFarClip2; float mWaterTimer; diff --git a/files/gbuffer/gbuffer.cg b/files/gbuffer/gbuffer.cg deleted file mode 100644 index c7f2fe678..000000000 --- a/files/gbuffer/gbuffer.cg +++ /dev/null @@ -1,18 +0,0 @@ -void RenderScene_vs(in float4 position : POSITION - ,in float2 uv :TEXCOORD0 - ,uniform float4x4 wvp - ,out float4 oPosition : POSITION - ,out float2 oUV :TEXCOORD0) -{ - oPosition = mul(wvp, position); - oUV = uv; -} - -void RenderScene_ps(in float4 position : POSITION - ,in float2 uv :TEXCOORD0 - ,uniform sampler2D tex1 : TEXUNIT0 - ,out float4 oColor : COLOR) -{ - float4 scene =tex2D(tex1, uv); - oColor= scene; -} diff --git a/files/gbuffer/gbuffer.compositor b/files/gbuffer/gbuffer.compositor index 19b890fec..b3d80e9d5 100644 --- a/files/gbuffer/gbuffer.compositor +++ b/files/gbuffer/gbuffer.compositor @@ -29,7 +29,7 @@ compositor gbuffer pass render_quad { - material RenderScene + material quad input 0 mrt_output 0 } } @@ -61,7 +61,7 @@ compositor gbufferFinalizer } pass render_quad { - material RenderSceneNoDepth + material quad_noDepthWrite input 0 previousscene } pass render_scene @@ -78,7 +78,7 @@ compositor gbufferFinalizer } pass render_quad { - material RenderSceneNoDepth + material quad_noDepthWrite input 0 no_mrt_output } } diff --git a/files/gbuffer/gbuffer.material b/files/gbuffer/gbuffer.material deleted file mode 100644 index faa8dd498..000000000 --- a/files/gbuffer/gbuffer.material +++ /dev/null @@ -1,63 +0,0 @@ -vertex_program RenderGBuffer_vs cg -{ - source gbuffer.cg - profiles vs_4_0 vs_1_1 arbvp1 - entry_point RenderScene_vs - default_params - { - param_named_auto wvp worldviewproj_matrix - } -} -fragment_program RenderGBuffer_ps cg -{ - source gbuffer.cg - entry_point RenderScene_ps - profiles ps_4_0 ps_2_x arbfp1 - default_params - { - } -} -material RenderScene -{ - technique - { - pass - { - vertex_program_ref RenderGBuffer_vs - { - } - - fragment_program_ref RenderGBuffer_ps - { - } - - texture_unit tex1 - { - //scenebuffer - } - } - } -} - -material RenderSceneNoDepth -{ - technique - { - pass - { - depth_write off - vertex_program_ref RenderGBuffer_vs - { - } - - fragment_program_ref RenderGBuffer_ps - { - } - - texture_unit tex1 - { - //scenebuffer - } - } - } -} diff --git a/files/materials/caustics.h b/files/materials/caustics.h deleted file mode 100644 index d6be680f0..000000000 --- a/files/materials/caustics.h +++ /dev/null @@ -1,117 +0,0 @@ -#define VISIBILITY 1500.0 // how far you can look through water - -#define BIG_WAVES_X 0.3 // strength of big waves -#define BIG_WAVES_Y 0.3 - -#define MID_WAVES_X 0.3 // strength of middle sized waves -#define MID_WAVES_Y 0.15 - -#define SMALL_WAVES_X 0.15 // strength of small waves -#define SMALL_WAVES_Y 0.1 - -#define WAVE_CHOPPYNESS 0.15 // wave choppyness -#define WAVE_SCALE 0.01 // overall wave scale - -#define ABBERATION 0.001 // chromatic abberation amount - -float3 intercept(float3 lineP, - float3 lineN, - float3 planeN, - float planeD) -{ - - float distance = (planeD - dot(planeN, lineP)) / dot(lineN, planeN); - return lineP + lineN * distance; -} - -float3 perturb1(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) -{ - float2 nCoord = float2(0.0); - bend *= WAVE_CHOPPYNESS; - nCoord = coords * (WAVE_SCALE * 0.05) + windDir * timer * (windSpeed*0.04); - float3 normal0 = 2.0 * shSample(tex, nCoord + float2(-timer*0.015,-timer*0.05)).rgb - 1.0; - nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.08)-normal0.xy*bend; - float3 normal1 = 2.0 * shSample(tex, nCoord + float2(+timer*0.020,+timer*0.015)).rgb - 1.0; - - nCoord = coords * (WAVE_SCALE * 0.25) + windDir * timer * (windSpeed*0.07)-normal1.xy*bend; - float3 normal2 = 2.0 * shSample(tex, nCoord + float2(-timer*0.04,-timer*0.03)).rgb - 1.0; - nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.09)-normal2.xy*bend; - float3 normal3 = 2.0 * shSample(tex, nCoord + float2(+timer*0.03,+timer*0.04)).rgb - 1.0; - - nCoord = coords * (WAVE_SCALE* 1.0) + windDir * timer * (windSpeed*0.4)-normal3.xy*bend; - float3 normal4 = 2.0 * shSample(tex, nCoord + float2(-timer*0.2,+timer*0.1)).rgb - 1.0; - nCoord = coords * (WAVE_SCALE * 2.0) + windDir * timer * (windSpeed*0.7)-normal4.xy*bend; - float3 normal5 = 2.0 * shSample(tex, nCoord + float2(+timer*0.1,-timer*0.06)).rgb - 1.0; - - - float3 normal = normalize(normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + - normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + - normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y); - return normal; -} - -float3 perturb(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) -{ - bend *= WAVE_CHOPPYNESS; - float3 col = float3(0.0); - float2 nCoord = float2(0.0); //normal coords - - nCoord = coords * (WAVE_SCALE * 0.025) + windDir * timer * (windSpeed*0.03); - col += shSample(tex,nCoord + float2(-timer*0.005,-timer*0.01)).rgb*0.20; - nCoord = coords * (WAVE_SCALE * 0.1) + windDir * timer * (windSpeed*0.05)-(col.xy/col.zz)*bend; - col += shSample(tex,nCoord + float2(+timer*0.01,+timer*0.005)).rgb*0.20; - - nCoord = coords * (WAVE_SCALE * 0.2) + windDir * timer * (windSpeed*0.1)-(col.xy/col.zz)*bend; - col += shSample(tex,nCoord + float2(-timer*0.02,-timer*0.03)).rgb*0.20; - nCoord = coords * (WAVE_SCALE * 0.5) + windDir * timer * (windSpeed*0.2)-(col.xy/col.zz)*bend; - col += shSample(tex,nCoord + float2(+timer*0.03,+timer*0.02)).rgb*0.15; - - nCoord = coords * (WAVE_SCALE* 0.8) + windDir * timer * (windSpeed*1.0)-(col.xy/col.zz)*bend; - col += shSample(tex, nCoord + float2(-timer*0.06,+timer*0.08)).rgb*0.15; - nCoord = coords * (WAVE_SCALE * 1.0) + windDir * timer * (windSpeed*1.3)-(col.xy/col.zz)*bend; - col += shSample(tex,nCoord + float2(+timer*0.08,-timer*0.06)).rgb*0.10; - - return col; -} - - -float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 eyePosWS, float3 worldNormal, float3 lightDirectionWS0, float waterLevel, float waterTimer, float3 windDir_windSpeed) -{ - float3 waterEyePos = intercept(worldPos.xyz, eyePosWS - worldPos, float3(0,1,0), waterLevel); - float waterDepth = shSaturate((waterEyePos.y - worldPos.y) / 50.0); - - float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,1,0), waterLevel); - - ///\ todo clean this up - float causticdepth = length(causticPos-worldPos.xyz); - causticdepth = 1.0-shSaturate(causticdepth / VISIBILITY); - causticdepth = shSaturate(causticdepth); - - // NOTE: the original shader calculated a tangent space basis here, - // but using only the world normal is cheaper and i couldn't see a visual difference - // also, if this effect gets moved to screen-space some day, it's unlikely to have tangent information - float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xzy * 2 - 1; - - //float fresnel = pow(clamp(dot(LV,causticnorm),0.0,1.0),2.0); - - float NdotL = max(dot(worldNormal.xyz, lightDirectionWS0.xyz),0.0); - - float causticR = 1.0-perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; - - /// \todo sunFade - - // float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*sunFade*causticdepth; - float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth; - float causticG = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; - float causticB = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; - //caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*sunFade*causticdepth; - caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*causticdepth; - - caustics *= 3; - - // shore transition - caustics = shLerp (float3(1,1,1), caustics, waterDepth); - - return caustics; -} - diff --git a/files/materials/objects.mat b/files/materials/objects.mat index 9b0357f01..f1198b4a2 100644 --- a/files/materials/objects.mat +++ b/files/materials/objects.mat @@ -11,9 +11,6 @@ material openmw_objects_base scene_blend default depth_write default alpha_rejection default - shadow_transparency true // use diffuse alpha as mask for shadow - - shadow_caster_material openmw_shadowcaster pass { diff --git a/files/materials/objects.shader b/files/materials/objects.shader index fdf2b3676..f40094fa7 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -94,7 +94,7 @@ // ----------------------------------- FRAGMENT ------------------------------------------ #if UNDERWATER - #include "caustics.h" + #include "underwater.h" #endif SH_BEGIN_PROGRAM @@ -163,7 +163,8 @@ shSampler2D(causticMap) shUniform(float, waterTimer) @shSharedParameter(waterTimer) - + shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight) + shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) #endif @@ -262,22 +263,30 @@ shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); #if UNDERWATER - float fogAmount = (worldPos.y > waterLevel) + float fogAmount = (cameraPos.y > waterLevel) ? shSaturate(length(waterEyePos-worldPos) / VISIBILITY) : shSaturate(length(cameraPos.xyz-worldPos)/ VISIBILITY); float3 eyeVec = normalize(cameraPos.xyz-worldPos); + float waterSunGradient = dot(eyeVec, -normalize(lightDirectionWS0.xyz)); - waterSunGradient = clamp(pow(waterSunGradient*0.7+0.3,2.0),0.0,1.0); + waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); + float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; - float waterGradient = dot(eyeVec, vec3(0.0,-1.0,0.0)); + float waterGradient = dot(eyeVec, float3(0.0,-1.0,0.0)); waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - - float3 waterSunColour = vec3(0.0,1.0,0.85)*waterSunGradient; - waterSunColour = (cameraPos.z < waterLevel) ? waterSunColour*0.5:waterSunColour*0.25;//below or above water? - - float3 waterColour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; - shOutputColour(0).xyz = waterColour; + float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; + float3 waterext = float3(0.6, 0.9, 1.0);//water extinction + watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; + + + float darkness = VISIBILITY*2.0; + darkness = clamp((waterEyePos.y - waterLevel + darkness)/darkness,0.2,1.0); + watercolour *= darkness; + + float isUnderwater = (worldPos.y < waterLevel) ? 1.0 : 0.0; + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater); #endif #if MRT diff --git a/files/materials/quad.mat b/files/materials/quad.mat new file mode 100644 index 000000000..1ada37bc4 --- /dev/null +++ b/files/materials/quad.mat @@ -0,0 +1,22 @@ +material quad +{ + depth_write on + + pass + { + vertex_program quad_vertex + fragment_program quad_fragment + + depth_write $depth_write + + texture_unit SceneBuffer + { + } + } +} + +material quad_noDepthWrite +{ + parent quad + depth_write off +} diff --git a/files/materials/quad.shader b/files/materials/quad.shader new file mode 100644 index 000000000..4fa38cac9 --- /dev/null +++ b/files/materials/quad.shader @@ -0,0 +1,25 @@ +#include "core.h" + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shInput(float2, uv0) + shOutput(float2, UV) + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + } + +#else + + SH_BEGIN_PROGRAM + shInput(float2, UV) + shSampler2D(SceneBuffer) + SH_START_PROGRAM + { + shOutputColour(0) = shSample(SceneBuffer, UV); + } + +#endif diff --git a/files/materials/quad.shaderset b/files/materials/quad.shaderset new file mode 100644 index 000000000..a1252b15c --- /dev/null +++ b/files/materials/quad.shaderset @@ -0,0 +1,15 @@ +shader_set quad_vertex +{ + source quad.shader + type vertex + profiles_cg vs_2_0 vp40 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set quad_fragment +{ + source quad.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 77ba01494..919857401 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -125,7 +125,7 @@ // ----------------------------------- FRAGMENT ------------------------------------------ #if UNDERWATER - #include "caustics.h" + #include "underwater.h" #endif SH_BEGIN_PROGRAM @@ -195,7 +195,8 @@ shSampler2D(causticMap) shUniform(float, waterTimer) @shSharedParameter(waterTimer) - + shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight) + shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) #endif @@ -226,7 +227,7 @@ if (worldPos.y < waterLevel) { float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - waterEyePos = intercept(worldPos, eyePosWS - worldPos, float3(0,1,0), waterLevel); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); } @@ -331,9 +332,44 @@ #if FOG float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); + + #if UNDERWATER + // regular fog only if fragment is above water + if (worldPos.y > waterLevel) + #endif shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); #endif + // prevent negative colour output (for example with negative lights) + shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); + +#if UNDERWATER + float fogAmount = (cameraPos.y > waterLevel) + ? shSaturate(length(waterEyePos-worldPos) / VISIBILITY) + : shSaturate(length(cameraPos.xyz-worldPos)/ VISIBILITY); + + float3 eyeVec = normalize(cameraPos.xyz-worldPos); + + float waterSunGradient = dot(eyeVec, -normalize(lightDirectionWS0.xyz)); + waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); + float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; + + float waterGradient = dot(eyeVec, float3(0.0,-1.0,0.0)); + waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); + float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; + float3 waterext = float3(0.6, 0.9, 1.0);//water extinction + watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; + + + float darkness = VISIBILITY*2.0; + darkness = clamp((waterEyePos.y - waterLevel + darkness)/darkness,0.2,1.0); + watercolour *= darkness; + + float isUnderwater = (worldPos.y < waterLevel) ? 1.0 : 0.0; + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater); +#endif + #if MRT shOutputColour(1) = float4(depth / far,1,1,1); diff --git a/files/materials/underwater.h b/files/materials/underwater.h index fa744ac39..fe19ff93b 100644 --- a/files/materials/underwater.h +++ b/files/materials/underwater.h @@ -14,6 +14,8 @@ #define ABBERATION 0.001 // chromatic abberation amount +#define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction + float3 intercept(float3 lineP, float3 lineN, float3 planeN, diff --git a/files/materials/water.mat b/files/materials/water.mat index d74305c73..2ae561d77 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -31,3 +31,11 @@ material Water } } } + + +material Underwater_Dome +{ + parent openmw_objects_base + + depth_write off +} diff --git a/files/materials/water.shader b/files/materials/water.shader index 4945770a5..4bfc32421 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -43,6 +43,8 @@ // tweakables ---------------------------------------------------- + #define VISIBILITY 1500.0 // how far you can look through water + #define BIG_WAVES_X 0.3 // strength of big waves #define BIG_WAVES_Y 0.3 @@ -57,15 +59,15 @@ #define ABBERATION 0.001 // chromatic abberation amount #define BUMP 1.5 // overall water surface bumpiness - #define REFL_BUMP 0.11 // reflection distortion amount - #define REFR_BUMP 0.08 // refraction distortion amount + #define REFL_BUMP 0.08 // reflection distortion amount + #define REFR_BUMP 0.06 // refraction distortion amount #define SCATTER_AMOUNT 3.0 // amount of sunlight scattering #define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering #define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction - #define SPEC_HARDNESS 256 // specular highlights hardness + #define SPEC_HARDNESS 256 // specular highlights hardness // --------------------------------------------------------------- @@ -201,16 +203,6 @@ // brighten up the refraction underwater refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; - - float waterSunGradient = dot(-vVec, -lVec); - waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); - float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; - - float waterGradient = dot(-vVec, float3(0.0,-1.0,0.0)); - waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; - float3 waterext = float3(0.6, 0.9, 1.0);//water extinction - watercolour = mix(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); // specular float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS); @@ -221,8 +213,31 @@ shOutputColour(0).xyz = shLerp(shOutputColour(0).xyz, refraction, (1-shoreFade) * (1-isUnderwater)); // fog - float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); + if (isUnderwater == 1) + { + float waterSunGradient = dot(-vVec, -lVec); + waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); + float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; + + float waterGradient = dot(-vVec, float3(0.0,-1.0,0.0)); + waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); + float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; + float3 waterext = float3(0.6, 0.9, 1.0);//water extinction + watercolour = mix(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + + float darkness = VISIBILITY*2.0; + darkness = clamp((cameraPos.y+darkness)/darkness,0.2,1.0); + + + float fog = shSaturate(length(cameraPos.xyz-position.xyz) / VISIBILITY); + shOutputColour(0).xyz = shLerp(shOutputColour(0).xyz, watercolour * darkness, shSaturate(fog / waterext)); + } + else + { + float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); + } + } #endif diff --git a/files/water/WaterNormal2.tga b/files/water/WaterNormal2.tga deleted file mode 100644 index 771d15041922130687cc943d8350165c7edadc0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196652 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

GK8 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 K>GQw)JO3X(c8^d1 diff --git a/files/water/caustic_0.png b/files/water/caustic_0.png deleted file mode 100644 index fee464860ea42100b844a4d8dfaa101c3bb80a88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35147 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Ⓢ+p>caiAE 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�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⩔jXf~T)t@b>`XZ1}WXzmgvic~p|ZvqVA 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

3X6KRrMiKbQ@^|Ag;R0leCt%5#7~ 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%0A}9&@$#GffZoEiQ0$R6dA|3w27HBm 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 z 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

N+W*Q_>{H&2lwvZn*zEswczQ!I5ARL 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 AApigX diff --git a/files/water/underwater.cg b/files/water/underwater.cg deleted file mode 100644 index 829d34347..000000000 --- a/files/water/underwater.cg +++ /dev/null @@ -1,61 +0,0 @@ -void main_vp -( - in float4 inPos : POSITION, - - out float4 pos : POSITION, - out float2 uv0 : TEXCOORD0, - out float4 noiseCoord : TEXCOORD1, - - uniform float4x4 worldViewProj, - uniform float timeVal, - uniform float scale -) -{ - // Use standardise transform, so work accord with render system specific (RS depth, requires texture flipping, etc) - pos = mul(worldViewProj, inPos); - - // The input positions adjusted by texel offsets, so clean up inaccuracies - inPos.xy = sign(inPos.xy); - - // Convert to image-space - uv0 = (float2(inPos.x, -inPos.y) + 1.0f) * 0.5f; - noiseCoord = (pos + timeVal) * scale; -} - - - -float4 main_fp_nomrt (float2 iTexCoord : TEXCOORD0, - float3 noiseCoord : TEXCOORD1, - uniform sampler2D RT : register(s0), - uniform sampler2D NormalMap : register(s1), - uniform sampler2D CausticMap : register(s2)) : COLOR -{ - float4 normal = tex2D(NormalMap, noiseCoord) * 2 - 1; - - float4 col = tex2D(RT, iTexCoord + normal.xy * 0.015) + - (tex2D(CausticMap, noiseCoord) / 5); - col.xyz = lerp(col.xyz, float3(0.15, 0.40, 0.40), 0.4); - return col; - -} - - -float4 main_fp (float2 iTexCoord : TEXCOORD0, - float3 noiseCoord : TEXCOORD1, - uniform float far, - uniform sampler2D RT : register(s0), - uniform sampler2D NormalMap : register(s1), - uniform sampler2D CausticMap : register(s2), - uniform sampler2D DepthMap : register(s3)) : COLOR -{ - float4 normal = tex2D(NormalMap, noiseCoord) * 2 - 1; - - float depth = tex2D(DepthMap, iTexCoord + normal.xy * 0.015).r * far; - depth = saturate(depth / 2000.f); - - float4 color = tex2D(RT, iTexCoord + normal.xy * 0.015) + - (tex2D(CausticMap, noiseCoord) / 5); - color.xyz = lerp(color.xyz, float3(0.15, 0.40, 0.40), 0.4); - - return lerp(color, float4(0.15, 0.40, 0.40, 1), depth); -} diff --git a/files/water/water.cg b/files/water/water.cg deleted file mode 100644 index ad0ff57f7..000000000 --- a/files/water/water.cg +++ /dev/null @@ -1,121 +0,0 @@ -void main_vp -( - in float4 iPos : POSITION - , in float2 iUv : TEXCOORD0 - - , out float4 oPos : POSITION - , out float3 oScreenCoords : TEXCOORD0 - , out float2 oUv : TEXCOORD1 - , out float oDepth : TEXCOORD2 - , out float4 oEyeVector : TEXCOORD3 - - , uniform float4x4 wvpMat - , uniform float4 camPosObjSpace -) -{ - oPos = mul(wvpMat, iPos); - - oUv = iUv * 10; // uv scale - oDepth = oPos.z; - - float4x4 scalemat = float4x4( 0.5, 0, 0, 0.5, - 0, -0.5, 0, 0.5, - 0, 0, 0.5, 0.5, - 0, 0, 0, 1 ); - float4 texcoordProj = mul(scalemat, oPos); - oScreenCoords = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w); - - oEyeVector = camPosObjSpace - iPos; -} - -void main_fp -( - out float4 oColor : COLOR - - , in float3 iScreenCoords : TEXCOORD0 - , in float2 iUv : TEXCOORD1 - , in float iDepth : TEXCOORD2 - , in float4 iEyeVector : TEXCOORD3 - , uniform float renderTargetFlipping - , uniform float4 lightPosObjSpace0 - , uniform float4 lightSpecularColour0 - - , uniform sampler2D reflectionMap : register(s0) - , uniform sampler2D refractionMap : register(s1) - , uniform sampler2D depthMap : register(s2) - , uniform sampler2D normalMap : register(s3) - , uniform float time - , uniform float far - , uniform float4 fogParams - , uniform float4 fogColour - , uniform float isUnderwater -) -{ - - float2 screenCoords = iScreenCoords.xy / iScreenCoords.z; - screenCoords.y = (1-saturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y; - - // No need for transparency since we are using a refraction map - oColor.a = 1; - - // Sample screen-space depth map and subtract pixel depth to get the real water depth - float depthTex = tex2D(depthMap, screenCoords).r; - float depth1 = depthTex * far - iDepth; - depth1 = saturate(depth1 / 500.f); - - // Simple wave effect. to be replaced by something better - float2 uv1 = iUv + time * float2(0.5, 0); - float2 uv2 = iUv + time * float2(0, 0.5); - float2 uv3 = iUv + time * float2(-0.5, 0); - float2 uv4 = iUv + time * float2(0, -0.5); - float4 normal = tex2D(normalMap, uv1) + tex2D(normalMap, uv2) + tex2D(normalMap, uv3) + tex2D(normalMap, uv4); - normal = normal / 4.f; - normal = 2*normal - 1; - - float2 screenCoords_reflect = screenCoords + normal.yx * 0.05; - float2 screenCoords_refract = screenCoords + normal.yx * 0.05 * depth1; - - // Sample depth again with the refracted coordinates - depthTex = tex2D(depthMap, screenCoords_refract).r; - float depth2 = (depthTex * far - iDepth) / 500.f; - depth2 = (depthTex == 0 ? 1 : depth2); - // if depth2 is less than 0, this means we would refract something which is above water, - // which we don't want to - so in that case, don't refract - if (depth2 < 0.25) // delta due to inaccuracies - { - screenCoords_refract = screenCoords; - depth2 = depth1; - } - depth2 = saturate(depth2); - - float4 reflection = tex2D(reflectionMap, screenCoords_reflect); - float4 refraction = tex2D(refractionMap, screenCoords_refract); - - // tangent to object space - normal.xyz = normal.xzy; - - iEyeVector.xyz = normalize(iEyeVector.xyz); - - // fresnel - float facing = 1.0 - max(abs(dot(iEyeVector.xyz, normal.xyz)), 0); - float reflectionFactor = saturate(0.35 + 0.65 * pow(facing, 2)); - - // specular - float3 lightDir = normalize(lightPosObjSpace0.xyz); // assumes that light 0 is a directional light - float3 halfVector = normalize(iEyeVector + lightDir); - float specular = pow(max(dot(normal.xyz, halfVector.xyz), 0), 64); - - float opacity = depth2 * saturate(reflectionFactor + specular); - opacity *= (1-isUnderwater); - - reflection.xyz += lightSpecularColour0.xyz * specular; - - oColor.xyz = lerp(refraction.xyz, reflection.xyz, opacity); - - oColor.xyz = lerp(oColor.xyz, float3(0.15, 0.40, 0.40), isUnderwater*0.6); // underwater tint color - oColor.xyz = lerp(oColor.xyz, float3(0.15, 0.40, 0.40), saturate(isUnderwater * (iDepth / 2000.f))); // underwater fog - - // add fog - //float fogValue = saturate((iDepth - fogParams.y) * fogParams.w); - //oColor.xyz = lerp(oColor.xyz, fogColour, fogValue); -} diff --git a/files/water/water.compositor b/files/water/water.compositor deleted file mode 100644 index 94b778773..000000000 --- a/files/water/water.compositor +++ /dev/null @@ -1,45 +0,0 @@ -compositor UnderwaterNoMRT -{ - technique - { - texture rt0 target_width target_height PF_R8G8B8 - - target rt0 { input previous } - - target_output - { - // Start with clear output - input none - - pass render_quad - { - material Water/CompositorNoMRT - input 0 rt0 - } - } - } -} - - -compositor Underwater -{ - technique - { - texture_ref scene gbuffer mrt_output - texture rt0 target_width target_height PF_R8G8B8 - - target rt0 { input previous } - - target_output - { - // Start with clear output - input none - - pass render_quad - { - material Water/Compositor - input 0 rt0 - } - } - } -} From f5ffea4d4b247cc83d0821e6cde2090651215526 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jul 2012 23:30:41 +0200 Subject: [PATCH 215/688] new button, water timescale --- apps/openmw/mwgui/settingswindow.cpp | 7 +++++++ apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 6 ++++++ apps/openmw/mwrender/water.cpp | 7 ++++--- files/materials/objects.shader | 2 +- files/materials/terrain.shader | 2 +- files/mygui/openmw_settings_window.layout | 5 +++++ 7 files changed, 25 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 96cdef4cc..4f8ad77c9 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -119,8 +119,10 @@ namespace MWGui getWidget(mStaticsShadows, "StaticsShadows"); getWidget(mMiscShadows, "MiscShadows"); getWidget(mShadowsDebug, "ShadowsDebug"); + getWidget(mUnderwaterButton, "UnderwaterButton"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); + mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); mFullscreenButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mWaterShaderButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -212,6 +214,7 @@ namespace MWGui mReflectObjectsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect statics", "Water") ? "#{sOn}" : "#{sOff}"); mReflectActorsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect actors", "Water") ? "#{sOn}" : "#{sOff}"); mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); + mUnderwaterButton->setCaptionWithReplacing(Settings::Manager::getBool("underwater effect", "Water") ? "#{sOn}" : "#{sOff}"); mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); @@ -351,6 +354,10 @@ namespace MWGui { if (_sender == mWaterShaderButton) Settings::Manager::setBool("shader", "Water", newState); + else if (_sender == mUnderwaterButton) + { + Settings::Manager::setBool("underwater effect", "Water", newState); + } else if (_sender == mReflectObjectsButton) { Settings::Manager::setBool("reflect misc", "Water", newState); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 840d266f1..63fbed46b 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -43,6 +43,7 @@ namespace MWGui MyGUI::Button* mReflectActorsButton; MyGUI::Button* mReflectTerrainButton; MyGUI::Button* mShadersButton; + MyGUI::Button* mUnderwaterButton; MyGUI::Button* mShadowsEnabledButton; MyGUI::Button* mShadowsLargeDistance; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e1ff91a24..23d222384 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -108,6 +108,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); sh::Factory::getInstance ().setGlobalSetting ("num_lights", Settings::Manager::getString ("num lights", "Objects")); + sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); applyCompositors(); @@ -639,6 +640,11 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); mObjects.rebuildStaticGeometry (); } + else if (it->second == "underwater effect" && it->first == "Water") + { + sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); + mObjects.rebuildStaticGeometry (); + } else if (it->second == "shaders" && it->first == "Objects") { sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index bdf61e96d..7bbe107ed 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -18,6 +18,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + using namespace Ogre; namespace MWRender @@ -67,8 +70,6 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel createMaterial(); mWater->setMaterial(mMaterial); - mUnderwaterEffect = Settings::Manager::getBool("underwater effect", "Water"); - Ogre::Entity* underwaterDome = mSceneManager->createEntity ("underwater_dome.mesh"); underwaterDome->setRenderQueueGroup (RQG_UnderWater); mUnderwaterDome = mSceneManager->getRootSceneNode ()->createChildSceneNode (); @@ -329,7 +330,7 @@ void Water::update(float dt) pos.y = -mWaterPlane.d; mUnderwaterDome->setPosition (pos); - mWaterTimer += dt; + mWaterTimer += dt / 30.0 * MWBase::Environment::get().getWorld()->getTimeScaleFactor(); sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(mWaterTimer))); mRendering->getSkyManager ()->setGlareEnabled (!mIsUnderwater); diff --git a/files/materials/objects.shader b/files/materials/objects.shader index f40094fa7..90d58da60 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -17,7 +17,7 @@ #endif -#define UNDERWATER LIGHTING +#define UNDERWATER @shGlobalSettingBool(underwater_effects) && LIGHTING #define HAS_VERTEXCOLOR @shPropertyBool(has_vertex_colour) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 919857401..24771ca41 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -22,7 +22,7 @@ #define NEED_DEPTH 1 #endif -#define UNDERWATER LIGHTING +#define UNDERWATER @shGlobalSettingBool(underwater_effects) && LIGHTING #if NEED_DEPTH diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index d8fbca69c..07c307324 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -188,6 +188,11 @@ + + + + + From c2acf47d880d2adfd20a9f6b3eca680a2ec0e31b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jul 2012 20:30:10 -0700 Subject: [PATCH 216/688] Store the list of keyframe controllers when building the bones --- components/nifogre/ogre_nif_loader.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 8795efbdf..8ba5c9752 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -144,7 +144,7 @@ static void fail(const std::string &msg) } -void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL) +void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector &ctrls, Ogre::Bone *parent=NULL) { Ogre::Bone *bone; if(!skel->hasBone(node->name)) @@ -159,6 +159,14 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent= bone->setBindingPose(); bone->setInitialState(); + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiKeyframeController) + ctrls.push_back(static_cast(ctrl.getPtr())); + ctrl = ctrl->next; + } + const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { @@ -166,7 +174,7 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent= for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - buildBones(skel, children[i].getPtr(), bone); + buildBones(skel, children[i].getPtr(), ctrls, bone); } } } @@ -183,7 +191,13 @@ void loadResource(Ogre::Resource *resource) Nif::NIFFile nif(skel->getName()); const Nif::Node *node = dynamic_cast(nif.getRecord(0)); - buildBones(skel, node); + + std::vector ctrls; + buildBones(skel, node, ctrls); + + // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file + if(ctrls.size() == 0) // No animations? Then we're done. + return; } bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node) From 0986cd5962d8dfa3279d761d7352ee441e11ae61 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jul 2012 20:48:12 -0700 Subject: [PATCH 217/688] Get the animation controller target names --- components/nifogre/ogre_nif_loader.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 8ba5c9752..8cdb2e99b 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -195,9 +195,27 @@ void loadResource(Ogre::Resource *resource) std::vector ctrls; buildBones(skel, node, ctrls); + std::vector targets; // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file if(ctrls.size() == 0) // No animations? Then we're done. return; + + float maxtime = 0.0f; + for(size_t i = 0;i < ctrls.size();i++) + { + Nif::NiKeyframeController *ctrl = ctrls[i]; + maxtime = std::max(maxtime, ctrl->timeStop); + Nif::Named *target = dynamic_cast(ctrl->target.getPtr()); + if(target != NULL) + targets.push_back(target->name); + } + + if(targets.size() != ctrls.size()) + { + warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+ + Ogre::StringConverter::toString(ctrls.size())+" controllers)"); + return; + } } bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node) From 4210880c061ea7698e0a96cd0ef7292b55a06c14 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jul 2012 21:46:16 -0700 Subject: [PATCH 218/688] Load the animation tracks into Ogre --- components/nifogre/ogre_nif_loader.cpp | 97 ++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 8cdb2e99b..eea53e963 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -180,6 +180,15 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector vectors */ +template +struct KeyTimeSort +{ + bool operator()(const Nif::KeyT &lhs, const Nif::KeyT &rhs) const + { return lhs.mTime < rhs.mTime; } +}; + + typedef std::map LoaderMap; static LoaderMap sLoaders; @@ -216,6 +225,94 @@ void loadResource(Ogre::Resource *resource) Ogre::StringConverter::toString(ctrls.size())+" controllers)"); return; } + + Ogre::Animation *anim = skel->createAnimation("default", maxtime); + /* HACK: Pre-create the node tracks by matching the track IDs with the + * bone IDs. Otherwise, Ogre animates the wrong bones. */ + size_t bonecount = skel->getNumBones(); + for(size_t i = 0;i < bonecount;i++) + anim->createNodeTrack(i, skel->getBone(i)); + + for(size_t i = 0;i < ctrls.size();i++) + { + Nif::NiKeyframeController *kfc = ctrls[i]; + Nif::NiKeyframeData *kf = kfc->data.getPtr(); + + /* Get the keyframes and make sure they're sorted first to last */ + QuaternionKeyList quatkeys = kf->mRotations; + Vector3KeyList trankeys = kf->mTranslations; + FloatKeyList scalekeys = kf->mScales; + std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort()); + std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort()); + std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort()); + + QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); + Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); + FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); + + Ogre::Bone *bone = skel->getBone(targets[i]); + const Ogre::Quaternion startquat = bone->getInitialOrientation(); + const Ogre::Vector3 starttrans = bone->getInitialPosition(); + const Ogre::Vector3 startscale = bone->getInitialScale(); + Ogre::NodeAnimationTrack *nodetrack = anim->getNodeTrack(bone->getHandle()); + + Ogre::Quaternion lastquat, curquat; + Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); + Ogre::Vector3 lastscale(1.0f), curscale(1.0f); + if(quatiter != quatkeys.mKeys.end()) + lastquat = curquat = startquat.Inverse() * quatiter->mValue; + if(traniter != trankeys.mKeys.end()) + lasttrans = curtrans = traniter->mValue - starttrans; + if(scaleiter != scalekeys.mKeys.end()) + lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + bool didlast = false; + while(!didlast) + { + float curtime = kfc->timeStop; + if(quatiter != quatkeys.mKeys.end()) + curtime = std::min(curtime, quatiter->mTime); + if(traniter != trankeys.mKeys.end()) + curtime = std::min(curtime, traniter->mTime); + if(scaleiter != scalekeys.mKeys.end()) + curtime = std::min(curtime, scaleiter->mTime); + + curtime = std::max(curtime, kfc->timeStart); + if(curtime >= kfc->timeStop) + { + didlast = true; + curtime = kfc->timeStop; + } + + // Get the latest quaternion, translation, and scale for the + // current time + while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) + { + lastquat = curquat; + curquat = startquat.Inverse() * quatiter->mValue; + quatiter++; + } + while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) + { + lasttrans = curtrans; + curtrans = traniter->mValue - starttrans; + traniter++; + } + while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) + { + lastscale = curscale; + curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + scaleiter++; + } + + Ogre::TransformKeyFrame *kframe; + kframe = nodetrack->createNodeKeyFrame(curtime); + // FIXME: These should be interpolated since they don't all fall on the same time + kframe->setRotation(curquat); + kframe->setTranslate(curtrans); + kframe->setScale(curscale); + } + } + anim->optimise(); } bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node) From 8b5b74f9ee3ec4b291e87d92fb01bfd78b177638 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 19 Jul 2012 22:34:26 -0700 Subject: [PATCH 219/688] Add a quick hack to let "playgroup all" work on creatures and NPCs --- apps/openmw/mwrender/creatureanimation.cpp | 42 +++++++++++++++------- apps/openmw/mwrender/npcanimation.cpp | 42 +++++++++++++++------- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index fd2855154..492749553 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -52,6 +52,18 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O } ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } + + if(mEntityList.mSkelBase) + { + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) + { + Ogre::AnimationState *state = as.getNext(); + state->setEnabled(true); + state->setLoop(false); + } + } } } @@ -59,20 +71,26 @@ void CreatureAnimation::runAnimation(float timepassed) { if(mAnimate > 0) { - //Add the amount of time passed to time - - //Handle the animation transforms dependent on time - - //Handle the shapes dependent on animation transforms mTime += timepassed; - if(mTime >= mStopTime) + + if(mEntityList.mSkelBase) { - mAnimate--; - //std::cout << "Stopping the animation\n"; - if(mAnimate == 0) - mTime = mStopTime; - else - mTime = mStartTime + (mTime - mStopTime); + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) + { + Ogre::AnimationState *state = as.getNext(); + state->setTimePosition(mTime); + if(state->getTimePosition() >= state->getLength()) + { + mAnimate--; + //std::cout << "Stopping the animation\n"; + if(mAnimate == 0) + mTime = state->getLength(); + else + mTime = mTime - state->getLength(); + } + } } handleAnimationTransforms(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a693a6b46..8c34acba2 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -117,6 +117,18 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere //stay in the same place when we skipanim, or open a gui window } + if(mEntityList.mSkelBase) + { + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) + { + Ogre::AnimationState *state = as.getNext(); + state->setEnabled(true); + state->setLoop(false); + } + } + if(isFemale) mInsert->scale(race->data.height.female, race->data.height.female, race->data.height.female); else @@ -361,24 +373,30 @@ void NpcAnimation::runAnimation(float timepassed) timeToChange = 0; updateParts(); } - timeToChange += timepassed; - //1. Add the amount of time passed to time - - //2. Handle the animation transforms dependent on time - - //3. Handle the shapes dependent on animation transforms if(mAnimate > 0) { mTime += timepassed; - if(mTime > mStopTime) + + if(mEntityList.mSkelBase) { - mAnimate--; - if(mAnimate == 0) - mTime = mStopTime; - else - mTime = mStartTime + (mTime - mStopTime); + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) + { + Ogre::AnimationState *state = as.getNext(); + state->setTimePosition(mTime); + if(state->getTimePosition() >= state->getLength()) + { + mAnimate--; + //std::cout << "Stopping the animation\n"; + if(mAnimate == 0) + mTime = state->getLength(); + else + mTime = mTime - state->getLength(); + } + } } handleAnimationTransforms(); From 66860825cfd4cc74154eb9fb147ed3e73506211c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 20 Jul 2012 00:29:22 -0700 Subject: [PATCH 220/688] Remove some unused and unneeded bits from the Animation class --- apps/openmw/mwrender/animation.cpp | 137 +-------------------- apps/openmw/mwrender/animation.hpp | 14 --- apps/openmw/mwrender/creatureanimation.cpp | 2 - apps/openmw/mwrender/npcanimation.cpp | 2 - 4 files changed, 1 insertion(+), 154 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index fddfe7b8a..06fd34e3c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -15,15 +15,7 @@ Animation::Animation(OEngine::Render::OgreRenderer& _rend) : mInsert(NULL) , mRend(_rend) , mTime(0.0f) - , mStartTime(0.0f) - , mStopTime(0.0f) , mAnimate(0) - , mRindexI() - , mTindexI() - , mShapeNumber(0) - , mShapeIndexI() - , mTransformations(NULL) - , mTextmappings(NULL) { } @@ -37,79 +29,11 @@ Animation::~Animation() void Animation::startScript(std::string groupname, int mode, int loops) { - //If groupname is recognized set animate to true - //Set the start time and stop time - //How many times to loop if(groupname == "all") { mAnimate = loops; - mTime = mStartTime; + mTime = 0.0f; } - else if(mTextmappings) - { - std::string startName = groupname + ": loop start"; - std::string stopName = groupname + ": loop stop"; - - bool first = false; - - if(loops > 1) - { - startName = groupname + ": loop start"; - stopName = groupname + ": loop stop"; - - for(std::map::iterator iter = mTextmappings->begin(); iter != mTextmappings->end(); iter++) - { - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); - - if(current == startName) - { - mStartTime = iter->second; - mAnimate = loops; - mTime = mStartTime; - first = true; - } - if(current2 == stopName) - { - mStopTime = iter->second; - if(first) - break; - } - } - } - - if(!first) - { - startName = groupname + ": start"; - stopName = groupname + ": stop"; - - for(std::map::iterator iter = mTextmappings->begin(); iter != mTextmappings->end(); iter++) - { - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); - - if(current == startName) - { - mStartTime = iter->second; - mAnimate = loops; - mTime = mStartTime; - first = true; - } - if(current2 == stopName) - { - mStopTime = iter->second; - if(first) - break; - } - } - } - - } - } @@ -118,63 +42,4 @@ void Animation::stopScript() mAnimate = 0; } - -bool Animation::timeIndex(float time, const std::vector ×, int &i, int &j, float &x) -{ - size_t count; - if((count=times.size()) == 0) - return false; - - if(time <= times[0]) - { - i = j = 0; - x = 0.0; - return true; - } - if(time >= times[count-1]) - { - i = j = count - 1; - x = 0.0; - return true; - } - - if(i < 0 || (size_t)i >= count) - i = 0; - - float tI = times[i]; - if(time > tI) - { - j = i + 1; - float tJ; - while(time >= (tJ=times[j])) - { - i = j++; - tI = tJ; - } - x = (time-tI) / (tJ-tI); - return true; - } - - if(time < tI) - { - j = i - 1; - float tJ; - while(time <= (tJ=times[j])) - { - i = j--; - tI = tJ; - } - x = (time-tI) / (tJ-tI); - return true; - } - - j = i; - x = 0.0; - return true; -} - -void Animation::handleAnimationTransforms() -{ -} - } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index def7f226c..bed02f85f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -26,23 +26,9 @@ protected: static std::map sUniqueIDs; float mTime; - float mStartTime; - float mStopTime; int mAnimate; - //Represents a rotation index for each bone - std::vectormRindexI; - //Represents a translation index for each bone - std::vectormTindexI; - //Only shapes with morphing data will use a shape number - int mShapeNumber; - std::vector > mShapeIndexI; - - std::vector* mTransformations; - std::map* mTextmappings; NifOgre::EntityList mEntityList; - void handleAnimationTransforms(); - bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); public: Animation(OEngine::Render::OgreRenderer& _rend); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 492749553..ee1fd45ba 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -92,8 +92,6 @@ void CreatureAnimation::runAnimation(float timepassed) } } } - - handleAnimationTransforms(); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 8c34acba2..4d2ca557d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -398,8 +398,6 @@ void NpcAnimation::runAnimation(float timepassed) } } } - - handleAnimationTransforms(); } } From 2db80a1504576d15df11b9988fb5beabf3f4bf8e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 20 Jul 2012 00:53:12 -0700 Subject: [PATCH 221/688] Rename a couple methods to match their scripting counterparts --- apps/openmw/mwrender/actors.cpp | 8 +++----- apps/openmw/mwrender/animation.cpp | 4 ++-- apps/openmw/mwrender/animation.hpp | 8 ++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index b37921b0c..a64397d49 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -122,15 +122,13 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store){ void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number){ if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->startScript(groupName, mode, number); + mAllActors[ptr]->playGroup(groupName, mode, number); } void Actors::skipAnimation (const MWWorld::Ptr& ptr){ if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->stopScript(); + mAllActors[ptr]->skipAnim(); } void Actors::update (float duration){ for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) - { - (iter->second)->runAnimation(duration); - } + iter->second->runAnimation(duration); } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 06fd34e3c..1f0a99518 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -27,7 +27,7 @@ Animation::~Animation() mEntityList.mEntities.clear(); } -void Animation::startScript(std::string groupname, int mode, int loops) +void Animation::playGroup(std::string groupname, int mode, int loops) { if(groupname == "all") { @@ -37,7 +37,7 @@ void Animation::startScript(std::string groupname, int mode, int loops) } -void Animation::stopScript() +void Animation::skipAnim() { mAnimate = 0; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index bed02f85f..0ed4545f2 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -32,11 +32,11 @@ protected: public: Animation(OEngine::Render::OgreRenderer& _rend); - virtual void runAnimation(float timepassed) = 0; - void startScript(std::string groupname, int mode, int loops); - void stopScript(); - virtual ~Animation(); + + void playGroup(std::string groupname, int mode, int loops); + void skipAnim(); + virtual void runAnimation(float timepassed) = 0; }; } From 21728020f6c6b017024f98595f43f29cad3e1e44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jul 2012 14:45:42 +0200 Subject: [PATCH 222/688] fixed the water on local map --- apps/openmw/mwrender/renderingmanager.cpp | 2 + apps/openmw/mwrender/water.cpp | 63 +++--- apps/openmw/mwrender/water.hpp | 8 +- extern/shiny | 2 +- files/materials/openmw.configuration | 1 + files/materials/water.mat | 14 ++ files/materials/water.shader | 64 +++++++ files/water/water.material | 221 ---------------------- 8 files changed, 122 insertions(+), 253 deletions(-) delete mode 100644 files/water/water.material diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 23d222384..d0060c243 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -109,6 +109,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); sh::Factory::getInstance ().setGlobalSetting ("num_lights", Settings::Manager::getString ("num lights", "Objects")); sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); + sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); applyCompositors(); @@ -638,6 +639,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec { applyCompositors(); sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); + sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); mObjects.rebuildStaticGeometry (); } else if (it->second == "underwater effect" && it->first == "Water") diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 7bbe107ed..a886eac93 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -17,6 +17,7 @@ #include "compositors.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -40,6 +41,8 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(0))); sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(1, 0.6))); + mMaterial = MaterialManager::getSingleton().getByName("Water"); + mTop = cell->water; mIsUnderwater = false; @@ -66,8 +69,6 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel applyRTT(); applyVisibilityMask(); - - createMaterial(); mWater->setMaterial(mMaterial); Ogre::Entity* underwaterDome = mSceneManager->createEntity ("underwater_dome.mesh"); @@ -84,6 +85,9 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel setHeight(mTop); + sh::MaterialInstance* m = sh::Factory::getInstance ().getMaterialInstance ("Water"); + m->setListener (this); + // ---------------------------------------------------------------------------------------------- // ---------------------------------- reflection debug overlay ---------------------------------- @@ -106,7 +110,7 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false); - TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(mReflectionTexture->getName()); + debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(mReflectionTexture->getName()); OverlayContainer* debugPanel; @@ -238,29 +242,6 @@ void Water::postRenderTargetUpdate(const RenderTargetEvent& evt) } } -void Water::createMaterial() -{ - if (mReflectionTarget == 0) - { - mMaterial = MaterialManager::getSingleton().getByName("Water_Fallback"); - } - else - { - mMaterial = MaterialManager::getSingleton().getByName("Water"); - sh::Factory::getInstance ().setTextureAlias ("WaterReflection", mReflectionTexture->getName()); - } - - // these have to be set in code - std::string textureNames[32]; - for (int i=0; i<32; ++i) - { - textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds"; - } - - if (mReflectionTarget == 0) - mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2); -} - void Water::assignTextures() { if (Settings::Manager::getBool("shader", "Water")) @@ -292,7 +273,7 @@ void Water::updateVisible() void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) { // We don't want the sky to get clipped by custom near clip plane (the water plane) - if (((queueGroupId < 20) || queueGroupId == RQG_UnderWater) && mReflectionRenderActive) + if (queueGroupId < 20 && mReflectionRenderActive) { mOldFarClip = mReflectionCamera->getFarClipDistance (); mReflectionCamera->disableCustomNearClipPlane(); @@ -309,7 +290,7 @@ void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &in void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) { - if (((queueGroupId < 20) || queueGroupId == RQG_UnderWater) && mReflectionRenderActive) + if (queueGroupId < 20 && mReflectionRenderActive) { mReflectionCamera->setFarClipDistance (mOldFarClip); if (!mIsUnderwater) @@ -363,6 +344,8 @@ void Water::applyRTT() rtt->setActive(true); mReflectionTarget = rtt; + + sh::Factory::getInstance ().setTextureAlias ("WaterReflection", mReflectionTexture->getName()); } } @@ -406,7 +389,6 @@ void Water::processChangedSettings(const Settings::CategorySettingVector& settin { applyRTT(); applyVisibilityMask(); - createMaterial(); mWater->setMaterial(mMaterial); assignTextures(); } @@ -414,4 +396,27 @@ void Water::processChangedSettings(const Settings::CategorySettingVector& settin applyVisibilityMask(); } +void Water::requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration) +{ +} + +void Water::createdConfiguration (sh::MaterialInstance* m, const std::string& configuration) +{ + if (configuration == "local_map" || !Settings::Manager::getBool("shader", "Water")) + { + // for simple water, set animated texture names + // these have to be set in code + std::string textureNames[32]; + for (int i=0; i<32; ++i) + { + textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds"; + } + + Ogre::Technique* t = static_cast(m->getMaterial())->getOgreTechniqueForConfiguration(configuration); + t->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2); + t->getPass(0)->setDepthWriteEnabled (false); + t->getPass(0)->setSceneBlending (Ogre::SBT_TRANSPARENT_ALPHA); + } +} + } // namespace diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 6d0001119..60e39c496 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -13,6 +13,8 @@ #include "renderconst.hpp" +#include + namespace Ogre { class Camera; @@ -29,7 +31,7 @@ namespace MWRender { class RenderingManager; /// Water rendering - class Water : public Ogre::RenderTargetListener, public Ogre::RenderQueueListener + class Water : public Ogre::RenderTargetListener, public Ogre::RenderQueueListener, public sh::MaterialInstanceListener { static const int CELL_SIZE = 8192; Ogre::Camera *mCamera; @@ -74,7 +76,6 @@ namespace MWRender { std::string mCompositorName; - void createMaterial(); Ogre::MaterialPtr mMaterial; Ogre::Camera* mReflectionCamera; @@ -104,6 +105,9 @@ namespace MWRender { void changeCell(const ESM::Cell* cell); void setHeight(const float height); + virtual void requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration); + virtual void createdConfiguration (sh::MaterialInstance* m, const std::string& configuration); + }; } diff --git a/extern/shiny b/extern/shiny index 4853ea735..7af50bf44 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 4853ea7351edced75a48662da5f3e857961e0b47 +Subproject commit 7af50bf4463a828ee0e8cb72c93445c793864bf9 diff --git a/files/materials/openmw.configuration b/files/materials/openmw.configuration index cddf49a03..ee97451d3 100644 --- a/files/materials/openmw.configuration +++ b/files/materials/openmw.configuration @@ -13,4 +13,5 @@ configuration local_map lighting false shadows false shadows_pssm false + simple_water true } diff --git a/files/materials/water.mat b/files/materials/water.mat index 2ae561d77..dcea5a0d0 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -2,6 +2,11 @@ material Water { pass { + emissive 0.6 0.7 1.0 + ambient 0 0 0 + diffuse 0 0 0 1 + specular 0 0 0 32 + vertex_program water_vertex fragment_program water_fragment @@ -29,6 +34,15 @@ material Water { direct_texture water_nm.png } + + + // for simple_water + texture_unit animatedTexture + { + create_in_ffp true + scale 0.1 0.1 + alpha_op_ex source1 src_manual src_current 0.7 + } } } diff --git a/files/materials/water.shader b/files/materials/water.shader index 4bfc32421..a5eb9b189 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -1,7 +1,68 @@ #include "core.h" + +#define SIMPLE_WATER @shGlobalSettingBool(simple_water) + + +#if SIMPLE_WATER + // --------------------------------------- SIMPLE WATER --------------------------------------------------- + + + #define MRT @shGlobalSettingBool(mrt_output) + + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + shInput(float2, uv0) + shOutput(float2, UV) + shOutput(float, depth) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + UV = uv0; + depth = shOutputPosition.z; + } + +#else + + SH_BEGIN_PROGRAM + shSampler2D(animatedTexture) + shInput(float2, UV) + shInput(float, depth) +#if MRT + shDeclareMrtOutput(1) +#endif + + shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour) + shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) + + + SH_START_PROGRAM + { + shOutputColour(0).xyz = shSample(animatedTexture, UV * 15).xyz * float3(0.6, 0.7, 1.0); + shOutputColour(0).w = 0.7; + + float fogValue = shSaturate((depth - fogParams.y) * fogParams.w); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); + +#if MRT + shOutputColour(1) = float4(1,1,1,1); +#endif + } + +#endif + +#else + + + // Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html ) + + #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM @@ -241,3 +302,6 @@ } #endif + + +#endif diff --git a/files/water/water.material b/files/water/water.material deleted file mode 100644 index 7376d10cc..000000000 --- a/files/water/water.material +++ /dev/null @@ -1,221 +0,0 @@ -vertex_program UnderwaterEffectVP cg -{ - source underwater.cg - entry_point main_vp - profiles vs_1_1 arbvp1 - - default_params - { - param_named_auto worldViewProj worldviewproj_matrix - } -} - - -fragment_program UnderwaterEffectFP_NoMRT cg -{ - source underwater.cg - entry_point main_fp_nomrt - profiles ps_2_0 arbfp1 -} - -fragment_program UnderwaterEffectFP cg -{ - source underwater.cg - entry_point main_fp - profiles ps_2_0 arbfp1 -} - -vertex_program Water_VP cg -{ - source water.cg - entry_point main_vp - profiles vs_2_x arbvp1 - - default_params - { - param_named_auto wvpMat worldviewproj_matrix - } -} - -fragment_program Water_FP cg -{ - source water.cg - entry_point main_fp - profiles ps_2_x arbfp1 -} - -material __Water -{ - technique - { - pass - { - cull_hardware none - - vertex_program_ref Water_VP - { - param_named_auto camPosObjSpace camera_position_object_space - } - fragment_program_ref Water_FP - { - param_named_auto time time 0.1 - //param_named_auto fogColour fog_colour - //param_named_auto fogParams fog_params - param_named_auto renderTargetFlipping render_target_flipping - param_named_auto far far_clip_distance - param_named_auto lightPosObjSpace0 light_position_object_space 0 - param_named_auto lightSpecularColour0 light_specular_colour 0 - param_named isUnderwater float 0 - } - - texture_unit reflectionMap - { - texture WaterReflection - tex_address_mode clamp - } - - texture_unit refractionMap - { - tex_address_mode clamp - } - - texture_unit depthMap - { - tex_address_mode clamp - } - - texture_unit normalMap - { - texture WaterNormal2.tga - } - } - } - - technique - { - scheme Fallback - pass - { - cull_hardware none - scene_blend alpha_blend - depth_write off - diffuse 0 0 0 1 - emissive 0.6 0.7 1.0 - ambient 0 0 0 - texture_unit - { - // texture names set via code - scale 0.1 0.1 - alpha_op_ex source1 src_manual src_current 0.7 - } - } - } - -} - -material Water_Fallback -{ - technique - { - scheme Fallback - pass - { - cull_hardware none - scene_blend alpha_blend - depth_write off - diffuse 0 0 0 1 - emissive 0.6 0.7 1.0 - ambient 0 0 0 - texture_unit - { - // texture names set via code - scale 0.1 0.1 - alpha_op_ex source1 src_manual src_current 0.7 - } - } - } -} - -material Water/CompositorNoMRT -{ - technique - { - pass - { - depth_check off - vertex_program_ref UnderwaterEffectVP - { - param_named_auto timeVal time 0.25 - param_named scale float 0.1 - } - - fragment_program_ref UnderwaterEffectFP_NoMRT - { - } - - texture_unit RT - { - tex_coord_set 0 - tex_address_mode clamp - filtering linear linear linear - } - - texture_unit - { - texture WaterNormal2.tga 2d - tex_coord_set 1 - //tex_address_mode clamp - filtering linear linear linear - } - texture_unit - { - texture caustic_0.png 2d - tex_coord_set 2 - //tex_address_mode clamp - filtering linear linear linear - } - } - } -} - -material Water/Compositor -{ - technique - { - pass - { - depth_check off - vertex_program_ref UnderwaterEffectVP - { - param_named_auto timeVal time 0.25 - param_named scale float 0.1 - } - - fragment_program_ref UnderwaterEffectFP - { - param_named_auto far far_clip_distance - } - - texture_unit RT - { - tex_coord_set 0 - tex_address_mode clamp - } - - texture_unit - { - texture WaterNormal2.tga 2d - tex_coord_set 2 - } - texture_unit - { - texture caustic_0.png 2d - tex_coord_set 3 - } - - texture_unit DepthMap - { - } - } - } -} From 014396e80c376663b8bba9ac598f1828d569ea49 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jul 2012 16:44:03 +0200 Subject: [PATCH 223/688] remove the plugins.cfg files, do not enforce CG plugin --- CMakeLists.txt | 39 ++++++++++------------- apps/launcher/graphicspage.cpp | 35 +++++++++++++++++--- apps/openmw/engine.cpp | 1 - apps/openmw/mwrender/renderingmanager.cpp | 11 ++++--- apps/openmw/mwrender/water.cpp | 4 +++ components/files/configurationmanager.cpp | 17 ---------- components/files/configurationmanager.hpp | 2 -- files/gbuffer/gbuffer.compositor | 7 +++- files/materials/atmosphere.shader | 2 ++ files/materials/clouds.shader | 2 +- files/materials/objects.shader | 5 +-- files/materials/quad.mat | 13 ++++++++ files/materials/quad.shaderset | 16 ++++++++++ files/materials/water.shader | 2 +- files/plugins.cfg.linux | 11 ------- files/plugins.cfg.mac | 12 ------- files/plugins.cfg.win32 | 13 -------- libs/openengine/ogre/renderer.cpp | 35 ++++++++++++++++++-- libs/openengine/ogre/renderer.hpp | 1 - 19 files changed, 131 insertions(+), 97 deletions(-) delete mode 100644 files/plugins.cfg.linux delete mode 100644 files/plugins.cfg.mac delete mode 100644 files/plugins.cfg.win32 diff --git a/CMakeLists.txt b/CMakeLists.txt index 269fe456b..c7e23a98d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,14 +20,6 @@ set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") -# Debug suffix for plugins -set(DEBUG_SUFFIX "") -if (DEFINED CMAKE_BUILD_TYPE) - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(DEBUG_SUFFIX "_d") - endif() -endif() - # doxygen main page configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") @@ -230,6 +222,22 @@ if (APPLE) ${OGRE_Plugin_ParticleFX_LIBRARY_REL}) endif (APPLE) + +# Set up Ogre plugin folder & debug suffix +set(DEBUG_SUFFIX "") +if (DEFINED CMAKE_BUILD_TYPE) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DEBUG_SUFFIX "_d") + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") + else() + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") + endif() +endif() +add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") +add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") +add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") + + add_subdirectory(files/) add_subdirectory(files/mygui) @@ -254,15 +262,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg "${OpenMW_BINARY_DIR}/openmw.cfg.install") -if (WIN32) - configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.win32 - "${OpenMW_BINARY_DIR}/plugins.cfg" COPYONLY) -endif (WIN32) if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.linux - "${OpenMW_BINARY_DIR}/plugins.cfg") - configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop "${OpenMW_BINARY_DIR}/openmw.desktop") endif() @@ -284,13 +285,8 @@ if (APPLE) set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") - configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac - "${OpenMW_BINARY_DIR}/plugins.cfg") - set(OGRE_PLUGIN_DIR_2 ${OGRE_PLUGIN_DIR}) set(OGRE_PLUGIN_DIR "") - configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac - "${OpenMW_BINARY_DIR}/plugins.cfg.install") set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_2}) configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist @@ -337,7 +333,6 @@ if(DPKG_PROGRAM) INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "../etc/openmw/" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") - INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") #Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "share/games/openmw/" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") @@ -375,7 +370,6 @@ if(WIN32) INSTALL(FILES ${dll_files} DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") INSTALL(FILES - "${OpenMW_BINARY_DIR}/plugins.cfg" "${OpenMW_SOURCE_DIR}/readme.txt" "${OpenMW_SOURCE_DIR}/GPL3.txt" "${OpenMW_SOURCE_DIR}/OFL.txt" @@ -547,7 +541,6 @@ if (APPLE) install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) - install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg.install" RENAME "plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index fb798fee8..8caa2b550 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -1,6 +1,9 @@ #include +#include + #include +#include #include #include @@ -70,9 +73,6 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent) bool GraphicsPage::setupOgre() { - QString pluginCfg = mCfgMgr.getPluginsConfigPath().string().c_str(); - QFile file(pluginCfg); - // Create a log manager so we can surpress debug text to stdout/stderr Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager; logMgr->createLog((mCfgMgr.getLogPath().string() + "/launcherOgre.log"), true, false, false); @@ -82,7 +82,7 @@ bool GraphicsPage::setupOgre() #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) mOgre = new Ogre::Root("", "", "./launcherOgre.log"); #else - mOgre = new Ogre::Root(pluginCfg.toStdString(), "", "./launcherOgre.log"); + mOgre = new Ogre::Root("", "", "./launcherOgre.log"); #endif } catch(Ogre::Exception &ex) @@ -93,7 +93,6 @@ bool GraphicsPage::setupOgre() msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("
Failed to create the Ogre::Root object

\ - Make sure the plugins.cfg is present and valid.

\ Press \"Show Details...\" for more information.
")); msgBox.setDetailedText(ogreError); msgBox.exec(); @@ -102,6 +101,32 @@ bool GraphicsPage::setupOgre() return false; } + + std::string pluginDir; + const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); + if (pluginEnv) + pluginDir = pluginEnv; + else + { +#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + pluginDir = ".\\"; +#endif +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + pluginDir = OGRE_PLUGIN_DIR; +#endif +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + pluginDir = OGRE_PLUGIN_DIR_REL; +#endif + } + + std::string glPlugin = std::string(pluginDir) + "/RenderSystem_GL" + OGRE_PLUGIN_DEBUG_SUFFIX; + if (boost::filesystem::exists(glPlugin + ".so") || boost::filesystem::exists(glPlugin + ".dll")) + mOgre->loadPlugin (glPlugin); + + std::string dxPlugin = std::string(pluginDir) + "/RenderSystem_Direct3D9" + OGRE_PLUGIN_DEBUG_SUFFIX; + if (boost::filesystem::exists(dxPlugin + ".so") || boost::filesystem::exists(dxPlugin + ".dll")) + mOgre->loadPlugin (dxPlugin); + #ifdef ENABLE_PLUGIN_GL mGLPlugin = new Ogre::GLPlugin(); mOgre->installPlugin(mGLPlugin); diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 7966639a6..96fbeb9e2 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -292,7 +292,6 @@ void OMW::Engine::go() } mOgre->configure( mCfgMgr.getLogPath().string(), - mCfgMgr.getPluginsConfigPath().string(), renderSystem, false); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d0060c243..852288d14 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -111,6 +111,9 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); + sh::Factory::getInstance ().setSharedParameter ("viewportBackground", sh::makeProperty (new sh::Vector3(0,0,0))); + sh::Factory::getInstance ().setSharedParameter ("waterEnabled", sh::makeProperty (new sh::FloatValue(0.0))); + applyCompositors(); // Turn the entire scene (represented by the 'root' node) -90 @@ -379,11 +382,9 @@ void RenderingManager::configureFog(const float density, const Ogre::ColourValue mRendering.getCamera()->setFarClipDistance ( max / density ); mRendering.getViewport()->setBackgroundColour (colour); - CompositorInstance* inst = CompositorManager::getSingleton().getCompositorChain(mRendering.getViewport())->getCompositor("gbuffer"); - if (inst != 0) - inst->getCompositor()->getTechnique(0)->getTargetPass(0)->getPass(0)->setClearColour(colour); - if (mWater) - mWater->setViewportBackground(colour); + sh::Factory::getInstance ().setSharedParameter ("viewportBackground", + sh::makeProperty (new sh::Vector3(colour.r, colour.g, colour.b))); + } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index a886eac93..1c0afab67 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -141,6 +141,8 @@ void Water::setActive(bool active) { mActive = active; updateVisible(); + + sh::Factory::getInstance ().setSharedParameter ("waterEnabled", sh::makeProperty (new sh::FloatValue(active ? 1.0 : 0.0))); } Water::~Water() @@ -413,6 +415,8 @@ void Water::createdConfiguration (sh::MaterialInstance* m, const std::string& co } Ogre::Technique* t = static_cast(m->getMaterial())->getOgreTechniqueForConfiguration(configuration); + if (t->getPass(0)->getNumTextureUnitStates () == 0) + return; t->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2); t->getPass(0)->setDepthWriteEnabled (false); t->getPass(0)->setSceneBlending (Ogre::SBT_TRANSPARENT_ALPHA); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 150a4fcd8..af057ef97 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -15,7 +15,6 @@ namespace Files { static const char* const openmwCfgFile = "openmw.cfg"; -static const char* const pluginsCfgFile = "plugins.cfg"; const char* const mwToken = "?mw?"; const char* const localToken = "?local?"; @@ -27,17 +26,6 @@ ConfigurationManager::ConfigurationManager() { setupTokensMapping(); - mPluginsCfgPath = mFixedPath.getLocalPath() / pluginsCfgFile; - if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) - { - mPluginsCfgPath = mFixedPath.getGlobalPath() / pluginsCfgFile; - if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) - { - std::cerr << "Failed to find " << pluginsCfgFile << " file!" << std::endl; - mPluginsCfgPath.clear(); - } - } - mLogPath = mFixedPath.getUserPath(); } @@ -162,11 +150,6 @@ const boost::filesystem::path& ConfigurationManager::getInstallPath() const return mFixedPath.getInstallPath(); } -const boost::filesystem::path& ConfigurationManager::getPluginsConfigPath() const -{ - return mPluginsCfgPath; -} - const boost::filesystem::path& ConfigurationManager::getLogPath() const { return mLogPath; diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 0c22c6f7d..ecbfac664 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -40,7 +40,6 @@ struct ConfigurationManager const boost::filesystem::path& getLocalDataPath() const; const boost::filesystem::path& getInstallPath() const; - const boost::filesystem::path& getPluginsConfigPath() const; const boost::filesystem::path& getLogPath() const; private: @@ -57,7 +56,6 @@ struct ConfigurationManager FixedPathType mFixedPath; - boost::filesystem::path mPluginsCfgPath; boost::filesystem::path mLogPath; TokensMappingContainer mTokensMapping; diff --git a/files/gbuffer/gbuffer.compositor b/files/gbuffer/gbuffer.compositor index b3d80e9d5..04600ce9b 100644 --- a/files/gbuffer/gbuffer.compositor +++ b/files/gbuffer/gbuffer.compositor @@ -9,11 +9,16 @@ compositor gbuffer target mrt_output { input none + pass clear { - // make sure to set this to the viewport background color from outside colour_value 0 0 0 1 } + pass render_quad + { + // this makes sure the depth for background is set to 1 + material openmw_viewport_init + } pass render_scene { // Renders everything except water diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index 295fa9376..6e22b7ac3 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -28,6 +28,8 @@ SH_START_PROGRAM { shOutputColour(0) = colourPassthrough * atmosphereColour; + + shOutputColour(0) = float3(0,0,0,1); #if MRT shOutputColour(1) = float4(1,1,1,1); diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index 7677ecd95..aa53e6051 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -43,7 +43,7 @@ float4 albedo = shSample(diffuseMap1, scrolledUV) * (1-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor; shOutputColour(0) = colourPassthrough * float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity); - + shOutputColour(0) = float3(0,0,0,1); #if MRT shOutputColour(1) = float4(1,1,1,1); #endif diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 90d58da60..eafce7af6 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -164,6 +164,7 @@ shUniform(float, waterTimer) @shSharedParameter(waterTimer) shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight) + shUniform(float, waterEnabled) @shSharedParameter(waterEnabled) shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed) #endif @@ -204,7 +205,7 @@ #if UNDERWATER float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)).xyz; float3 waterEyePos = float3(1,1,1); - if (worldPos.y < waterLevel) + if (worldPos.y < waterLevel && waterEnabled == 1) { float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); @@ -286,7 +287,7 @@ watercolour *= darkness; float isUnderwater = (worldPos.y < waterLevel) ? 1.0 : 0.0; - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater * waterEnabled); #endif #if MRT diff --git a/files/materials/quad.mat b/files/materials/quad.mat index 1ada37bc4..afb7f5111 100644 --- a/files/materials/quad.mat +++ b/files/materials/quad.mat @@ -20,3 +20,16 @@ material quad_noDepthWrite parent quad depth_write off } + +material openmw_viewport_init +{ + pass + { + vertex_program viewport_init_vertex + fragment_program viewport_init_fragment + + depth_write off + depth_check off + scene_blend add + } +} diff --git a/files/materials/quad.shaderset b/files/materials/quad.shaderset index a1252b15c..c61497503 100644 --- a/files/materials/quad.shaderset +++ b/files/materials/quad.shaderset @@ -13,3 +13,19 @@ shader_set quad_fragment profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 profiles_hlsl ps_2_0 } + +shader_set viewport_init_vertex +{ + source quad2.shader + type vertex + profiles_cg vs_2_0 vp40 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set viewport_init_fragment +{ + source quad2.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/materials/water.shader b/files/materials/water.shader index a5eb9b189..805cf7b81 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -116,7 +116,7 @@ #define SMALL_WAVES_Y 0.1 #define WAVE_CHOPPYNESS 0.15 // wave choppyness - #define WAVE_SCALE 150 // overall wave scale + #define WAVE_SCALE 75 // overall wave scale #define ABBERATION 0.001 // chromatic abberation amount #define BUMP 1.5 // overall water surface bumpiness diff --git a/files/plugins.cfg.linux b/files/plugins.cfg.linux deleted file mode 100644 index 7b8d99e8f..000000000 --- a/files/plugins.cfg.linux +++ /dev/null @@ -1,11 +0,0 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=${OGRE_PLUGIN_DIR_REL} - -# Define plugins -Plugin=RenderSystem_GL${DEBUG_SUFFIX} -Plugin=Plugin_ParticleFX${DEBUG_SUFFIX} -Plugin=Plugin_OctreeSceneManager${DEBUG_SUFFIX} -Plugin=Plugin_CgProgramManager${DEBUG_SUFFIX} - diff --git a/files/plugins.cfg.mac b/files/plugins.cfg.mac deleted file mode 100644 index fac18dc8f..000000000 --- a/files/plugins.cfg.mac +++ /dev/null @@ -1,12 +0,0 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=${OGRE_PLUGIN_DIR} - -# Define plugins -Plugin=RenderSystem_GL${DEBUG_SUFFIX}.1.8.0 -Plugin=Plugin_ParticleFX${DEBUG_SUFFIX}.1.8.0 -Plugin=Plugin_OctreeSceneManager${DEBUG_SUFFIX}.1.8.0 -Plugin=Plugin_CgProgramManager${DEBUG_SUFFIX}.1.8.0 - - diff --git a/files/plugins.cfg.win32 b/files/plugins.cfg.win32 deleted file mode 100644 index 6b4e9ef9d..000000000 --- a/files/plugins.cfg.win32 +++ /dev/null @@ -1,13 +0,0 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=.\ - -# Define plugins -Plugin=RenderSystem_Direct3D9${DEBUG_SUFFIX} -Plugin=RenderSystem_GL${DEBUG_SUFFIX} -Plugin=Plugin_ParticleFX${DEBUG_SUFFIX} -Plugin=Plugin_OctreeSceneManager${DEBUG_SUFFIX} -Plugin=Plugin_CgProgramManager${DEBUG_SUFFIX} - - diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index f2f4b4c81..7e609faa8 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -9,7 +9,10 @@ #include "OgreTexture.h" #include "OgreHardwarePixelBuffer.h" +#include + #include +#include #include using namespace Ogre; @@ -70,7 +73,6 @@ float OgreRenderer::getFPS() } void OgreRenderer::configure(const std::string &logPath, - const std::string &pluginCfg, const std::string& renderSystem, bool _logging) { @@ -90,9 +92,38 @@ void OgreRenderer::configure(const std::string &logPath, mRoot = new Root("", "", ""); loadPlugins(); #else - mRoot = new Root(pluginCfg, "", ""); + mRoot = new Root("", "", ""); #endif + std::string pluginDir; + const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); + if (pluginEnv) + pluginDir = pluginEnv; + else + { +#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + pluginDir = ".\\"; +#endif +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + pluginDir = OGRE_PLUGIN_DIR; +#endif +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + pluginDir = OGRE_PLUGIN_DIR_REL; +#endif + } + + std::string glPlugin = std::string(pluginDir) + "/RenderSystem_GL" + OGRE_PLUGIN_DEBUG_SUFFIX; + if (boost::filesystem::exists(glPlugin + ".so") || boost::filesystem::exists(glPlugin + ".dll")) + mRoot->loadPlugin (glPlugin); + + std::string dxPlugin = std::string(pluginDir) + "/RenderSystem_Direct3D9" + OGRE_PLUGIN_DEBUG_SUFFIX; + if (boost::filesystem::exists(dxPlugin + ".so") || boost::filesystem::exists(dxPlugin + ".dll")) + mRoot->loadPlugin (dxPlugin); + + std::string cgPlugin = std::string(pluginDir) + "/Plugin_CgProgramManager" + OGRE_PLUGIN_DEBUG_SUFFIX; + if (boost::filesystem::exists(cgPlugin + ".so") || boost::filesystem::exists(cgPlugin + ".dll")) + mRoot->loadPlugin (cgPlugin); + RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) throw std::runtime_error ("RenderSystem with name " + renderSystem + " not found, make sure the plugins are loaded"); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index c7c30c8d4..247c8f95a 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -110,7 +110,6 @@ namespace OEngine set up the Root and logging classes. */ void configure( const std::string &logPath, // Path to directory where to store log files - const std::string &pluginCfg, // plugin.cfg file const std::string &renderSystem, bool _logging); // Enable or disable logging From 41791ccaa2eb337bc374034d9ad5fac7bdc08607 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jul 2012 16:44:40 +0200 Subject: [PATCH 224/688] add file --- files/materials/quad2.shader | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 files/materials/quad2.shader diff --git a/files/materials/quad2.shader b/files/materials/quad2.shader new file mode 100644 index 000000000..e54d83ef4 --- /dev/null +++ b/files/materials/quad2.shader @@ -0,0 +1,23 @@ +#include "core.h" + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + } + +#else + + SH_BEGIN_PROGRAM + shUniform(float3, viewportBackground) @shSharedParameter(viewportBackground) + shDeclareMrtOutput(1) + SH_START_PROGRAM + { + shOutputColour(0) = float4(viewportBackground, 1); + shOutputColour(1) = float4(1,1,1,1); + } + +#endif From acc5c3bbbf3e59baf55c536e189394d36511da01 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jul 2012 17:08:15 +0200 Subject: [PATCH 225/688] some fixes --- apps/openmw/mwrender/sky.cpp | 15 ++++++++++++++- apps/openmw/mwrender/sky.hpp | 8 ++++++-- apps/openmw/mwworld/physicssystem.cpp | 11 +++++------ files/materials/atmosphere.shader | 2 -- files/materials/clouds.shader | 2 +- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 25928cf3f..2a2df7943 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -57,6 +57,8 @@ BillboardObject::BillboardObject( const String& textureName, mMaterial = sh::Factory::getInstance().createMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount), material); mMaterial->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + sh::Factory::getInstance().getMaterialInstance ("BillboardMaterial"+StringConverter::toString(bodyCount))->setListener(this); + mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); bodyCount++; @@ -66,6 +68,16 @@ BillboardObject::BillboardObject() { } +void BillboardObject::requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration) +{ +} + +void BillboardObject::createdConfiguration (sh::MaterialInstance* m, const std::string& configuration) +{ + setVisibility(mVisibility); + setColour(mColour); +} + void BillboardObject::setVisible(const bool visible) { mBBSet->setVisible(visible); @@ -78,6 +90,7 @@ void BillboardObject::setSize(const float size) void BillboardObject::setVisibility(const float visibility) { + mVisibility = visibility; Ogre::MaterialPtr m = static_cast(mMaterial->getMaterial ())->getOgreMaterial (); for (int i=0; igetNumTechniques(); ++i) { @@ -110,6 +123,7 @@ void BillboardObject::setVisibilityFlags(int flags) void BillboardObject::setColour(const ColourValue& pColour) { + mColour = pColour; Ogre::MaterialPtr m = static_cast(mMaterial->getMaterial ())->getOgreMaterial (); for (int i=0; igetNumTechniques(); ++i) { @@ -117,7 +131,6 @@ void BillboardObject::setColour(const ColourValue& pColour) if (t->getNumPasses ()) t->getPass(0)->setSelfIllumination (pColour); } - //mMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(pColour); } void BillboardObject::setRenderQueue(unsigned int id) diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index d785c6eb6..9fdff3a01 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -27,7 +27,7 @@ namespace Ogre namespace MWRender { - class BillboardObject + class BillboardObject : public sh::MaterialInstanceListener { public: BillboardObject( const Ogre::String& textureName, @@ -38,6 +38,9 @@ namespace MWRender ); BillboardObject(); + void requestedConfiguration (sh::MaterialInstance* m, const std::string& configuration); + void createdConfiguration (sh::MaterialInstance* m, const std::string& configuration); + virtual ~BillboardObject() {} void setColour(const Ogre::ColourValue& pColour); @@ -53,7 +56,8 @@ namespace MWRender Ogre::SceneNode* getNode(); protected: - + float mVisibility; + Ogre::ColourValue mColour; Ogre::SceneNode* mNode; sh::MaterialInstance* mMaterial; Ogre::BillboardSet* mBBSet; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 105995aca..bf5c001db 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -181,16 +181,15 @@ namespace MWWorld playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() *-1 + 90; - Ogre::Quaternion quat = yawNode->getOrientation(); - Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); + Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); - pm_ref.rightmove = -iter->second.x; - pm_ref.forwardmove = -iter->second.y; - pm_ref.upmove = iter->second.z; + pm_ref.rightmove = -iter->second.x; + pm_ref.forwardmove = -iter->second.y; + pm_ref.upmove = iter->second.z; - } + } diff --git a/files/materials/atmosphere.shader b/files/materials/atmosphere.shader index 6e22b7ac3..295fa9376 100644 --- a/files/materials/atmosphere.shader +++ b/files/materials/atmosphere.shader @@ -28,8 +28,6 @@ SH_START_PROGRAM { shOutputColour(0) = colourPassthrough * atmosphereColour; - - shOutputColour(0) = float3(0,0,0,1); #if MRT shOutputColour(1) = float4(1,1,1,1); diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index aa53e6051..7677ecd95 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -43,7 +43,7 @@ float4 albedo = shSample(diffuseMap1, scrolledUV) * (1-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor; shOutputColour(0) = colourPassthrough * float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity); - shOutputColour(0) = float3(0,0,0,1); + #if MRT shOutputColour(1) = float4(1,1,1,1); #endif From 4bc93ecd1a9c851e5bff3d3670f0dd74e7d61201 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 20 Jul 2012 11:09:05 -0700 Subject: [PATCH 226/688] Use the skeleton name for the main animation --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index eea53e963..2aa7cd433 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -226,7 +226,7 @@ void loadResource(Ogre::Resource *resource) return; } - Ogre::Animation *anim = skel->createAnimation("default", maxtime); + Ogre::Animation *anim = skel->createAnimation(skel->getName(), maxtime); /* HACK: Pre-create the node tracks by matching the track IDs with the * bone IDs. Otherwise, Ogre animates the wrong bones. */ size_t bonecount = skel->getNumBones(); From 17a5c22c8ff25dd29d5a29ca333962269723f882 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jul 2012 23:31:49 +0200 Subject: [PATCH 227/688] don't use globbing --- cmake/OpenMWMacros.cmake | 7 ++-- files/CMakeLists.txt | 49 ++++++++++++++++++++++++--- files/mygui/CMakeLists.txt | 68 +++++++++++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 9 deletions(-) diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index c2567830d..e6f45fdb1 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -23,10 +23,9 @@ endforeach (u) source_group ("components\\${dir}" FILES ${files}) endmacro (add_component_dir) -macro (copy_all_files file_expression destination_dir) -file (GLOB ALL "${file_expression}") -foreach (f ${ALL}) +macro (copy_all_files source_dir destination_dir files) +foreach (f ${files}) get_filename_component(filename ${f} NAME) -configure_file(${f} ${destination_dir}/${filename} COPYONLY) +configure_file(${source_dir}/${f} ${destination_dir}/${filename} COPYONLY) endforeach (f) endmacro (copy_all_files) diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index ce8bc9fbd..3e99f5745 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -1,9 +1,50 @@ project(resources) -copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water/* "${OpenMW_BINARY_DIR}/resources/water/") +set(WATER_FILES + underwater_dome.mesh + water_nm.png +) -copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/* "${OpenMW_BINARY_DIR}/resources/gbuffer/") +set(GBUFFER_FILES + gbuffer.compositor +) -copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/shadows/* "${OpenMW_BINARY_DIR}/resources/shadows/") +set(MATERIAL_FILES + atmosphere.shader + atmosphere.shaderset + clouds.shader + clouds.shaderset + core.h + moon.shader + moon.shaderset + objects.mat + objects.shader + objects.shaderset + openmw.configuration + quad2.shader + quad.mat + quad.shader + quad.shaderset + shadowcaster.mat + shadowcaster.shader + shadowcaster.shaderset + shadows.h + sky.mat + stars.shader + stars.shaderset + sun.shader + sun.shaderset + terrain.shader + terrain.shaderset + underwater.h + water.mat + water.shader + water.shaderset -copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/materials/* "${OpenMW_BINARY_DIR}/resources/materials/") +) + +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water "${OpenMW_BINARY_DIR}/resources/water/" "${WATER_FILES}") + +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer "${OpenMW_BINARY_DIR}/resources/gbuffer/" "${GBUFFER_FILES}") + +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/materials "${OpenMW_BINARY_DIR}/resources/materials/" "${MATERIAL_FILES}") diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index b8a04a31c..ae007f023 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -3,4 +3,70 @@ set(SDIR ${CMAKE_CURRENT_SOURCE_DIR}) set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) -copy_all_files(${SDIR}/* ${DDIR}) +set(MYGUI_FILES + atlas1.cfg + bigbars.png + black.png + core.skin + core.xml + EBGaramond-Regular.ttf + mwgui.png + Obliviontt.zip + openmw_alchemy_window.layout + openmw_book.layout + openmw_box.skin.xml + openmw_button.skin.xml + openmw_chargen_birth.layout + openmw_chargen_class_description.layout + openmw_chargen_class.layout + openmw_chargen_create_class.layout + openmw_chargen_generate_class_result.layout + openmw_chargen_race.layout + openmw_chargen_review.layout + openmw_chargen_select_attribute.layout + openmw_chargen_select_skill.layout + openmw_chargen_select_specialization.layout + openmw_confirmation_dialog.layout + openmw_console.layout + openmw_console.skin.xml + openmw_container_window.layout + openmw_count_window.layout + openmw_dialogue_window.layout + openmw_dialogue_window_skin.xml + openmw_edit.skin.xml + openmw.font.xml + openmw_hud_box.skin.xml + openmw_hud_energybar.skin.xml + openmw_hud.layout + openmw_infobox.layout + openmw_interactive_messagebox.layout + openmw_inventory_window.layout + openmw_journal.layout + openmw_journal_skin.xml + openmw_layers.xml + openmw_list.skin.xml + openmw_mainmenu.layout + openmw_mainmenu_skin.xml + openmw_map_window.layout + openmw_map_window_skin.xml + openmw_messagebox.layout + openmw.pointer.xml + openmw_progress.skin.xml + openmw_resources.xml + openmw_scroll.layout + openmw_scroll_skin.xml + openmw_settings_window.layout + openmw_settings.xml + openmw_spell_window.layout + openmw_stats_window.layout + openmw_text_input.layout + openmw_text.skin.xml + openmw_tooltips.layout + openmw_trade_window.layout + openmw_windows.skin.xml + smallbars.png + VeraMono.ttf +) + + +copy_all_files(${CMAKE_CURRENT_SOURCE_DIR} ${DDIR} "${MYGUI_FILES}") From e7ab3544acc3fb5047998c7cdb13e15b56b2fce1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 21 Jul 2012 13:52:12 +0200 Subject: [PATCH 228/688] removed some redundant code --- apps/launcher/graphicspage.cpp | 4 ---- libs/openengine/ogre/renderer.cpp | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 8caa2b550..3c1d76f3d 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -79,11 +79,7 @@ bool GraphicsPage::setupOgre() try { -#if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) mOgre = new Ogre::Root("", "", "./launcherOgre.log"); -#else - mOgre = new Ogre::Root("", "", "./launcherOgre.log"); -#endif } catch(Ogre::Exception &ex) { diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 7e609faa8..e40bdf708 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -88,11 +88,10 @@ void OgreRenderer::configure(const std::string &logPath, // Disable logging log->setDebugOutputEnabled(false); + mRoot = new Root("", "", ""); + #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) - mRoot = new Root("", "", ""); loadPlugins(); - #else - mRoot = new Root("", "", ""); #endif std::string pluginDir; From fcaa8aae06d791c37daab0897f4fd19f6b2129b5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Jul 2012 11:26:09 -0700 Subject: [PATCH 229/688] Don't skip animation state updates for NPCs --- apps/openmw/mwrender/npcanimation.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 4d2ca557d..f66ab8403 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -113,8 +113,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere } } base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones - //stay in the same place when we skipanim, or open a gui window } if(mEntityList.mSkelBase) From 81ce8dbe122db57110a32996975356bb057e0e91 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Jul 2012 14:41:26 -0700 Subject: [PATCH 230/688] Combine animation handling into the base class --- apps/openmw/mwrender/animation.cpp | 29 ++++++++++++++++++++++ apps/openmw/mwrender/animation.hpp | 2 +- apps/openmw/mwrender/creatureanimation.cpp | 25 ++----------------- apps/openmw/mwrender/npcanimation.cpp | 25 +------------------ 4 files changed, 33 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 1f0a99518..88f6cebe6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -42,4 +42,33 @@ void Animation::skipAnim() mAnimate = 0; } +void Animation::runAnimation(float timepassed) +{ + if(mAnimate != 0) + { + mTime += timepassed; + + if(mEntityList.mSkelBase) + { + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) + { + Ogre::AnimationState *state = as.getNext(); + state->setTimePosition(mTime); + if(mTime >= state->getLength()) + { + if(mAnimate != -1) + mAnimate--; + //std::cout << "Stopping the animation\n"; + if(mAnimate == 0) + mTime = state->getLength(); + else + mTime = mTime - state->getLength(); + } + } + } + } +} + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 0ed4545f2..5de314df1 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -36,7 +36,7 @@ public: void playGroup(std::string groupname, int mode, int loops); void skipAnim(); - virtual void runAnimation(float timepassed) = 0; + virtual void runAnimation(float timepassed); }; } diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index ee1fd45ba..9d2a58a1e 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -69,30 +69,9 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O void CreatureAnimation::runAnimation(float timepassed) { - if(mAnimate > 0) - { - mTime += timepassed; + // Placeholder - if(mEntityList.mSkelBase) - { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - state->setTimePosition(mTime); - if(state->getTimePosition() >= state->getLength()) - { - mAnimate--; - //std::cout << "Stopping the animation\n"; - if(mAnimate == 0) - mTime = state->getLength(); - else - mTime = mTime - state->getLength(); - } - } - } - } + Animation::runAnimation(timepassed); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index f66ab8403..3cca110e3 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -373,30 +373,7 @@ void NpcAnimation::runAnimation(float timepassed) } timeToChange += timepassed; - if(mAnimate > 0) - { - mTime += timepassed; - - if(mEntityList.mSkelBase) - { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - state->setTimePosition(mTime); - if(state->getTimePosition() >= state->getLength()) - { - mAnimate--; - //std::cout << "Stopping the animation\n"; - if(mAnimate == 0) - mTime = state->getLength(); - else - mTime = mTime - state->getLength(); - } - } - } - } + Animation::runAnimation(timepassed); } void NpcAnimation::removeEntities(NifOgre::EntityList &entities) From c5b9098517714827d575166de0163aaed26e1398 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Jul 2012 17:09:16 -0700 Subject: [PATCH 231/688] Remove an unused field from EntityList --- apps/openmw/mwrender/npcanimation.cpp | 1 - components/nifogre/ogre_nif_loader.cpp | 1 - components/nifogre/ogre_nif_loader.hpp | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 3cca110e3..e89122e4b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -388,7 +388,6 @@ void NpcAnimation::removeEntities(NifOgre::EntityList &entities) } entities.mEntities.clear(); entities.mSkelBase = NULL; - entities.mRootNode = NULL; } void NpcAnimation::removeIndividualPart(int type) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 2aa7cd433..2c5b7e324 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1027,7 +1027,6 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string if(meshes.size() == 0) return entitylist; - entitylist.mRootNode = parent; Ogre::SceneManager *sceneMgr = parent->getCreator(); for(size_t i = 0;i < meshes.size();i++) { diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 76e94975c..a9195f2fb 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -62,9 +62,8 @@ namespace NifOgre struct EntityList { std::vector mEntities; Ogre::Entity *mSkelBase; - Ogre::SceneNode *mRootNode; - EntityList() : mSkelBase(0), mRootNode(0) + EntityList() : mSkelBase(0) { } }; From e81fc42daaf0adc5ae296b7781aea4270f299b9d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Jul 2012 17:12:41 -0700 Subject: [PATCH 232/688] Remove the beast-race special cases from updateParts The special handling should happen at a much lower level, and prevent the objects from being equipped in the first place. --- apps/openmw/mwrender/npcanimation.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index e89122e4b..415de5859 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -149,7 +149,7 @@ void NpcAnimation::updateParts() { &greaves, MWWorld::InventoryStore::Slot_Greaves }, { &leftpauldron, MWWorld::InventoryStore::Slot_LeftPauldron }, { &rightpauldron, MWWorld::InventoryStore::Slot_RightPauldron }, - { &boots, MWWorld::InventoryStore::Slot_Boots }, // !isBeast + { &boots, MWWorld::InventoryStore::Slot_Boots }, { &leftglove, MWWorld::InventoryStore::Slot_LeftGauntlet }, { &rightglove, MWWorld::InventoryStore::Slot_RightGauntlet }, { &shirt, MWWorld::InventoryStore::Slot_Shirt }, @@ -157,9 +157,6 @@ void NpcAnimation::updateParts() }; for(size_t i = 0;i < sizeof(slotlist)/sizeof(slotlist[0]);i++) { - if(slotlist[i].iter == &boots && isBeast) - continue; - MWWorld::ContainerStoreIterator iter = mInv.getSlot(slotlist[i].slot); if(*slotlist[i].iter != iter) { @@ -235,7 +232,7 @@ void NpcAnimation::updateParts() std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); } - if(!isBeast && boots != mInv.end()) + if(boots != mInv.end()) { if(boots->getTypeName() == typeid(ESM::Clothing).name()) { From 77446a0d58fde458f1ba01788f2854e1e2b1b558 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Jul 2012 17:39:57 -0700 Subject: [PATCH 233/688] Fix skipAnim, only skip one animation update --- apps/openmw/mwrender/animation.cpp | 6 ++++-- apps/openmw/mwrender/animation.hpp | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 88f6cebe6..653a506e8 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -16,6 +16,7 @@ Animation::Animation(OEngine::Render::OgreRenderer& _rend) , mRend(_rend) , mTime(0.0f) , mAnimate(0) + , mSkipFrame(false) { } @@ -39,12 +40,12 @@ void Animation::playGroup(std::string groupname, int mode, int loops) void Animation::skipAnim() { - mAnimate = 0; + mSkipFrame = true; } void Animation::runAnimation(float timepassed) { - if(mAnimate != 0) + if(mAnimate != 0 && !mSkipFrame) { mTime += timepassed; @@ -69,6 +70,7 @@ void Animation::runAnimation(float timepassed) } } } + mSkipFrame = false; } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 5de314df1..ae1477666 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -27,6 +27,7 @@ protected: float mTime; int mAnimate; + bool mSkipFrame; NifOgre::EntityList mEntityList; From 6bfcf2bc1b84e888e11742c33547e001e5835036 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jul 2012 02:45:39 +0200 Subject: [PATCH 234/688] - exchanged the preprocessor again, no warnings now - disable line directives for now, causing some trouble --- extern/shiny | 2 +- files/materials/objects.shader | 2 +- files/materials/terrain.shader | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/extern/shiny b/extern/shiny index 7af50bf44..34c098c63 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 7af50bf4463a828ee0e8cb72c93445c793864bf9 +Subproject commit 34c098c636659b63e0ce85d371f4933c27191753 diff --git a/files/materials/objects.shader b/files/materials/objects.shader index eafce7af6..39c9b42e0 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -132,7 +132,7 @@ shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) #endif -#ifdef HAS_VERTEXCOLOR +#if HAS_VERTEXCOLOR shInput(float4, colourPassthrough) #endif diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 24771ca41..e0187959f 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -247,10 +247,7 @@ // first layer of first pass doesn't need a blend map albedo = shSample(diffuseMap0, UV * 10).rgb; #else - #define BLEND_AMOUNT blendValues@shPropertyString(blendmap_component_@shIterator) - - - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, BLEND_AMOUNT); + albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); #endif @shEndForeach From d8cb6855437813e1145f213c3cb83f43b0580923 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Jul 2012 18:03:01 -0700 Subject: [PATCH 235/688] Interpolate keyframes when creating them Probably not fully correct, but better than nothing. --- components/nifogre/ogre_nif_loader.cpp | 28 ++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 2c5b7e324..e695cd959 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -306,10 +306,30 @@ void loadResource(Ogre::Resource *resource) Ogre::TransformKeyFrame *kframe; kframe = nodetrack->createNodeKeyFrame(curtime); - // FIXME: These should be interpolated since they don't all fall on the same time - kframe->setRotation(curquat); - kframe->setTranslate(curtrans); - kframe->setScale(curscale); + if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) + kframe->setRotation(curquat); + else + { + QuaternionKeyList::VecType::const_iterator last = quatiter-1; + float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); + kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); + } + if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) + kframe->setTranslate(curtrans); + else + { + Vector3KeyList::VecType::const_iterator last = traniter-1; + float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); + kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); + } + if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) + kframe->setScale(curscale); + else + { + FloatKeyList::VecType::const_iterator last = scaleiter-1; + float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); + kframe->setScale(lastscale + ((curscale-lastscale)*diff)); + } } } anim->optimise(); From 4035d7370e5288898d1e590030b1aa4f853bad28 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 21 Jul 2012 22:04:05 -0700 Subject: [PATCH 236/688] Fix name/filter comparison --- components/nifogre/ogre_nif_loader.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index e695cd959..c34f69404 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -25,6 +25,8 @@ #include "ogre_nif_loader.hpp" +#include + #include #include #include @@ -1080,6 +1082,11 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string return entitylist; } +static bool checklow(const char &a, const char &b) +{ + return ::tolower(a) == ::tolower(b); +} + EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, const std::string &name, @@ -1099,8 +1106,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(ent->hasSkeleton()) { if(meshes[i].second.length() < filter.length() || - !boost::algorithm::lexicographical_compare(meshes[i].second.substr(0, filter.length()), - filter, boost::algorithm::is_iequal())) + std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow).first != filter.end()) { sceneMgr->destroyEntity(ent); meshes.erase(meshes.begin()+i); From 1dde806addefa39bd4779b7e3c4954b355fc7c69 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 22 Jul 2012 14:52:55 +0300 Subject: [PATCH 237/688] Fixes #313: openmw without a ~/.config/openmw folder segfault. Added creation of $HOME/.config/openmw directory. Signed-off-by: Lukasz Gromanowski --- components/files/configurationmanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 150a4fcd8..dee51b0c1 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -27,6 +27,8 @@ ConfigurationManager::ConfigurationManager() { setupTokensMapping(); + boost::filesystem::create_directories(mFixedPath.getUserPath()); + mPluginsCfgPath = mFixedPath.getLocalPath() / pluginsCfgFile; if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) { From b2dcf5adcdcfc73c8b61e782e7153facc03a6b77 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jul 2012 14:41:23 +0200 Subject: [PATCH 238/688] support system install for boost wave --- CMakeLists.txt | 13 ++++++++++++- extern/shiny | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7e23a98d..c0b5bf451 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,9 +186,20 @@ if (UNIX AND NOT APPLE) find_package (Threads) endif() +set(BOOST_COMPONENTS system filesystem program_options thread) + +if (Boost_VERSION LESS 104900) + set(SHINY_USE_WAVE_SYSTEM_INSTALL TRUE) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} wave) +else() + set(SHINY_USE_WAVE_SYSTEM_INSTALL FALSE) +endif() + +MESSAGE(STATUS ${BOOST_COMPONENTS}) + find_package(OGRE REQUIRED) find_package(MyGUI REQUIRED) -find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread) +find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) diff --git a/extern/shiny b/extern/shiny index 34c098c63..fdc92a424 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 34c098c636659b63e0ce85d371f4933c27191753 +Subproject commit fdc92a4243c98f5d86a33cbdf2ed0fbb94dad3f6 From 32b1350b63e7fe8ee99d316f822df3cc8ffd978e Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 22 Jul 2012 18:29:54 +0400 Subject: [PATCH 239/688] CreatureStats class --- apps/openmw/mwclass/creature.cpp | 38 +-- apps/openmw/mwclass/npc.cpp | 40 +-- apps/openmw/mwgui/spellwindow.cpp | 8 +- apps/openmw/mwmechanics/actors.cpp | 54 ++-- apps/openmw/mwmechanics/creaturestats.cpp | 48 ++++ apps/openmw/mwmechanics/creaturestats.hpp | 257 ++++++++++++++++++- apps/openmw/mwmechanics/mechanicsmanager.cpp | 64 ++--- apps/openmw/mwmechanics/spellsuccess.hpp | 10 +- apps/openmw/mwscript/aiextensions.cpp | 8 +- apps/openmw/mwscript/statsextensions.cpp | 73 +++--- 10 files changed, 462 insertions(+), 138 deletions(-) create mode 100644 apps/openmw/mwmechanics/creaturestats.cpp diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a5a4f337a..83370478f 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -47,24 +47,24 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); // creature stats - data->mCreatureStats.mAttributes[0].set (ref->base->data.strength); - data->mCreatureStats.mAttributes[1].set (ref->base->data.intelligence); - data->mCreatureStats.mAttributes[2].set (ref->base->data.willpower); - data->mCreatureStats.mAttributes[3].set (ref->base->data.agility); - data->mCreatureStats.mAttributes[4].set (ref->base->data.speed); - data->mCreatureStats.mAttributes[5].set (ref->base->data.endurance); - data->mCreatureStats.mAttributes[6].set (ref->base->data.personality); - data->mCreatureStats.mAttributes[7].set (ref->base->data.luck); - data->mCreatureStats.mDynamic[0].set (ref->base->data.health); - data->mCreatureStats.mDynamic[1].set (ref->base->data.mana); - data->mCreatureStats.mDynamic[2].set (ref->base->data.fatigue); + data->mCreatureStats.getAttribute(0).set (ref->base->data.strength); + data->mCreatureStats.getAttribute(1).set (ref->base->data.intelligence); + data->mCreatureStats.getAttribute(2).set (ref->base->data.willpower); + data->mCreatureStats.getAttribute(3).set (ref->base->data.agility); + data->mCreatureStats.getAttribute(4).set (ref->base->data.speed); + data->mCreatureStats.getAttribute(5).set (ref->base->data.endurance); + data->mCreatureStats.getAttribute(6).set (ref->base->data.personality); + data->mCreatureStats.getAttribute(7).set (ref->base->data.luck); + data->mCreatureStats.getHealth().set (ref->base->data.health); + data->mCreatureStats.getMagicka().set (ref->base->data.mana); + data->mCreatureStats.getFatigue().set (ref->base->data.fatigue); - data->mCreatureStats.mLevel = ref->base->data.level; + data->mCreatureStats.setLevel(ref->base->data.level); - data->mCreatureStats.mHello = ref->base->AI.hello; - data->mCreatureStats.mFight = ref->base->AI.fight; - data->mCreatureStats.mFlee = ref->base->AI.flee; - data->mCreatureStats.mAlarm = ref->base->AI.alarm; + data->mCreatureStats.setHello(ref->base->AI.hello); + data->mCreatureStats.setFight(ref->base->AI.fight); + data->mCreatureStats.setFlee(ref->base->AI.flee); + data->mCreatureStats.setAlarm(ref->base->AI.alarm); // store ptr.getRefData().setCustomData (data.release()); @@ -169,7 +169,7 @@ namespace MWClass float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return stats.mAttributes[0].getModified()*5; + return stats.getAttribute(0).getModified()*5; } float Creature::getEncumbrance (const MWWorld::Ptr& ptr) const @@ -178,9 +178,9 @@ namespace MWClass const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - weight -= stats.mMagicEffects.get (MWMechanics::EffectKey (8)).mMagnitude; // feather + weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather - weight += stats.mMagicEffects.get (MWMechanics::EffectKey (7)).mMagnitude; // burden + weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden if (weight<0) weight = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ffe7dec61..80bff73fa 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -81,29 +81,29 @@ namespace MWClass for (int i=0; i<27; ++i) data->mNpcStats.getSkill (i).setBase (ref->base->npdt52.skills[i]); - data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength); - data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence); - data->mCreatureStats.mAttributes[2].set (ref->base->npdt52.willpower); - data->mCreatureStats.mAttributes[3].set (ref->base->npdt52.agility); - data->mCreatureStats.mAttributes[4].set (ref->base->npdt52.speed); - data->mCreatureStats.mAttributes[5].set (ref->base->npdt52.endurance); - data->mCreatureStats.mAttributes[6].set (ref->base->npdt52.personality); - data->mCreatureStats.mAttributes[7].set (ref->base->npdt52.luck); - data->mCreatureStats.mDynamic[0].set (ref->base->npdt52.health); - data->mCreatureStats.mDynamic[1].set (ref->base->npdt52.mana); - data->mCreatureStats.mDynamic[2].set (ref->base->npdt52.fatigue); + data->mCreatureStats.getAttribute(0).set (ref->base->npdt52.strength); + data->mCreatureStats.getAttribute(1).set (ref->base->npdt52.intelligence); + data->mCreatureStats.getAttribute(2).set (ref->base->npdt52.willpower); + data->mCreatureStats.getAttribute(3).set (ref->base->npdt52.agility); + data->mCreatureStats.getAttribute(4).set (ref->base->npdt52.speed); + data->mCreatureStats.getAttribute(5).set (ref->base->npdt52.endurance); + data->mCreatureStats.getAttribute(6).set (ref->base->npdt52.personality); + data->mCreatureStats.getAttribute(7).set (ref->base->npdt52.luck); + data->mCreatureStats.getHealth().set (ref->base->npdt52.health); + data->mCreatureStats.getMagicka().set (ref->base->npdt52.mana); + data->mCreatureStats.getFatigue().set (ref->base->npdt52.fatigue); - data->mCreatureStats.mLevel = ref->base->npdt52.level; + data->mCreatureStats.setLevel(ref->base->npdt52.level); } else { /// \todo do something with npdt12 maybe:p } - data->mCreatureStats.mHello = ref->base->AI.hello; - data->mCreatureStats.mFight = ref->base->AI.fight; - data->mCreatureStats.mFlee = ref->base->AI.flee; - data->mCreatureStats.mAlarm = ref->base->AI.alarm; + data->mCreatureStats.setHello(ref->base->AI.hello); + data->mCreatureStats.setFight(ref->base->AI.fight); + data->mCreatureStats.setFlee(ref->base->AI.flee); + data->mCreatureStats.setAlarm(ref->base->AI.alarm); // store ptr.getRefData().setCustomData (data.release()); @@ -330,7 +330,7 @@ namespace MWClass float Npc::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return stats.mAttributes[0].getModified()*5; + return stats.getAttribute(0).getModified()*5; } float Npc::getEncumbrance (const MWWorld::Ptr& ptr) const @@ -339,9 +339,9 @@ namespace MWClass const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - weight -= stats.mMagicEffects.get (MWMechanics::EffectKey (8)).mMagnitude; // feather + weight -= stats.getMagicEffects().get (MWMechanics::EffectKey (8)).mMagnitude; // feather - weight += stats.mMagicEffects.get (MWMechanics::EffectKey (7)).mMagnitude; // burden + weight += stats.getMagicEffects().get (MWMechanics::EffectKey (7)).mMagnitude; // burden if (weight<0) weight = 0; @@ -356,7 +356,7 @@ namespace MWClass /// \todo consider instant effects - return stats.mActiveSpells.addSpell (id); + return stats.getActiveSpells().addSpell (id); } void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index f6e7d3b77..8f81a1761 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -81,7 +81,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& spells = stats.mSpells; + MWMechanics::Spells& spells = stats.getSpells(); // the following code switches between selected enchanted item and selected spell (only one of these // can be active at a time) @@ -333,7 +333,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::Spells& spells = stats.mSpells; + MWMechanics::Spells& spells = stats.getSpells(); MWWorld::Ptr item = *_sender->getUserData(); // retrieve ContainerStoreIterator to the item @@ -397,7 +397,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); - MWMechanics::Spells& spells = stats.mSpells; + MWMechanics::Spells& spells = stats.getSpells(); if (MyGUI::InputManager::getInstance().isShiftPressed()) { @@ -451,7 +451,7 @@ namespace MWGui { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& spells = stats.mSpells; + MWMechanics::Spells& spells = stats.getSpells(); if (spells.getSelectedSpell() == mSpellToDelete) { diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ff3e91da8..e1c5f855f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -33,7 +33,7 @@ namespace MWMechanics { CreatureStats& creatureStats = MWWorld::Class::get (creature).getCreatureStats (creature); - MagicEffects now = creatureStats.mSpells.getMagicEffects(); + MagicEffects now = creatureStats.getSpells().getMagicEffects(); if (creature.getTypeName()==typeid (ESM::NPC).name()) { @@ -41,11 +41,11 @@ namespace MWMechanics now += store.getMagicEffects(); } - now += creatureStats.mActiveSpells.getMagicEffects(); + now += creatureStats.getActiveSpells().getMagicEffects(); - MagicEffects diff = MagicEffects::diff (creatureStats.mMagicEffects, now); + MagicEffects diff = MagicEffects::diff (creatureStats.getMagicEffects(), now); - creatureStats.mMagicEffects = now; + creatureStats.setMagicEffects(now); // TODO apply diff to other stats } @@ -54,18 +54,22 @@ namespace MWMechanics { CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - int strength = creatureStats.mAttributes[0].getBase(); - int intelligence = creatureStats.mAttributes[1].getBase(); - int willpower = creatureStats.mAttributes[2].getBase(); - int agility = creatureStats.mAttributes[3].getBase(); - int endurance = creatureStats.mAttributes[5].getBase(); + int strength = creatureStats.getAttribute(0).getBase(); + int intelligence = creatureStats.getAttribute(1).getBase(); + int willpower = creatureStats.getAttribute(2).getBase(); + int agility = creatureStats.getAttribute(3).getBase(); + int endurance = creatureStats.getAttribute(5).getBase(); - double magickaFactor = creatureStats.mMagicEffects.get (EffectKey (84)).mMagnitude*0.1 + 0.5; + double magickaFactor = + creatureStats.getMagicEffects().get (EffectKey (84)).mMagnitude * 0.1 + 0.5; - creatureStats.mDynamic[0].setBase (static_cast (0.5 * (strength + endurance))); - creatureStats.mDynamic[1].setBase (static_cast (intelligence + - magickaFactor * intelligence)); - creatureStats.mDynamic[2].setBase (strength+willpower+agility+endurance); + creatureStats.getHealth().setBase( + static_cast (0.5 * (strength + endurance))); + + creatureStats.getMagicka().setBase( + static_cast (intelligence + magickaFactor * intelligence)); + + creatureStats.getFatigue().setBase(strength+willpower+agility+endurance); } void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr) @@ -75,20 +79,24 @@ namespace MWMechanics // attributes for (int i=0; i<5; ++i) { - int modifier = creatureStats.mMagicEffects.get (EffectKey (79, i)).mMagnitude - - creatureStats.mMagicEffects.get (EffectKey (17, i)).mMagnitude; + int modifier = + creatureStats.getMagicEffects().get (EffectKey (79, i)).mMagnitude; - creatureStats.mAttributes[i].setModifier (modifier); + modifier -= creatureStats.getMagicEffects().get (EffectKey (17, i)).mMagnitude; + + creatureStats.getAttribute(i).setModifier (modifier); } // dynamic stats - for (int i=0; i<3; ++i) - { - int modifier = creatureStats.mMagicEffects.get (EffectKey (80+i)).mMagnitude - - creatureStats.mMagicEffects.get (EffectKey (18+i)).mMagnitude; + MagicEffects effects = creatureStats.getMagicEffects(); + creatureStats.getHealth().setModifier( + effects.get(EffectKey(80)).mMagnitude - effects.get(EffectKey(18)).mMagnitude); - creatureStats.mDynamic[i].setModifier (modifier); - } + creatureStats.getMagicka().setModifier( + effects.get(EffectKey(81)).mMagnitude - effects.get(EffectKey(19)).mMagnitude); + + creatureStats.getFatigue().setModifier( + effects.get(EffectKey(82)).mMagnitude - effects.get(EffectKey(20)).mMagnitude); } Actors::Actors() : mDuration (0) {} diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp new file mode 100644 index 000000000..38d2442fa --- /dev/null +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -0,0 +1,48 @@ +#include "creaturestats.hpp" + +namespace MWMechanics +{ + CreatureStats::CreatureStats() + {} + + // Can't use all benefits of members initialization because of + // lack of copy constructors + CreatureStats::CreatureStats(const CreatureStats &orig) + : mLevel(orig.mLevel), mHello(orig.mHello), mFight(orig.mFight), + mFlee(orig.mFlee), mAlarm(orig.mAlarm) + { + for (int i = 0; i < 8; ++i) { + mAttributes[i] = orig.mAttributes[i]; + } + for (int i = 0; i < 3; ++i) { + mDynamic[i] = orig.mDynamic[i]; + } + mSpells = orig.mSpells; + mActiveSpells = orig.mActiveSpells; + mMagicEffects = orig.mMagicEffects; + } + + CreatureStats::~CreatureStats() + {} + + const CreatureStats & + CreatureStats::operator=(const CreatureStats &orig) + { + for (int i = 0; i < 8; ++i) { + mAttributes[i] = orig.mAttributes[i]; + } + for (int i = 0; i < 3; ++i) { + mDynamic[i] = orig.mDynamic[i]; + } + mLevel = orig.mLevel; + mSpells = orig.mSpells; + mActiveSpells = orig.mActiveSpells; + mMagicEffects = orig.mMagicEffects; + mHello = orig.mHello; + mFight = orig.mFight; + mFlee = orig.mFlee; + mAlarm = orig.mAlarm; + + return *this; + } +} diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 8d40e1942..9d69c868f 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "stat.hpp" #include "magiceffects.hpp" @@ -11,7 +12,10 @@ namespace MWMechanics { - struct CreatureStats + /// \brief Common creature stats + /// + /// + class CreatureStats { Stat mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue @@ -23,7 +27,258 @@ namespace MWMechanics int mFight; int mFlee; int mAlarm; + + public: + CreatureStats(); + CreatureStats(const CreatureStats &); + virtual ~CreatureStats(); + + const CreatureStats & operator=(const CreatureStats &); + + const Stat & getAttribute(int index) const; + + const DynamicStat & getHealth() const; + + const DynamicStat & getMagicka() const; + + const DynamicStat & getFatigue() const; + + const Spells & getSpells() const; + + const ActiveSpells & getActiveSpells() const; + + const MagicEffects & getMagicEffects() const; + + int getLevel() const; + + int getHello() const; + + int getFight() const; + + int getFlee() const; + + int getAlarm() const; + + + Stat & getAttribute(int index); + + DynamicStat & getHealth(); + + DynamicStat & getMagicka(); + + DynamicStat & getFatigue(); + + DynamicStat & getDynamic(int index); + + Spells & getSpells(); + + ActiveSpells & getActiveSpells(); + + MagicEffects & getMagicEffects(); + + + void setAttribute(int index, const Stat &value); + + void setHealth(const DynamicStat &value); + + void setMagicka(const DynamicStat &value); + + void setFatigue(const DynamicStat &value); + + void setSpells(const Spells &spells); + + void setActiveSpells(const ActiveSpells &active); + + void setMagicEffects(const MagicEffects &effects); + + void setLevel(int level); + + void setHello(int value); + + void setFight(int value); + + void setFlee(int value); + + void setAlarm(int value); }; + + // Inline const getters + + inline const Stat & + CreatureStats::getAttribute(int index) const { + if (index < 0 || index > 7) { + throw std::runtime_error("attribute index is out of range"); + } + return mAttributes[index]; + } + + inline const DynamicStat & + CreatureStats::getHealth() const { + return mDynamic[0]; + } + + inline const DynamicStat & + CreatureStats::getMagicka() const { + return mDynamic[1]; + } + + inline const DynamicStat & + CreatureStats::getFatigue() const { + return mDynamic[2]; + } + + inline const Spells & + CreatureStats::getSpells() const { + return mSpells; + } + + inline const ActiveSpells & + CreatureStats::getActiveSpells() const { + return mActiveSpells; + } + + inline const MagicEffects & + CreatureStats::getMagicEffects() const { + return mMagicEffects; + } + + inline int + CreatureStats::getLevel() const { + return mLevel; + } + + inline int + CreatureStats::getHello() const { + return mHello; + } + + inline int + CreatureStats::getFight() const { + return mFight; + } + + inline int + CreatureStats::getFlee() const { + return mFlee; + } + + inline int + CreatureStats::getAlarm() const { + return mAlarm; + } + + // Inline non-const getters + + inline Stat & + CreatureStats::getAttribute(int index) { + if (index < 0 || index > 7) { + throw std::runtime_error("attribute index is out of range"); + } + return mAttributes[index]; + } + + inline DynamicStat & + CreatureStats::getHealth() { + return mDynamic[0]; + } + + inline DynamicStat & + CreatureStats::getMagicka() { + return mDynamic[1]; + } + + inline DynamicStat & + CreatureStats::getFatigue() { + return mDynamic[2]; + } + + inline DynamicStat & + CreatureStats::getDynamic(int index) { + if (index < 0 || index > 2) { + throw std::runtime_error("dynamic stat index is out of range"); + } + return mDynamic[index]; + } + + inline Spells & + CreatureStats::getSpells() { + return mSpells; + } + + inline void + CreatureStats::setSpells(const Spells &spells) { + mSpells = spells; + } + + inline ActiveSpells & + CreatureStats::getActiveSpells() { + return mActiveSpells; + } + + inline MagicEffects & + CreatureStats::getMagicEffects() { + return mMagicEffects; + } + + // Inline setters + + inline void + CreatureStats::setAttribute(int index, const Stat &value) { + if (index < 0 || index > 7) { + throw std::runtime_error("attribute index is out of range"); + } + mAttributes[index] = value; + } + + inline void + CreatureStats::setHealth(const DynamicStat &value) { + mDynamic[0] = value; + } + + inline void + CreatureStats::setMagicka(const DynamicStat &value) { + mDynamic[1] = value; + } + + inline void + CreatureStats::setFatigue(const DynamicStat &value) { + mDynamic[2] = value; + } + + inline void + CreatureStats::setLevel(int level) { + mLevel = level; + } + + inline void + CreatureStats::setActiveSpells(const ActiveSpells &active) { + mActiveSpells = active; + } + + inline void + CreatureStats::setMagicEffects(const MagicEffects &effects) { + mMagicEffects = effects; + } + + inline void + CreatureStats::setHello(int value) { + mHello = value; + } + + inline void + CreatureStats::setFight(int value) { + mFight = value; + } + + inline void + CreatureStats::setFlee(int value) { + mFlee = value; + } + + inline void + CreatureStats::setAlarm(int value) { + mAlarm = value; + } } #endif diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanager.cpp index ada05a11b..fe5485d61 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.cpp @@ -23,21 +23,21 @@ namespace MWMechanics const ESM::NPC *player = ptr.get()->base; // reset - creatureStats.mLevel = player->npdt52.level; - creatureStats.mSpells.clear(); - creatureStats.mMagicEffects = MagicEffects(); + creatureStats.setLevel(player->npdt52.level); + creatureStats.getSpells().clear(); + creatureStats.setMagicEffects(MagicEffects()); for (int i=0; i<27; ++i) npcStats.getSkill (i).setBase (player->npdt52.skills[i]); - creatureStats.mAttributes[0].setBase (player->npdt52.strength); - creatureStats.mAttributes[1].setBase (player->npdt52.intelligence); - creatureStats.mAttributes[2].setBase (player->npdt52.willpower); - creatureStats.mAttributes[3].setBase (player->npdt52.agility); - creatureStats.mAttributes[4].setBase (player->npdt52.speed); - creatureStats.mAttributes[5].setBase (player->npdt52.endurance); - creatureStats.mAttributes[6].setBase (player->npdt52.personality); - creatureStats.mAttributes[7].setBase (player->npdt52.luck); + creatureStats.getAttribute(0).setBase (player->npdt52.strength); + creatureStats.getAttribute(1).setBase (player->npdt52.intelligence); + creatureStats.getAttribute(2).setBase (player->npdt52.willpower); + creatureStats.getAttribute(3).setBase (player->npdt52.agility); + creatureStats.getAttribute(4).setBase (player->npdt52.speed); + creatureStats.getAttribute(5).setBase (player->npdt52.endurance); + creatureStats.getAttribute(6).setBase (player->npdt52.personality); + creatureStats.getAttribute(7).setBase (player->npdt52.luck); // race if (mRaceSelected) @@ -63,7 +63,7 @@ namespace MWMechanics case 7: attribute = &race->data.luck; break; } - creatureStats.mAttributes[i].setBase ( + creatureStats.getAttribute(i).setBase ( static_cast (male ? attribute->male : attribute->female)); } @@ -81,7 +81,7 @@ namespace MWMechanics for (std::vector::const_iterator iter (race->powers.list.begin()); iter!=race->powers.list.end(); ++iter) { - creatureStats.mSpells.add (*iter); + creatureStats.getSpells().add (*iter); } } @@ -95,7 +95,7 @@ namespace MWMechanics for (std::vector::const_iterator iter (sign->powers.list.begin()); iter!=sign->powers.list.end(); ++iter) { - creatureStats.mSpells.add (*iter); + creatureStats.getSpells().add (*iter); } } @@ -109,8 +109,8 @@ namespace MWMechanics int attribute = class_.data.attribute[i]; if (attribute>=0 && attribute<8) { - creatureStats.mAttributes[attribute].setBase ( - creatureStats.mAttributes[attribute].getBase() + 10); + creatureStats.getAttribute(attribute).setBase ( + creatureStats.getAttribute(attribute).getBase() + 10); } } @@ -151,8 +151,9 @@ namespace MWMechanics // forced update and current value adjustments mActors.updateActor (ptr, 0); - for (int i=0; i<3; ++i) - creatureStats.mDynamic[i].setCurrent (creatureStats.mDynamic[i].getModified()); + creatureStats.getHealth().setCurrent(creatureStats.getHealth().getModified()); + creatureStats.getMagicka().setCurrent(creatureStats.getMagicka().getModified()); + creatureStats.getFatigue().setCurrent(creatureStats.getFatigue().getModified()); } @@ -213,22 +214,25 @@ namespace MWMechanics for (int i=0; i<8; ++i) { - if (stats.mAttributes[i]!=mWatchedCreature.mAttributes[i]) + if (stats.getAttribute(i)!=mWatchedCreature.getAttribute(i)) { - mWatchedCreature.mAttributes[i] = stats.mAttributes[i]; + mWatchedCreature.setAttribute(i, stats.getAttribute(i)); - MWBase::Environment::get().getWindowManager()->setValue (attributeNames[i], stats.mAttributes[i]); + MWBase::Environment::get().getWindowManager()->setValue (attributeNames[i], stats.getAttribute(i)); } } - for (int i=0; i<3; ++i) - { - if (stats.mDynamic[i]!=mWatchedCreature.mDynamic[i]) - { - mWatchedCreature.mDynamic[i] = stats.mDynamic[i]; - - MWBase::Environment::get().getWindowManager()->setValue (dynamicNames[i], stats.mDynamic[i]); - } + if (stats.getHealth() != mWatchedCreature.getHealth()) { + mWatchedCreature.setHealth(stats.getHealth()); + MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[0], stats.getHealth()); + } + if (stats.getMagicka() != mWatchedCreature.getMagicka()) { + mWatchedCreature.setMagicka(stats.getMagicka()); + MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[1], stats.getMagicka()); + } + if (stats.getFatigue() != mWatchedCreature.getFatigue()) { + mWatchedCreature.setFatigue(stats.getFatigue()); + MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[2], stats.getFatigue()); } bool update = false; @@ -247,7 +251,7 @@ namespace MWMechanics if (update) MWBase::Environment::get().getWindowManager()->updateSkillArea(); - MWBase::Environment::get().getWindowManager()->setValue ("level", stats.mLevel); + MWBase::Environment::get().getWindowManager()->setValue ("level", stats.getLevel()); } if (mUpdatePlayer) diff --git a/apps/openmw/mwmechanics/spellsuccess.hpp b/apps/openmw/mwmechanics/spellsuccess.hpp index 42a7d5bba..1ab1bb11f 100644 --- a/apps/openmw/mwmechanics/spellsuccess.hpp +++ b/apps/openmw/mwmechanics/spellsuccess.hpp @@ -81,12 +81,12 @@ namespace MWMechanics int skillLevel = stats.getSkill (getSpellSchool(spellId, actor)).getModified(); // Sound magic effect (reduces spell casting chance) - int soundMagnitude = creatureStats.mMagicEffects.get (MWMechanics::EffectKey (48)).mMagnitude; + int soundMagnitude = creatureStats.getMagicEffects().get (MWMechanics::EffectKey (48)).mMagnitude; - int willpower = creatureStats.mAttributes[ESM::Attribute::Willpower].getModified(); - int luck = creatureStats.mAttributes[ESM::Attribute::Luck].getModified(); - int currentFatigue = creatureStats.mDynamic[2].getCurrent(); - int maxFatigue = creatureStats.mDynamic[2].getModified(); + int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getModified(); + int luck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); + int currentFatigue = creatureStats.getFatigue().getCurrent(); + int maxFatigue = creatureStats.getFatigue().getModified(); int spellCost = spell->data.cost; // There we go, all needed variables are there, lets go diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 924a0e9dd..9d70c28bd 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -130,7 +130,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mHello = value; + MWWorld::Class::get (ptr).getCreatureStats (ptr).setHello(value); } }; @@ -146,7 +146,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mFight = value; + MWWorld::Class::get (ptr).getCreatureStats (ptr).setFight(value); } }; @@ -162,7 +162,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mFlee = value; + MWWorld::Class::get (ptr).getCreatureStats (ptr).setFlee(value); } }; @@ -178,7 +178,7 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mAlarm = value; + MWWorld::Class::get (ptr).getCreatureStats (ptr).setAlarm(value); } }; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 2edd7925e..0cbbe8398 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -46,8 +46,10 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = - MWWorld::Class::get (ptr).getCreatureStats (ptr).mAttributes[mIndex]. - getModified(); + MWWorld::Class::get (ptr) + .getCreatureStats (ptr) + .getAttribute(mIndex) + .getModified(); runtime.push (value); } @@ -69,8 +71,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mAttributes[mIndex]. - setModified (value, 0); + MWWorld::Class::get(ptr) + .getCreatureStats(ptr) + .getAttribute(mIndex) + .setModified (value, 0); } }; @@ -90,11 +94,16 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - value += MWWorld::Class::get (ptr).getCreatureStats (ptr).mAttributes[mIndex]. - getModified(); + value += + MWWorld::Class::get(ptr) + .getCreatureStats(ptr) + .getAttribute(mIndex) + .getModified(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mAttributes[mIndex]. - setModified (value, 0, 100); + MWWorld::Class::get(ptr) + .getCreatureStats(ptr) + .getAttribute(mIndex) + .setModified (value, 0, 100); } }; @@ -110,21 +119,19 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); + Interpreter::Type_Integer value; if (mIndex==0 && MWWorld::Class::get (ptr).hasItemHealth (ptr)) { // health is a special case - Interpreter::Type_Integer value = - MWWorld::Class::get (ptr).getItemMaxHealth (ptr); - runtime.push (value); - - return; + value = MWWorld::Class::get (ptr).getItemMaxHealth (ptr); + } else { + value = + MWWorld::Class::get(ptr) + .getCreatureStats(ptr) + .getDynamic(mIndex) + .getCurrent(); } - - Interpreter::Type_Integer value = - MWWorld::Class::get (ptr).getCreatureStats (ptr).mDynamic[mIndex]. - getCurrent(); - runtime.push (value); } }; @@ -145,8 +152,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mDynamic[mIndex]. - setModified (value, 0); + MWWorld::Class::get(ptr) + .getCreatureStats(ptr) + .getDynamic(mIndex) + .setModified(value, 0); } }; @@ -168,12 +177,12 @@ namespace MWScript MWMechanics::CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - Interpreter::Type_Integer current = stats.mDynamic[mIndex].getCurrent(); + Interpreter::Type_Integer current = stats.getDynamic(mIndex).getCurrent(); - stats.mDynamic[mIndex].setModified ( - diff + stats.mDynamic[mIndex].getModified(), 0); + stats.getDynamic(mIndex).setModified( + diff + stats.getDynamic(mIndex).getModified(), 0); - stats.mDynamic[mIndex].setCurrent (diff + current); + stats.getDynamic(mIndex).setCurrent(diff + current); } }; @@ -195,9 +204,9 @@ namespace MWScript MWMechanics::CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - Interpreter::Type_Integer current = stats.mDynamic[mIndex].getCurrent(); + Interpreter::Type_Integer current = stats.getDynamic(mIndex).getCurrent(); - stats.mDynamic[mIndex].setCurrent (diff + current); + stats.getDynamic(mIndex).setCurrent (diff + current); } }; @@ -218,10 +227,10 @@ namespace MWScript Interpreter::Type_Float value = 0; - Interpreter::Type_Float max = stats.mDynamic[mIndex].getModified(); + Interpreter::Type_Float max = stats.getDynamic(mIndex).getModified(); if (max>0) - value = stats.mDynamic[mIndex].getCurrent() / max; + value = stats.getDynamic(mIndex).getCurrent() / max; runtime.push (value); } @@ -335,7 +344,7 @@ namespace MWScript // make sure a spell with this ID actually exists. MWBase::Environment::get().getWorld()->getStore().spells.find (id); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.add (id); + MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().add (id); } }; @@ -351,7 +360,7 @@ namespace MWScript std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.remove (id); + MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().remove (id); } }; @@ -371,8 +380,8 @@ namespace MWScript Interpreter::Type_Integer value = 0; for (MWMechanics::Spells::TIterator iter ( - MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.begin()); - iter!=MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.end(); ++iter) + MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().begin()); + iter!=MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().end(); ++iter) if (*iter==id) { value = 1; From df70b2dc9f180db935bf25faf75ce3f577ec858a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 22 Jul 2012 21:25:37 +0200 Subject: [PATCH 240/688] updated credits.txt --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 16b41d56a..3a942bfd8 100644 --- a/credits.txt +++ b/credits.txt @@ -3,6 +3,7 @@ CREDITS Current Developers: Aleksandar Jovanov Alexander “Ace†Olofsson +Artem “greye†Kotsynyak athile BrotherBrick Cris “Mirceam†Mihalache From f0b3142966ecaf8a8c9df68eae29dd3a4ac43823 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 02:54:50 +0200 Subject: [PATCH 241/688] switch to glsl 1.2 --- extern/shiny | 2 +- files/materials/clouds.shader | 2 +- files/materials/core.h | 58 +++++++++++++++++++++++++++-- files/materials/moon.shader | 2 +- files/materials/objects.shader | 3 +- files/materials/quad.shader | 2 +- files/materials/shadowcaster.shader | 2 +- files/materials/stars.shader | 2 +- files/materials/sun.shader | 2 +- files/materials/terrain.shader | 5 ++- files/materials/water.shader | 4 +- 11 files changed, 68 insertions(+), 16 deletions(-) diff --git a/extern/shiny b/extern/shiny index fdc92a424..2408fda3e 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit fdc92a4243c98f5d86a33cbdf2ed0fbb94dad3f6 +Subproject commit 2408fda3e226dda1a1aa7bafd07a769324419b37 diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index 7677ecd95..f4258bf5d 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -6,7 +6,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) shColourInput(float4) shOutput(float4, colourPassthrough) diff --git a/files/materials/core.h b/files/materials/core.h index c4d287761..306073a77 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -11,6 +11,7 @@ #define shUniform(type, name) , uniform type name + #define shVertexInput(type, name) , in type name : TEXCOORD@shCounter(1) #define shInput(type, name) , in type name : TEXCOORD@shCounter(1) #define shOutput(type, name) , out type name : TEXCOORD@shCounter(2) @@ -52,7 +53,8 @@ #endif #if SH_GLSL == 1 - @version 130 + + @version 120 #define float2 vec2 #define float3 vec3 @@ -61,7 +63,7 @@ #define int3 ivec3 #define int4 ivec4/ #define shTexture2D sampler2D - #define shSample(tex, coord) texture(tex, coord) + #define shSample(tex, coord) texture2D(tex, coord) #define shLerp(a, b, t) mix(a, b, t) #define shSaturate(a) clamp(a, 0.0, 1.0) @@ -71,13 +73,19 @@ #define shMatrixMult(m, v) (m * v) + #define shOutputPosition gl_Position + + #define float4x4 mat4 + + // GLSL 1.3 + #if 0 + // automatically recognized by ogre when the input name equals this #define shInputPosition vertex - #define shOutputPosition gl_Position #define shOutputColour(num) oColor##num - #define float4x4 mat4 + #define shInput(type, name) in type name; #define shOutput(type, name) out type name; @@ -105,5 +113,47 @@ void main(void) + #endif + + #endif + + // GLSL 1.2 + + #if 1 + + // automatically recognized by ogre when the input name equals this + #define shInputPosition gl_Vertex + + #define shOutputColour(num) gl_FragData[num] + + #define shVertexInput(type, name) attribute type name; + #define shInput(type, name) varying type name; + #define shOutput(type, name) varying type name; + + // automatically recognized by ogre when the input name equals this + #define shNormalInput(type) attribute type normal; + #define shColourInput(type) attribute type colour; + + #ifdef SH_VERTEX_SHADER + + #define SH_BEGIN_PROGRAM + + #define SH_START_PROGRAM \ + void main(void) + + #endif + + #ifdef SH_FRAGMENT_SHADER + + #define shDeclareMrtOutput(num) + + #define SH_BEGIN_PROGRAM + + #define SH_START_PROGRAM \ + void main(void) + + + #endif + #endif #endif diff --git a/files/materials/moon.shader b/files/materials/moon.shader index 4a4eaf0b4..7640563ce 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -7,7 +7,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 39c9b42e0..238ccdcf6 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -28,7 +28,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) shNormalInput(float4) #ifdef NEED_DEPTH @@ -207,6 +207,7 @@ float3 waterEyePos = float3(1,1,1); if (worldPos.y < waterLevel && waterEnabled == 1) { + // NOTE: this calculation would be wrong for non-uniform scaling float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); diff --git a/files/materials/quad.shader b/files/materials/quad.shader index 4fa38cac9..4620588c3 100644 --- a/files/materials/quad.shader +++ b/files/materials/quad.shader @@ -3,7 +3,7 @@ #ifdef SH_VERTEX_SHADER SH_BEGIN_PROGRAM - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) SH_START_PROGRAM diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader index a5551509d..b312d414c 100644 --- a/files/materials/shadowcaster.shader +++ b/files/materials/shadowcaster.shader @@ -6,7 +6,7 @@ SH_BEGIN_PROGRAM #if ALPHA - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) #endif shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) diff --git a/files/materials/stars.shader b/files/materials/stars.shader index 75592fc72..5a55d171e 100644 --- a/files/materials/stars.shader +++ b/files/materials/stars.shader @@ -7,7 +7,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) shOutput(float, fade) diff --git a/files/materials/sun.shader b/files/materials/sun.shader index ceab60565..45cd2f24b 100644 --- a/files/materials/sun.shader +++ b/files/materials/sun.shader @@ -7,7 +7,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) SH_START_PROGRAM diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index e0187959f..a562d1ec3 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -54,8 +54,8 @@ shUniform(float2, lodMorph) @shAutoConstant(lodMorph, custom, 1001) - shInput(float2, uv0) - shInput(float2, delta) // lodDelta, lodThreshold + shVertexInput(float2, uv0) + shVertexInput(float2, delta) // lodDelta, lodThreshold #if SHADOWS shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) @@ -226,6 +226,7 @@ float3 waterEyePos = float3(1,1,1); if (worldPos.y < waterLevel) { + // NOTE: this calculation would be wrong for non-uniform scaling float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); diff --git a/files/materials/water.shader b/files/materials/water.shader index 805cf7b81..2145919b0 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -15,7 +15,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) shOutput(float, depth) @@ -67,7 +67,7 @@ SH_BEGIN_PROGRAM shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) - shInput(float2, uv0) + shVertexInput(float2, uv0) shOutput(float2, UV) shOutput(float3, screenCoordsPassthrough) From 268987835dc90105fbadc6d5540ac6c9c7cd9c18 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 12:19:53 +0200 Subject: [PATCH 242/688] fix the crash --- extern/shiny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny b/extern/shiny index 2408fda3e..41245d136 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 2408fda3e226dda1a1aa7bafd07a769324419b37 +Subproject commit 41245d1361bc0242e5d2c2313caffb711207e5fc From bc7876e980fb4093a1274e953e12981f6d66ace9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 13:04:48 +0200 Subject: [PATCH 243/688] use alpha map for moons --- files/materials/moon.shader | 9 ++++----- files/materials/sky.mat | 5 +++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/files/materials/moon.shader b/files/materials/moon.shader index 7640563ce..02f3d8001 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -20,6 +20,7 @@ SH_BEGIN_PROGRAM shSampler2D(diffuseMap) + shSampler2D(alphaMap) shInput(float2, UV) #if MRT shDeclareMrtOutput(1) @@ -36,17 +37,15 @@ shOutputColour(0) = float4(materialEmissive.xyz, 1) * tex; - // use a circle for the alpha (compute UV distance to center) - // looks a bit bad because it's not filtered on the edges, - // but cheaper than a seperate alpha texture. - float sqrUVdist = pow(UV.x-0.5,2) + pow(UV.y-0.5, 2); - shOutputColour(0).a = materialDiffuse.a * (sqrUVdist >= 0.24 ? 0 : 1); + shOutputColour(0).a = shSample(alphaMap, UV).a * materialDiffuse.a; + shOutputColour(0).rgb += (1-tex.a) * shOutputColour(0).a * atmosphereColour.rgb; //fill dark side of moon with atmosphereColour shOutputColour(0).rgb += (1-materialDiffuse.a) * atmosphereColour.rgb; //fade bump #if MRT shOutputColour(1) = float4(1,1,1,1); #endif + } #endif diff --git a/files/materials/sky.mat b/files/materials/sky.mat index 17da7fc13..4af90a170 100644 --- a/files/materials/sky.mat +++ b/files/materials/sky.mat @@ -15,6 +15,11 @@ material openmw_moon { texture_alias $texture } + + texture_unit alphaMap + { + direct_texture textures\tx_secunda_full.dds + } } } From afe7c4172906c39e4f2e9ad9b48141da5ad026ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 13:47:51 +0200 Subject: [PATCH 244/688] removed some unused code --- apps/openmw/mwrender/water.cpp | 4 ++++ apps/openmw/mwrender/water.hpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 1c0afab67..3aff334d1 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -71,6 +71,7 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mWater->setMaterial(mMaterial); + /* Ogre::Entity* underwaterDome = mSceneManager->createEntity ("underwater_dome.mesh"); underwaterDome->setRenderQueueGroup (RQG_UnderWater); mUnderwaterDome = mSceneManager->getRootSceneNode ()->createChildSceneNode (); @@ -78,6 +79,7 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mUnderwaterDome->setScale(10000,10000,10000); mUnderwaterDome->setVisible(false); underwaterDome->setMaterialName("Underwater_Dome"); + */ mSceneManager->addRenderQueueListener(this); @@ -309,9 +311,11 @@ void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invo void Water::update(float dt) { + /* Ogre::Vector3 pos = mCamera->getDerivedPosition (); pos.y = -mWaterPlane.d; mUnderwaterDome->setPosition (pos); + */ mWaterTimer += dt / 30.0 * MWBase::Environment::get().getWorld()->getTimeScaleFactor(); sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(mWaterTimer))); diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 60e39c496..d0a5a4352 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -43,7 +43,7 @@ namespace MWRender { Ogre::SceneNode *mWaterNode; Ogre::Entity *mWater; - Ogre::SceneNode* mUnderwaterDome; + //Ogre::SceneNode* mUnderwaterDome; bool mIsUnderwater; bool mActive; From 2eca27bba9b7649f939acaf66bbcd0755bea81e6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 14:21:40 +0200 Subject: [PATCH 245/688] fix horizon color of the sky reflection --- apps/openmw/mwrender/renderingmanager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 852288d14..b535cb6ae 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -368,6 +368,9 @@ void RenderingManager::configureFog(MWWorld::Ptr::CellStore &mCell) color.setAsABGR (mCell.cell->ambi.fog); configureFog(mCell.cell->ambi.fogDensity, color); + + if (mWater) + mWater->setViewportBackground (Ogre::ColourValue(0.8f, 0.9f, 1.0f)); } void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) @@ -382,6 +385,9 @@ void RenderingManager::configureFog(const float density, const Ogre::ColourValue mRendering.getCamera()->setFarClipDistance ( max / density ); mRendering.getViewport()->setBackgroundColour (colour); + if (mWater) + mWater->setViewportBackground (colour); + sh::Factory::getInstance ().setSharedParameter ("viewportBackground", sh::makeProperty (new sh::Vector3(colour.r, colour.g, colour.b))); From ac5bd38df9522da88932725e62d9859720f8fc75 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 14:59:57 +0200 Subject: [PATCH 246/688] some cmake fixes --- CMakeLists.txt | 6 +----- extern/shiny | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0b5bf451..f124f1383 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,14 +189,10 @@ endif() set(BOOST_COMPONENTS system filesystem program_options thread) if (Boost_VERSION LESS 104900) - set(SHINY_USE_WAVE_SYSTEM_INSTALL TRUE) + set(SHINY_USE_WAVE_SYSTEM_INSTALL "TRUE") set(BOOST_COMPONENTS ${BOOST_COMPONENTS} wave) -else() - set(SHINY_USE_WAVE_SYSTEM_INSTALL FALSE) endif() -MESSAGE(STATUS ${BOOST_COMPONENTS}) - find_package(OGRE REQUIRED) find_package(MyGUI REQUIRED) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) diff --git a/extern/shiny b/extern/shiny index 41245d136..73ddc737c 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 41245d1361bc0242e5d2c2313caffb711207e5fc +Subproject commit 73ddc737ce6334b264649fd4b0a16109fd8b8a99 From 1fef0860887fd41a1b775efb9f7f278f9b30fd3f Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 15:36:12 +0200 Subject: [PATCH 247/688] Revert "Merge remote-tracking branch 'mark76/multiple_esm_esp' into nif-cleanup" This reverts commit 546b640022e3ec111b5c795c3fc1d067c3b1a684, reversing changes made to fcaa8aae06d791c37daab0897f4fd19f6b2129b5. --- apps/openmw/engine.cpp | 30 +++++-------------- apps/openmw/engine.hpp | 8 ++--- apps/openmw/main.cpp | 28 ++++++++--------- apps/openmw/mwrender/terrain.cpp | 24 ++++----------- apps/openmw/mwrender/terrain.hpp | 5 +--- apps/openmw/mwworld/worldimp.cpp | 32 +++++--------------- apps/openmw/mwworld/worldimp.hpp | 3 +- components/esm/esm_reader.hpp | 8 ----- components/esm/loadland.cpp | 1 - components/esm/loadland.hpp | 1 - components/esm_store/reclists.hpp | 50 +++++++------------------------ components/esm_store/store.cpp | 7 ----- 12 files changed, 48 insertions(+), 149 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f41d453b5..ab5b63071 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -207,32 +207,18 @@ void OMW::Engine::setCell (const std::string& cellName) // Set master file (esm) // - If the given name does not have an extension, ".esm" is added automatically +// - Currently OpenMW only supports one master at the same time. void OMW::Engine::addMaster (const std::string& master) { - mMaster.push_back(master); - std::string &str = mMaster.back(); + assert (mMaster.empty()); + mMaster = master; - // Append .esm if not already there - std::string::size_type sep = str.find_last_of ("."); + // Append .esm if not already there + std::string::size_type sep = mMaster.find_last_of ("."); if (sep == std::string::npos) { - str += ".esm"; - } -} - -// Add plugin file (esp) - -void OMW::Engine::addPlugin (const std::string& plugin) -{ - mPlugins.push_back(plugin); - std::string &str = mPlugins.back(); - - // Append .esp if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esp"; + mMaster += ".esm"; } } @@ -333,8 +319,8 @@ void OMW::Engine::go() MWGui::CursorReplace replacer; // Create the world - mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, - mResDir, mNewGame, mEncoding, mFallbackMap) ); + mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, + mResDir, mNewGame, mEncoding, mFallbackMap)); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 0582800c9..031cae551 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -64,8 +64,7 @@ namespace OMW boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; - std::vector mMaster; - std::vector mPlugins; + std::string mMaster; int mFpsLevel; bool mDebug; bool mVerboseScripts; @@ -121,12 +120,9 @@ namespace OMW /// Set master file (esm) /// - If the given name does not have an extension, ".esm" is added automatically + /// - Currently OpenMW only supports one master at the same time. void addMaster(const std::string& master); - /// Same as "addMaster", but for plugin files (esp) - /// - If the given name does not have an extension, ".esp" is added automatically - void addPlugin(const std::string& plugin); - /// Enable fps counter void showFPS(int level); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 9fe333a28..993ec6623 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -224,23 +224,19 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat master.push_back("Morrowind"); } - StringsVector plugin = variables["plugin"].as(); - int cnt = master.size() + plugin.size(); - if (cnt > 255) - { - std::cerr - << "Error: Trying to load more than 255 master and plugin files! This will break all combaibility and is not supported!" - << std::endl; - return false; - } - for (std::vector::size_type i = 0; i < master.size(); i++) + if (master.size() > 1) { - engine.addMaster(master[i]); - } - for (std::vector::size_type i = 0; i < plugin.size(); i++) - { - engine.addPlugin(plugin[i]); - } + std::cout + << "Ignoring all but the first master file (multiple master files not yet supported)." + << std::endl; + } + engine.addMaster(master[0]); + + StringsVector plugin = variables["plugin"].as(); + if (!plugin.empty()) + { + std::cout << "Ignoring plugin files (plugins not yet supported)." << std::endl; + } // startup-settings engine.setCell(variables["start"].as()); diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 909836659..691e7c4af 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -141,7 +141,7 @@ namespace MWRender std::map indexes; initTerrainTextures(&terrainData, cellX, cellY, x * numTextures, y * numTextures, - numTextures, indexes, land->plugin); + numTextures, indexes); if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL) { @@ -200,14 +200,8 @@ namespace MWRender void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData, int cellX, int cellY, int fromX, int fromY, int size, - std::map& indexes, - size_t plugin) + std::map& indexes) { - // FIXME: In a multiple esm configuration, we have multiple palettes. Since this code - // crosses cell boundaries, we no longer have a unique terrain palette. Instead, we need - // to adopt the following code for a dynamic palette. And this is evil - the current design - // does not work well for this task... - assert(terrainData != NULL && "Must have valid terrain data"); assert(fromX >= 0 && fromY >= 0 && "Can't get a terrain texture on terrain outside the current cell"); @@ -220,16 +214,12 @@ namespace MWRender // //If we don't sort the ltex indexes, the splatting order may differ between //cells which may lead to inconsistent results when shading between cells - int num = MWBase::Environment::get().getWorld()->getStore().landTexts.getSizePlugin(plugin); std::set ltexIndexes; for ( int y = fromY - 1; y < fromY + size + 1; y++ ) { for ( int x = fromX - 1; x < fromX + size + 1; x++ ) { - int idx = getLtexIndexAt(cellX, cellY, x, y); - if (idx > num) - idx = 0; - ltexIndexes.insert(idx); + ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y)); } } @@ -241,7 +231,7 @@ namespace MWRender iter != ltexIndexes.end(); ++iter ) { - uint16_t ltexIndex = *iter; + const uint16_t ltexIndex = *iter; //this is the base texture, so we can ignore this at present if ( ltexIndex == baseTexture ) { @@ -254,10 +244,8 @@ namespace MWRender { //NB: All vtex ids are +1 compared to the ltex ids - /* - assert( (int)mEnvironment.mWorld->getStore().landTexts.getSizePlugin(plugin) >= (int)ltexIndex - 1 && + assert( (int)MWBase::Environment::get().getWorld()->getStore().landTexts.getSize() >= (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); - */ std::string texture; if ( ltexIndex == 0 ) @@ -266,7 +254,7 @@ namespace MWRender } else { - texture = MWBase::Environment::get().getWorld()->getStore().landTexts.search(ltexIndex-1, plugin)->texture; + texture = MWBase::Environment::get().getWorld()->getStore().landTexts.search(ltexIndex-1)->texture; //TODO this is needed due to MWs messed up texture handling texture = texture.substr(0, texture.rfind(".")) + ".dds"; } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 94072fc40..c83d96cf4 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -71,14 +71,11 @@ namespace MWRender{ * @param size the size (number of splats) to get * @param indexes a mapping of ltex index to the terrain texture layer that * can be used by initTerrainBlendMaps - * @param plugin the index of the plugin providing the texture list for this - * cell data; required because MW uses texture data on a per-plugin base */ void initTerrainTextures(Ogre::Terrain::ImportData* terrainData, int cellX, int cellY, int fromX, int fromY, int size, - std::map& indexes, - size_t plugin = 0); + std::map& indexes); /** * Creates the blend (splatting maps) for the given terrain from the ltex data. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ae517071e..a68f08e34 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -167,8 +167,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, - const std::vector& plugins, const boost::filesystem::path& resDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm), @@ -181,32 +180,15 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering); - int idx = 0; - for (std::vector::size_type i = 0; i < master.size(); i++, idx++) - { - boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i])); - - std::cout << "Loading ESM " << masterPath.string() << "\n"; + boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master)); - // This parses the ESM file and loads a sample cell - mEsm.setEncoding(encoding); - mEsm.open (masterPath.string()); - mEsm.setIndex(idx); - mStore.load (mEsm); - } + std::cout << "Loading ESM " << masterPath.string() << "\n"; - for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) - { - boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); - - std::cout << "Loading ESP " << pluginPath.string() << "\n"; + // This parses the ESM file and loads a sample cell + mEsm.setEncoding(encoding); + mEsm.open (masterPath.string()); + mStore.load (mEsm); - // This parses the ESP file and loads a sample cell - mEsm.setEncoding(encoding); - mEsm.open (pluginPath.string()); - mEsm.setIndex(idx); - mStore.load (mEsm); - } MWRender::Player* play = &(mRendering->getPlayer()); mPlayer = new MWWorld::Player (play, mStore.npcs.find ("player"), *this); mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index af6232aaf..43b178fe3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -93,8 +93,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, - const std::vector& plugins, const boost::filesystem::path& resDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, bool newGame, const std::string& encoding, std::map fallbackMap); virtual ~World(); diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 0326fdc04..13f1f4a01 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -189,14 +189,6 @@ public: void openRaw(const std::string &file); - // This is a quick hack for multiple esm/esp files. Each plugin introduces its own - // terrain palette, but ESMReader does not pass a reference to the correct plugin - // to the individual load() methods. This hack allows to pass this reference - // indirectly to the load() method. - int idx; - void setIndex(const int index) {idx = index;} - const int getIndex() {return idx;} - /************************************************************************* * * Medium-level reading shortcuts diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 822952c91..96afdf831 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -23,7 +23,6 @@ Land::~Land() void Land::load(ESMReader &esm) { mEsm = &esm; - plugin = mEsm->getIndex(); // Get the grid location esm.getSubNameIs("INTV"); diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index d03012d38..ebc314a28 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -17,7 +17,6 @@ struct Land int flags; // Only first four bits seem to be used, don't know what // they mean. int X, Y; // Map coordinates. - int plugin; // Plugin index, used to reference the correct material palette. // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 668c4e58e..ffecfc8de 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -26,7 +26,6 @@ namespace ESMS virtual void load(ESMReader &esm, const std::string &id) = 0; virtual int getSize() = 0; - virtual void remove(const std::string &id) {}; virtual void listIdentifier (std::vector& identifier) const = 0; static std::string toLower (const std::string& name) @@ -58,14 +57,6 @@ namespace ESMS list[id2].load(esm); } - // Delete the given object ID - void remove(const std::string &id) - { - std::string id2 = toLower (id); - - list.erase(id2); - } - // Find the given object ID, or return NULL if not found. const X* search(const std::string &id) const { @@ -277,57 +268,38 @@ namespace ESMS { virtual ~LTexList() {} - // For multiple ESM/ESP files we need one list per file. - typedef std::vector LandTextureList; - std::vector ltex; + // TODO: For multiple ESM/ESP files we need one list per file. + std::vector ltex; LTexList() { - ltex.push_back(LandTextureList()); - LandTextureList <exl = ltex[0]; // More than enough to hold Morrowind.esm. - ltexl.reserve(128); + ltex.reserve(128); } - const LandTexture* search(size_t index, size_t plugin) const + const LandTexture* search(size_t index) const { - assert(plugin < ltex.size()); - const LandTextureList <exl = ltex[plugin]; - - assert(index < ltexl.size()); - return <exl.at(index); + assert(index < ltex.size()); + return <ex.at(index); } int getSize() { return ltex.size(); } int getSize() const { return ltex.size(); } - int getSizePlugin(size_t plugin) { assert(plugin < ltex.size()); return ltex[plugin].size(); } - int getSizePlugin(size_t plugin) const { assert(plugin < ltex.size()); return ltex[plugin].size(); } + virtual void listIdentifier (std::vector& identifier) const {} - virtual void listIdentifier (std::vector& identifier) const {} - - void load(ESMReader &esm, const std::string &id, size_t plugin) + void load(ESMReader &esm, const std::string &id) { LandTexture lt; lt.load(esm); lt.id = id; // Make sure we have room for the structure - if (plugin >= ltex.size()) { - ltex.resize(plugin+1); - } - LandTextureList <exl = ltex[plugin]; - if(lt.index + 1 > (int)ltexl.size()) - ltexl.resize(lt.index+1); + if(lt.index + 1 > (int)ltex.size()) + ltex.resize(lt.index+1); // Store it - ltexl[lt.index] = lt; - } - - void load(ESMReader &esm, const std::string &id) - { - size_t plugin = esm.getIndex(); - load(esm, id, plugin); + ltex[lt.index] = lt; } }; diff --git a/components/esm_store/store.cpp b/components/esm_store/store.cpp index b6972355c..c676601e5 100644 --- a/components/esm_store/store.cpp +++ b/components/esm_store/store.cpp @@ -67,13 +67,6 @@ void ESMStore::load(ESMReader &esm) { // Load it std::string id = esm.getHNOString("NAME"); - // ... unless it got deleted! - if (esm.isNextSub("DELE")) { - esm.skipRecord(); - all.erase(id); - it->second->remove(id); - continue; - } it->second->load(esm, id); if (n.val==ESM::REC_DIAL) From 5572421576351e3140a95942e73ff0a29e2cddcc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 15:40:10 +0200 Subject: [PATCH 248/688] submodule update for "fix a typo" --- extern/shiny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny b/extern/shiny index 73ddc737c..9e833c95a 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 73ddc737ce6334b264649fd4b0a16109fd8b8a99 +Subproject commit 9e833c95a1dbd426bc87818ed5dbad02917dd504 From d5384403f3aa8f32e5d567eff807a2e697a118d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 15:46:18 +0200 Subject: [PATCH 249/688] forgot something else --- extern/shiny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny b/extern/shiny index 9e833c95a..5546a5bd8 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 9e833c95a1dbd426bc87818ed5dbad02917dd504 +Subproject commit 5546a5bd8474ef328dfedae2df42126cdaf9515f From ab35bfa32cb4642750538c77bf8996419d9bdc7a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 18:19:34 +0200 Subject: [PATCH 250/688] fixed a sky issue --- apps/openmw/mwrender/sky.cpp | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index d78a3caff..d928995f4 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -287,21 +287,12 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) void SkyManager::create() { + assert(!mCreated); + sh::Factory::getInstance().setSharedParameter ("cloudBlendFactor", sh::makeProperty(new sh::FloatValue(0))); - sh::Factory::getInstance().setSharedParameter ("cloudOpacity", - sh::makeProperty(new sh::FloatValue(1))); - sh::Factory::getInstance().setSharedParameter ("cloudColour", - sh::makeProperty(new sh::Vector3(1,1,1))); - sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", - sh::makeProperty(new sh::FloatValue(0))); - sh::Factory::getInstance().setSharedParameter ("nightFade", - sh::makeProperty(new sh::FloatValue(0))); - sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", ""); - sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", ""); - - // Create overlay used for thunderstorm + // Create light used for thunderstorm mLightning = mSceneMgr->createLight(); mLightning->setType (Ogre::Light::LT_DIRECTIONAL); mLightning->setDirection (Ogre::Vector3(0.3, -0.7, 0.3)); @@ -332,19 +323,15 @@ void SkyManager::create() night1_ent->setVisibilityFlags(RV_Sky); night1_ent->setCastShadows(false); - for (unsigned int i=0; igetNumSubEntities(); ++i) - { - std::string matName = "openmw_stars_" + boost::lexical_cast(i); - sh::MaterialInstance* m = sh::Factory::getInstance ().createMaterialInstance (matName, "openmw_stars"); + std::string matName = "openmw_stars_" + boost::lexical_cast(i); + sh::MaterialInstance* m = sh::Factory::getInstance ().createMaterialInstance (matName, "openmw_stars"); - std::string textureName = sh::retrieveValue( - sh::Factory::getInstance().getMaterialInstance(night1_ent->getSubEntity (i)->getMaterialName ())->getProperty("diffuseMap"), NULL).get(); + std::string textureName = sh::retrieveValue( + sh::Factory::getInstance().getMaterialInstance(night1_ent->getSubEntity (0)->getMaterialName ())->getProperty("diffuseMap"), NULL).get(); - m->setProperty ("texture", sh::makeProperty(new sh::StringValue(textureName))); + m->setProperty ("texture", sh::makeProperty(new sh::StringValue(textureName))); - night1_ent->getSubEntity(i)->setMaterialName (matName); - - } + night1_ent->getSubEntity(0)->setMaterialName (matName); } @@ -370,7 +357,6 @@ void SkyManager::create() Entity* clouds_ent = entities.mEntities[i]; clouds_ent->setVisibilityFlags(RV_Sky); clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); - clouds_node->attachObject(clouds_ent); clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds"); clouds_ent->setCastShadows(false); From 9f9183ff73b1a85afff14a7c7f8ec053f30e01f9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 18:25:20 +0200 Subject: [PATCH 251/688] depth bias --- files/materials/shadows.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/files/materials/shadows.h b/files/materials/shadows.h index 9127d28f7..769a4fea7 100644 --- a/files/materials/shadows.h +++ b/files/materials/shadows.h @@ -1,13 +1,15 @@ +#define FIXED_BIAS 0.005 + float depthShadowPCF (shTexture2D shadowMap, float4 shadowMapPos, float2 offset) { shadowMapPos /= shadowMapPos.w; float3 o = float3(offset.xy, -offset.x) * 0.3; //float3 o = float3(0,0,0); - float c = (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left - c += (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right - c += (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left - c += (shadowMapPos.z <= shSample(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right + float c = (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left + c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right + c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left + c += (shadowMapPos.z <= FIXED_BIAS + shSample(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right return c / 4; } From a70f93b0244fde6065157d5a827be30af8e3e1e5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 20:12:42 +0200 Subject: [PATCH 252/688] fix "Crash when running openmw with --start="XYZ", remove HLSL for windows --- apps/openmw/mwgui/settingswindow.cpp | 16 +++++++++------- apps/openmw/mwrender/renderingmanager.cpp | 9 +++++++-- apps/openmw/mwrender/sky.cpp | 1 + apps/openmw/mwrender/water.cpp | 4 ---- files/materials/core.h | 6 +++++- files/materials/objects.shaderset | 2 +- files/materials/terrain.shaderset | 2 +- 7 files changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 4f8ad77c9..599783e42 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -75,12 +75,9 @@ namespace return boost::lexical_cast(xaspect) + " : " + boost::lexical_cast(yaspect); } - std::string hlslGlsl () + bool hasGLSL () { - if (Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") == std::string::npos) - return "hlsl"; - else - return "glsl"; + return (Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") != std::string::npos); } } @@ -389,8 +386,13 @@ namespace MWGui { std::string val = static_cast(_sender)->getCaption(); if (val == "off") - val = hlslGlsl(); - else if (val == hlslGlsl()) + { + if (hasGLSL ()) + val = "glsl"; + else + val = "cg"; + } + else if (val == "glsl") val = "cg"; else val = "off"; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b535cb6ae..bcf9f2101 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -46,7 +46,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const if (Settings::Manager::getString("shader mode", "General") == "") { if (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") == std::string::npos) - Settings::Manager::setString("shader mode", "General", "hlsl"); + Settings::Manager::setString("shader mode", "General", "cg"); else Settings::Manager::setString("shader mode", "General", "glsl"); } @@ -113,6 +113,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setSharedParameter ("viewportBackground", sh::makeProperty (new sh::Vector3(0,0,0))); sh::Factory::getInstance ().setSharedParameter ("waterEnabled", sh::makeProperty (new sh::FloatValue(0.0))); + sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(0))); + sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(0))); + sh::Factory::getInstance ().setSharedParameter ("windDir_windSpeed", sh::makeProperty(new sh::Vector3(0.5, -0.8, 0.2))); + sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(1, 0.6))); applyCompositors(); @@ -269,7 +273,8 @@ void RenderingManager::update (float duration){ checkUnderwater(); - mWater->update(duration); + if (mWater) + mWater->update(duration); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ if(store->cell->data.flags & store->cell->HasWater diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 2a2df7943..aeefb95d1 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -297,6 +297,7 @@ void SkyManager::create() sh::makeProperty(new sh::FloatValue(0))); sh::Factory::getInstance().setSharedParameter ("nightFade", sh::makeProperty(new sh::FloatValue(0))); + sh::Factory::getInstance().setSharedParameter ("atmosphereColour", sh::makeProperty(new sh::Vector4(0,0,0,1))); sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", ""); sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", ""); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 3aff334d1..92fc97b3b 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -37,10 +37,6 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel { mSky = rend->getSkyManager(); - sh::Factory::getInstance ().setSharedParameter ("windDir_windSpeed", sh::makeProperty(new sh::Vector3(0.5, -0.8, 0.2))); - sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(0))); - sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(1, 0.6))); - mMaterial = MaterialManager::getSingleton().getByName("Water"); mTop = cell->water; diff --git a/files/materials/core.h b/files/materials/core.h index 306073a77..6e27d349c 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -1,4 +1,8 @@ -#if SH_HLSL == 1 || SH_CG == 1 +#if SH_HLSL == 1 + #error "HLSL is unsupported" +#endif + +#if SH_CG == 1 #define shTexture2D sampler2D #define shSample(tex, coord) tex2D(tex, coord) diff --git a/files/materials/objects.shaderset b/files/materials/objects.shaderset index e84368a5b..ccb975fe9 100644 --- a/files/materials/objects.shaderset +++ b/files/materials/objects.shaderset @@ -10,6 +10,6 @@ shader_set openmw_objects_fragment { source objects.shader type fragment - profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 + profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 profiles_hlsl ps_2_0 } diff --git a/files/materials/terrain.shaderset b/files/materials/terrain.shaderset index 4132b8e9c..be8ecd7d8 100644 --- a/files/materials/terrain.shaderset +++ b/files/materials/terrain.shaderset @@ -10,6 +10,6 @@ shader_set terrain_fragment { source terrain.shader type fragment - profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 + profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 profiles_hlsl ps_2_0 } From 3ebc6fd5904f78e10e04fa749a257ae668ef2fbd Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 20:50:56 +0200 Subject: [PATCH 253/688] fixed a settings window bug --- apps/openmw/mwgui/settingswindow.cpp | 13 ++++++++----- files/mygui/openmw_settings_window.layout | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 599783e42..3c8e06dc0 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -419,11 +419,14 @@ namespace MWGui else { // re-enable - mWaterShaderButton->setEnabled(true); - mReflectObjectsButton->setEnabled(true); - mReflectActorsButton->setEnabled(true); - mReflectTerrainButton->setEnabled(true); - mShadowsEnabledButton->setEnabled(true); + if (MWRender::RenderingManager::waterShaderSupported()) + { + mWaterShaderButton->setEnabled(true); + mReflectObjectsButton->setEnabled(true); + mReflectActorsButton->setEnabled(true); + mReflectTerrainButton->setEnabled(true); + mShadowsEnabledButton->setEnabled(true); + } Settings::Manager::setBool("shaders", "Objects", true); Settings::Manager::setString("shader mode", "General", val); diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 07c307324..a14606ade 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -189,10 +189,10 @@
- + - + From 3aa53fea327b061af6208b99fcbe0cb8e39732ea Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 23 Jul 2012 23:06:53 +0400 Subject: [PATCH 254/688] wrong --- apps/openmw/mwworld/scene.cpp | 124 ---------------------------------- 1 file changed, 124 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 33c67aad8..b28a933b1 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -336,130 +336,6 @@ namespace MWWorld /// \todo this whole code needs major clean up, and doesn't belong in this class. - void Scene::insertObject (const Ptr& ptr, CellStore* cell) - { - std::string type = ptr.getTypeName(); - - MWWorld::Ptr newPtr; - - // insert into the correct CellRefList - if (type == typeid(ESM::Potion).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->potions.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); - } - else if (type == typeid(ESM::Apparatus).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->appas.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); - } - else if (type == typeid(ESM::Armor).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->armors.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); - } - else if (type == typeid(ESM::Book).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->books.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); - } - else if (type == typeid(ESM::Clothing).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->clothes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); - } - else if (type == typeid(ESM::Ingredient).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->ingreds.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); - } - else if (type == typeid(ESM::Light).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lights.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); - } - else if (type == typeid(ESM::Tool).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lockpicks.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); - } - else if (type == typeid(ESM::Repair).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->repairs.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); - } - else if (type == typeid(ESM::Probe).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->probes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); - } - else if (type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->weapons.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); - } - else if (type == typeid(ESM::Miscellaneous).name()) - { - - // if this is gold, we need to fetch the correct mesh depending on the amount of gold. - if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) - { - int goldAmount = ptr.getRefData().getCount(); - - std::string base = "Gold_001"; - if (goldAmount >= 100) - base = "Gold_100"; - else if (goldAmount >= 25) - base = "Gold_025"; - else if (goldAmount >= 10) - base = "Gold_010"; - else if (goldAmount >= 5) - base = "Gold_005"; - - MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); - - MWWorld::LiveCellRef* ref = newRef.getPtr().get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - - ESM::Position& p = newPtr.getRefData().getPosition(); - p.pos[0] = ptr.getRefData().getPosition().pos[0]; - p.pos[1] = ptr.getRefData().getPosition().pos[1]; - p.pos[2] = ptr.getRefData().getPosition().pos[2]; - } - else - { - MWWorld::LiveCellRef* ref = ptr.get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - } - } - else - throw std::runtime_error("Trying to insert object of unhandled type"); - - - - newPtr.getRefData().setCount(ptr.getRefData().getCount()); - ptr.getRefData().setCount(0); - newPtr.getRefData().enable(); - - mRendering.addObject(newPtr); - MWWorld::Class::get(newPtr).insertObject(newPtr, *mPhysics); - - } void Scene::addObjectToScene (const Ptr& ptr) { From 9a2690f84921696f8894cdaf8b8ee5e2de9843d8 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 23 Jul 2012 23:07:28 +0400 Subject: [PATCH 255/688] Revert "wrong" This reverts commit 3aa53fea327b061af6208b99fcbe0cb8e39732ea. --- apps/openmw/mwworld/scene.cpp | 124 ++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b28a933b1..33c67aad8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -336,6 +336,130 @@ namespace MWWorld /// \todo this whole code needs major clean up, and doesn't belong in this class. + void Scene::insertObject (const Ptr& ptr, CellStore* cell) + { + std::string type = ptr.getTypeName(); + + MWWorld::Ptr newPtr; + + // insert into the correct CellRefList + if (type == typeid(ESM::Potion).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->potions.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); + } + else if (type == typeid(ESM::Apparatus).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->appas.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); + } + else if (type == typeid(ESM::Armor).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->armors.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); + } + else if (type == typeid(ESM::Book).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->books.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); + } + else if (type == typeid(ESM::Clothing).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->clothes.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); + } + else if (type == typeid(ESM::Ingredient).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->ingreds.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); + } + else if (type == typeid(ESM::Light).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->lights.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); + } + else if (type == typeid(ESM::Tool).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->lockpicks.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); + } + else if (type == typeid(ESM::Repair).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->repairs.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); + } + else if (type == typeid(ESM::Probe).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->probes.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); + } + else if (type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->weapons.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); + } + else if (type == typeid(ESM::Miscellaneous).name()) + { + + // if this is gold, we need to fetch the correct mesh depending on the amount of gold. + if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + { + int goldAmount = ptr.getRefData().getCount(); + + std::string base = "Gold_001"; + if (goldAmount >= 100) + base = "Gold_100"; + else if (goldAmount >= 25) + base = "Gold_025"; + else if (goldAmount >= 10) + base = "Gold_010"; + else if (goldAmount >= 5) + base = "Gold_005"; + + MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); + + MWWorld::LiveCellRef* ref = newRef.getPtr().get(); + + cell->miscItems.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); + + ESM::Position& p = newPtr.getRefData().getPosition(); + p.pos[0] = ptr.getRefData().getPosition().pos[0]; + p.pos[1] = ptr.getRefData().getPosition().pos[1]; + p.pos[2] = ptr.getRefData().getPosition().pos[2]; + } + else + { + MWWorld::LiveCellRef* ref = ptr.get(); + + cell->miscItems.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); + } + } + else + throw std::runtime_error("Trying to insert object of unhandled type"); + + + + newPtr.getRefData().setCount(ptr.getRefData().getCount()); + ptr.getRefData().setCount(0); + newPtr.getRefData().enable(); + + mRendering.addObject(newPtr); + MWWorld::Class::get(newPtr).insertObject(newPtr, *mPhysics); + + } void Scene::addObjectToScene (const Ptr& ptr) { From 02a52c0ff1f7f92170ac25e162da9f34a02686a4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jul 2012 21:39:22 +0200 Subject: [PATCH 256/688] fixed some more settings bugs --- apps/openmw/mwgui/settingswindow.cpp | 17 +++++++++++++---- apps/openmw/mwrender/renderingmanager.cpp | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 3c8e06dc0..f20b72d4a 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -238,6 +238,12 @@ namespace MWGui mReflectTerrainButton->setEnabled(false); } + if (shaders == "off") + { + mUnderwaterButton->setEnabled (false); + mShadowsEnabledButton->setEnabled(false); + } + mFullscreenButton->setCaptionWithReplacing(Settings::Manager::getBool("fullscreen", "Video") ? "#{sOn}" : "#{sOff}"); mVSyncButton->setCaptionWithReplacing(Settings::Manager::getBool("vsync", "Video") ? "#{sOn}": "#{sOff}"); mFPSButton->setCaptionWithReplacing(fpsLevelToStr(Settings::Manager::getInt("fps", "HUD"))); @@ -409,7 +415,9 @@ namespace MWGui mReflectObjectsButton->setEnabled(false); mReflectActorsButton->setEnabled(false); mReflectTerrainButton->setEnabled(false); + mUnderwaterButton->setEnabled(false); Settings::Manager::setBool("shader", "Water", false); + Settings::Manager::setBool("underwater effect", "Water", false); // shadows not supported mShadowsEnabledButton->setEnabled(false); @@ -418,6 +426,9 @@ namespace MWGui } else { + Settings::Manager::setBool("shaders", "Objects", true); + Settings::Manager::setString("shader mode", "General", val); +/ // re-enable if (MWRender::RenderingManager::waterShaderSupported()) { @@ -425,11 +436,9 @@ namespace MWGui mReflectObjectsButton->setEnabled(true); mReflectActorsButton->setEnabled(true); mReflectTerrainButton->setEnabled(true); - mShadowsEnabledButton->setEnabled(true); } - - Settings::Manager::setBool("shaders", "Objects", true); - Settings::Manager::setString("shader mode", "General", val); + mUnderwaterButton->setEnabled(true); + mShadowsEnabledButton->setEnabled(true); } apply(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bcf9f2101..c5449cfba 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -104,6 +104,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const if (!Settings::Manager::getBool("shaders", "Objects")) Settings::Manager::setBool("enabled", "Shadows", false); + sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); + sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); From 2d080ce4efef7e6596578acd5537a27240c4d3ca Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 23 Jul 2012 21:47:53 +0200 Subject: [PATCH 257/688] compile fix --- apps/openmw/mwgui/settingswindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f20b72d4a..d8b0c96d7 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -428,7 +428,7 @@ namespace MWGui { Settings::Manager::setBool("shaders", "Objects", true); Settings::Manager::setString("shader mode", "General", val); -/ + // re-enable if (MWRender::RenderingManager::waterShaderSupported()) { From d36d6aacf49741ccee9c6a6138f55b4123fd9b07 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 23 Jul 2012 23:56:20 +0400 Subject: [PATCH 258/688] move Scene::insertObject to CellStore::insertObject, part 1 --- apps/openmw/mwworld/cellstore.cpp | 107 +++++++++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 6 ++ apps/openmw/mwworld/scene.cpp | 126 ------------------------------ 3 files changed, 113 insertions(+), 126 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 60a7eb6e1..51a46ec3c 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -121,4 +121,111 @@ namespace MWWorld } } } + + /// \todo this whole code needs major clean up + void CellStore::insertObject(const Ptr &ptr) + { + std::string type = ptr.getTypeName(); + + MWWorld::Ptr newPtr; + + // insert into the correct CellRefList + if (type == typeid(ESM::Potion).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&potions.insert(*ref), cell); + } + else if (type == typeid(ESM::Apparatus).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&appas.insert(*ref), cell); + } + else if (type == typeid(ESM::Armor).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&armors.insert(*ref), cell); + } + else if (type == typeid(ESM::Book).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&books.insert(*ref), cell); + } + else if (type == typeid(ESM::Clothing).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&clothes.insert(*ref), cell); + } + else if (type == typeid(ESM::Ingredient).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&ingreds.insert(*ref), cell); + } + else if (type == typeid(ESM::Light).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&lights.insert(*ref), cell); + } + else if (type == typeid(ESM::Tool).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&lockpicks.insert(*ref), cell); + } + else if (type == typeid(ESM::Repair).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&repairs.insert(*ref), cell); + } + else if (type == typeid(ESM::Probe).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&probes.insert(*ref), cell); + } + else if (type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&weapons.insert(*ref), cell); + } + else if (type == typeid(ESM::Miscellaneous).name()) + { + + // if this is gold, we need to fetch the correct mesh depending on the amount of gold. + if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + { + int goldAmount = ptr.getRefData().getCount(); + + std::string base = "Gold_001"; + if (goldAmount >= 100) + base = "Gold_100"; + else if (goldAmount >= 25) + base = "Gold_025"; + else if (goldAmount >= 10) + base = "Gold_010"; + else if (goldAmount >= 5) + base = "Gold_005"; + + MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); + + MWWorld::LiveCellRef* ref = + newRef.getPtr().get(); + + newPtr = MWWorld::Ptr(&miscItems.insert(*ref), cell); + + ESM::Position& p = newPtr.getRefData().getPosition(); + p.pos[0] = ptr.getRefData().getPosition().pos[0]; + p.pos[1] = ptr.getRefData().getPosition().pos[1]; + p.pos[2] = ptr.getRefData().getPosition().pos[2]; + } + else + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&miscItems.insert(*ref), cell); + } + } + else + throw std::runtime_error("Trying to insert object of unhandled type"); + + newPtr.getRefData().setCount(ptr.getRefData().getCount()); + ptr.getRefData().setCount(0); + newPtr.getRefData().enable(); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index de3ac12ae..89724bca4 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -7,6 +7,7 @@ #include #include "refdata.hpp" +#include "ptr.hpp" namespace ESMS { @@ -63,6 +64,11 @@ namespace MWWorld list.push_back(LiveRef(ref, obj)); } + const LiveRef &insert(LiveRef &item) { + list.push_back(item); + return list.back(); + } + LiveRef *find (const std::string& name) { for (typename std::list::iterator iter (list.begin()); iter!=list.end(); ++iter) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 33c67aad8..d2725b00e 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -335,132 +335,6 @@ namespace MWWorld } - /// \todo this whole code needs major clean up, and doesn't belong in this class. - void Scene::insertObject (const Ptr& ptr, CellStore* cell) - { - std::string type = ptr.getTypeName(); - - MWWorld::Ptr newPtr; - - // insert into the correct CellRefList - if (type == typeid(ESM::Potion).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->potions.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); - } - else if (type == typeid(ESM::Apparatus).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->appas.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); - } - else if (type == typeid(ESM::Armor).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->armors.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); - } - else if (type == typeid(ESM::Book).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->books.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); - } - else if (type == typeid(ESM::Clothing).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->clothes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); - } - else if (type == typeid(ESM::Ingredient).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->ingreds.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); - } - else if (type == typeid(ESM::Light).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lights.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); - } - else if (type == typeid(ESM::Tool).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lockpicks.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); - } - else if (type == typeid(ESM::Repair).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->repairs.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); - } - else if (type == typeid(ESM::Probe).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->probes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); - } - else if (type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->weapons.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); - } - else if (type == typeid(ESM::Miscellaneous).name()) - { - - // if this is gold, we need to fetch the correct mesh depending on the amount of gold. - if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) - { - int goldAmount = ptr.getRefData().getCount(); - - std::string base = "Gold_001"; - if (goldAmount >= 100) - base = "Gold_100"; - else if (goldAmount >= 25) - base = "Gold_025"; - else if (goldAmount >= 10) - base = "Gold_010"; - else if (goldAmount >= 5) - base = "Gold_005"; - - MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); - - MWWorld::LiveCellRef* ref = newRef.getPtr().get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - - ESM::Position& p = newPtr.getRefData().getPosition(); - p.pos[0] = ptr.getRefData().getPosition().pos[0]; - p.pos[1] = ptr.getRefData().getPosition().pos[1]; - p.pos[2] = ptr.getRefData().getPosition().pos[2]; - } - else - { - MWWorld::LiveCellRef* ref = ptr.get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - } - } - else - throw std::runtime_error("Trying to insert object of unhandled type"); - - - - newPtr.getRefData().setCount(ptr.getRefData().getCount()); - ptr.getRefData().setCount(0); - newPtr.getRefData().enable(); - - mRendering.addObject(newPtr); - MWWorld::Class::get(newPtr).insertObject(newPtr, *mPhysics); - - } - void Scene::addObjectToScene (const Ptr& ptr) { mRendering.addObject (ptr); From b76022517935b7d5e16f34100143c0beec3fa253 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 24 Jul 2012 00:00:10 +0400 Subject: [PATCH 259/688] wrong branch This reverts commit d36d6aacf49741ccee9c6a6138f55b4123fd9b07. --- apps/openmw/mwworld/cellstore.cpp | 107 ------------------------- apps/openmw/mwworld/cellstore.hpp | 6 -- apps/openmw/mwworld/scene.cpp | 126 ++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 113 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 51a46ec3c..60a7eb6e1 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -121,111 +121,4 @@ namespace MWWorld } } } - - /// \todo this whole code needs major clean up - void CellStore::insertObject(const Ptr &ptr) - { - std::string type = ptr.getTypeName(); - - MWWorld::Ptr newPtr; - - // insert into the correct CellRefList - if (type == typeid(ESM::Potion).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&potions.insert(*ref), cell); - } - else if (type == typeid(ESM::Apparatus).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&appas.insert(*ref), cell); - } - else if (type == typeid(ESM::Armor).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&armors.insert(*ref), cell); - } - else if (type == typeid(ESM::Book).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&books.insert(*ref), cell); - } - else if (type == typeid(ESM::Clothing).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&clothes.insert(*ref), cell); - } - else if (type == typeid(ESM::Ingredient).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&ingreds.insert(*ref), cell); - } - else if (type == typeid(ESM::Light).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&lights.insert(*ref), cell); - } - else if (type == typeid(ESM::Tool).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&lockpicks.insert(*ref), cell); - } - else if (type == typeid(ESM::Repair).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&repairs.insert(*ref), cell); - } - else if (type == typeid(ESM::Probe).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&probes.insert(*ref), cell); - } - else if (type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&weapons.insert(*ref), cell); - } - else if (type == typeid(ESM::Miscellaneous).name()) - { - - // if this is gold, we need to fetch the correct mesh depending on the amount of gold. - if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) - { - int goldAmount = ptr.getRefData().getCount(); - - std::string base = "Gold_001"; - if (goldAmount >= 100) - base = "Gold_100"; - else if (goldAmount >= 25) - base = "Gold_025"; - else if (goldAmount >= 10) - base = "Gold_010"; - else if (goldAmount >= 5) - base = "Gold_005"; - - MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); - - MWWorld::LiveCellRef* ref = - newRef.getPtr().get(); - - newPtr = MWWorld::Ptr(&miscItems.insert(*ref), cell); - - ESM::Position& p = newPtr.getRefData().getPosition(); - p.pos[0] = ptr.getRefData().getPosition().pos[0]; - p.pos[1] = ptr.getRefData().getPosition().pos[1]; - p.pos[2] = ptr.getRefData().getPosition().pos[2]; - } - else - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&miscItems.insert(*ref), cell); - } - } - else - throw std::runtime_error("Trying to insert object of unhandled type"); - - newPtr.getRefData().setCount(ptr.getRefData().getCount()); - ptr.getRefData().setCount(0); - newPtr.getRefData().enable(); - } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 89724bca4..de3ac12ae 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -7,7 +7,6 @@ #include #include "refdata.hpp" -#include "ptr.hpp" namespace ESMS { @@ -64,11 +63,6 @@ namespace MWWorld list.push_back(LiveRef(ref, obj)); } - const LiveRef &insert(LiveRef &item) { - list.push_back(item); - return list.back(); - } - LiveRef *find (const std::string& name) { for (typename std::list::iterator iter (list.begin()); iter!=list.end(); ++iter) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index d2725b00e..33c67aad8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -335,6 +335,132 @@ namespace MWWorld } + /// \todo this whole code needs major clean up, and doesn't belong in this class. + void Scene::insertObject (const Ptr& ptr, CellStore* cell) + { + std::string type = ptr.getTypeName(); + + MWWorld::Ptr newPtr; + + // insert into the correct CellRefList + if (type == typeid(ESM::Potion).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->potions.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); + } + else if (type == typeid(ESM::Apparatus).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->appas.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); + } + else if (type == typeid(ESM::Armor).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->armors.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); + } + else if (type == typeid(ESM::Book).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->books.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); + } + else if (type == typeid(ESM::Clothing).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->clothes.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); + } + else if (type == typeid(ESM::Ingredient).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->ingreds.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); + } + else if (type == typeid(ESM::Light).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->lights.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); + } + else if (type == typeid(ESM::Tool).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->lockpicks.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); + } + else if (type == typeid(ESM::Repair).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->repairs.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); + } + else if (type == typeid(ESM::Probe).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->probes.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); + } + else if (type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + cell->weapons.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); + } + else if (type == typeid(ESM::Miscellaneous).name()) + { + + // if this is gold, we need to fetch the correct mesh depending on the amount of gold. + if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + { + int goldAmount = ptr.getRefData().getCount(); + + std::string base = "Gold_001"; + if (goldAmount >= 100) + base = "Gold_100"; + else if (goldAmount >= 25) + base = "Gold_025"; + else if (goldAmount >= 10) + base = "Gold_010"; + else if (goldAmount >= 5) + base = "Gold_005"; + + MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); + + MWWorld::LiveCellRef* ref = newRef.getPtr().get(); + + cell->miscItems.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); + + ESM::Position& p = newPtr.getRefData().getPosition(); + p.pos[0] = ptr.getRefData().getPosition().pos[0]; + p.pos[1] = ptr.getRefData().getPosition().pos[1]; + p.pos[2] = ptr.getRefData().getPosition().pos[2]; + } + else + { + MWWorld::LiveCellRef* ref = ptr.get(); + + cell->miscItems.list.push_back( *ref ); + newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); + } + } + else + throw std::runtime_error("Trying to insert object of unhandled type"); + + + + newPtr.getRefData().setCount(ptr.getRefData().getCount()); + ptr.getRefData().setCount(0); + newPtr.getRefData().enable(); + + mRendering.addObject(newPtr); + MWWorld::Class::get(newPtr).insertObject(newPtr, *mPhysics); + + } + void Scene::addObjectToScene (const Ptr& ptr) { mRendering.addObject (ptr); From f67983bbee071b886f27682c322fb66628db2b44 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 23 Jul 2012 13:37:05 -0700 Subject: [PATCH 260/688] Don't assume one sub-entity for the night sky entities --- apps/openmw/mwrender/sky.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index d928995f4..3dd038659 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -316,22 +316,25 @@ void SkyManager::create() // Stars mAtmosphereNight = mRootNode->createChildSceneNode(); NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, "meshes\\sky_night_01.nif"); - for(size_t i = 0;i < entities.mEntities.size();i++) + for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++) { Entity* night1_ent = entities.mEntities[i]; night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); night1_ent->setVisibilityFlags(RV_Sky); night1_ent->setCastShadows(false); - std::string matName = "openmw_stars_" + boost::lexical_cast(i); - sh::MaterialInstance* m = sh::Factory::getInstance ().createMaterialInstance (matName, "openmw_stars"); + for (unsigned int j=0; jgetNumSubEntities(); ++j) + { + std::string matName = "openmw_stars_" + boost::lexical_cast(matidx++); + sh::MaterialInstance* m = sh::Factory::getInstance().createMaterialInstance(matName, "openmw_stars"); - std::string textureName = sh::retrieveValue( - sh::Factory::getInstance().getMaterialInstance(night1_ent->getSubEntity (0)->getMaterialName ())->getProperty("diffuseMap"), NULL).get(); + std::string textureName = sh::retrieveValue( + sh::Factory::getInstance().getMaterialInstance(night1_ent->getSubEntity(j)->getMaterialName())->getProperty("diffuseMap"), NULL).get(); - m->setProperty ("texture", sh::makeProperty(new sh::StringValue(textureName))); + m->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); - night1_ent->getSubEntity(0)->setMaterialName (matName); + night1_ent->getSubEntity(j)->setMaterialName(matName); + } } From 8c8228a15ca76b45b8a059830ea2f49689df3e1c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jul 2012 00:08:53 +0200 Subject: [PATCH 261/688] fix terrain num lights setting --- apps/openmw/mwrender/renderingmanager.cpp | 1 + files/materials/terrain.shader | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c5449cfba..bee4bd68a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -110,6 +110,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); sh::Factory::getInstance ().setGlobalSetting ("num_lights", Settings::Manager::getString ("num lights", "Objects")); + sh::Factory::getInstance ().setGlobalSetting ("terrain_num_lights", Settings::Manager::getString ("num lights", "Terrain")); sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index a562d1ec3..601f287a0 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -161,7 +161,7 @@ #if LIGHTING shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - @shForeach(@shGlobalSettingString(num_lights)) + @shForeach(@shGlobalSettingString(terrain_num_lights)) shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) shUniform(float4, lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) shUniform(float4, lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) @@ -298,7 +298,7 @@ float3 diffuse = float3(0,0,0); float d; - @shForeach(@shGlobalSettingString(num_lights)) + @shForeach(@shGlobalSettingString(terrain_num_lights)) lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePosition.xyz * lightPosObjSpace@shIterator.w); d = length(lightDir); From 36be1536d90a5d927854e0c66eca5efbe17c4767 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 23 Jul 2012 17:20:47 -0700 Subject: [PATCH 262/688] Return text keys from NIFs when creating entities --- apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwrender/objects.cpp | 2 +- apps/openmw/mwrender/sky.cpp | 6 ++-- components/nifogre/ogre_nif_loader.cpp | 36 ++++++++++++++++------ components/nifogre/ogre_nif_loader.hpp | 8 +++-- 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 9d2a58a1e..4086f4f7a 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,7 +26,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O { std::string mesh = "meshes\\" + ref->base->model; - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, mesh); + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, NULL, mesh); for(size_t i = 0;i < mEntityList.mEntities.size();i++) { Ogre::Entity *ent = mEntityList.mEntities[i]; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 415de5859..47135b35d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -89,7 +89,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, smodel); + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, NULL, smodel); for(size_t i = 0;i < mEntityList.mEntities.size();i++) { Ogre::Entity *base = mEntityList.mEntities[i]; diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 3af99b469..ecd1328c4 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -93,7 +93,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) assert(insert); Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, mesh); + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, NULL, mesh); for(size_t i = 0;i < entities.mEntities.size();i++) { const Ogre::AxisAlignedBox &tmp = entities.mEntities[i]->getBoundingBox(); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index a87f56ad8..9e551ba2a 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -327,7 +327,7 @@ void SkyManager::create() // Stars mAtmosphereNight = mRootNode->createChildSceneNode(); - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, "meshes\\sky_night_01.nif"); + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, NULL, "meshes\\sky_night_01.nif"); for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++) { Entity* night1_ent = entities.mEntities[i]; @@ -352,7 +352,7 @@ void SkyManager::create() // Atmosphere (day) mAtmosphereDay = mRootNode->createChildSceneNode(); - entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, "meshes\\sky_atmosphere.nif"); + entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, NULL, "meshes\\sky_atmosphere.nif"); for(size_t i = 0;i < entities.mEntities.size();i++) { Entity* atmosphere_ent = entities.mEntities[i]; @@ -366,7 +366,7 @@ void SkyManager::create() // Clouds SceneNode* clouds_node = mRootNode->createChildSceneNode(); - entities = NifOgre::NIFLoader::createEntities(clouds_node, "meshes\\sky_clouds_01.nif"); + entities = NifOgre::NIFLoader::createEntities(clouds_node, NULL, "meshes\\sky_clouds_01.nif"); for(size_t i = 0;i < entities.mEntities.size();i++) { Entity* clouds_ent = entities.mEntities[i]; diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 3338b8cdd..9df8dd916 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -342,8 +342,23 @@ void loadResource(Ogre::Resource *resource) anim->optimise(); } -bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node) +bool createSkeleton(const std::string &name, const std::string &group, TextKeyMap *textkeys, const Nif::Node *node) { + if(textkeys) + { + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + for(size_t i = 0;i < tk->list.size();i++) + (*textkeys)[tk->list[i].time] = tk->list[i].text; + } + e = e->extra; + } + } + if(node->boneTrafo != NULL) { Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); @@ -355,18 +370,19 @@ bool createSkeleton(const std::string &name, const std::string &group, Nif::Node skel = skelMgr.create(name, group, true, loader); } - return true; + if(!textkeys || textkeys->size() > 0) + return true; } - Nif::NiNode *ninode = dynamic_cast(node); + const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { - Nif::NodeList &children = ninode->children; + const Nif::NodeList &children = ninode->children; for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) { - if(createSkeleton(name, group, children[i].getPtr())) + if(createSkeleton(name, group, textkeys, children[i].getPtr())) return true; } } @@ -965,7 +981,7 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) +MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group) { MeshPairList meshes; @@ -992,7 +1008,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: } NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(skelName, group, node); + bool hasSkel = skelldr.createSkeleton(skelName, group, textkeys, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); @@ -1000,11 +1016,11 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: return meshes; } -EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, const std::string &name, const std::string &group) +EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textkeys, const std::string &name, const std::string &group) { EntityList entitylist; - MeshPairList meshes = load(name, name, group); + MeshPairList meshes = load(name, name, textkeys, group); if(meshes.size() == 0) return entitylist; @@ -1053,7 +1069,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo { EntityList entitylist; - MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group); + MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), NULL, group); if(meshes.size() == 0) return entitylist; diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index a9195f2fb..b6610d8a7 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -58,7 +58,8 @@ namespace Nif namespace NifOgre { -// FIXME: This should not be in NifOgre, it works agnostic of what model format is used +// FIXME: These should not be in NifOgre, it works agnostic of what model format is used +typedef std::map TextKeyMap; struct EntityList { std::vector mEntities; Ogre::Entity *mSkelBase; @@ -67,11 +68,11 @@ struct EntityList { { } }; + /** This holds a list of meshes along with the names of their parent nodes */ typedef std::vector< std::pair > MeshPairList; - /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into something Ogre can use. @@ -86,7 +87,7 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { - static MeshPairList load(std::string name, std::string skelName, const std::string &group); + static MeshPairList load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group); public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, @@ -95,6 +96,7 @@ public: const std::string &group="General"); static EntityList createEntities(Ogre::SceneNode *parent, + TextKeyMap *textkeys, const std::string &name, const std::string &group="General"); }; From f953d7f8c0c0145def6fc81f265d931adfca15bf Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 23 Jul 2012 17:27:35 -0700 Subject: [PATCH 263/688] Store text keys from base NIF animations --- apps/openmw/mwrender/animation.hpp | 1 + apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ae1477666..dd1fdc95e 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -30,6 +30,7 @@ protected: bool mSkipFrame; NifOgre::EntityList mEntityList; + NifOgre::TextKeyMap mTextKeys; public: Animation(OEngine::Render::OgreRenderer& _rend); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 4086f4f7a..7fb153bd1 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -26,7 +26,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O { std::string mesh = "meshes\\" + ref->base->model; - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, NULL, mesh); + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, mesh); for(size_t i = 0;i < mEntityList.mEntities.size();i++) { Ogre::Entity *ent = mEntityList.mEntities[i]; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 47135b35d..4f98aebc4 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -89,7 +89,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, NULL, smodel); + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, smodel); for(size_t i = 0;i < mEntityList.mEntities.size();i++) { Ogre::Entity *base = mEntityList.mEntities[i]; From 94ce95c679d111a69d6cc5e6f4126c8b64feabb8 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Wed, 25 Jul 2012 00:13:33 +0400 Subject: [PATCH 264/688] bug #348: works again on OS X --- CMakeLists.txt | 59 +++++++++++++++---------------- apps/launcher/graphicspage.cpp | 8 +++-- libs/openengine/ogre/renderer.cpp | 12 +++++-- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f124f1383..05c53e6d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,13 +227,41 @@ if (APPLE) ${OGRE_Plugin_OctreeSceneManager_LIBRARY_REL} ${OGRE_Plugin_CgProgramManager_LIBRARY_REL} ${OGRE_Plugin_ParticleFX_LIBRARY_REL}) + + if (${OGRE_PLUGIN_DIR_REL}}) + set(OGRE_PLUGINS_REL_FOUND TRUE) + endif () + + if (${OGRE_PLUGIN_DIR_DBG}) + set(OGRE_PLUGINS_DBG_FOUND TRUE) + endif () + + if (${OGRE_PLUGINS_REL_FOUND}) + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + else () + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) + endif () + + set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") + +# set(OGRE_PLUGIN_DIR_2 ${OGRE_PLUGIN_DIR}) +# set(OGRE_PLUGIN_DIR "") +# set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_2}) + + configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist + "${APP_BUNDLE_DIR}/Contents/Info.plist") + + configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns + "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) endif (APPLE) # Set up Ogre plugin folder & debug suffix set(DEBUG_SUFFIX "") +add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") if (DEFINED CMAKE_BUILD_TYPE) - if (CMAKE_BUILD_TYPE STREQUAL "Debug") + # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) + if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) set(DEBUG_SUFFIX "_d") add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") else() @@ -275,35 +303,6 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") "${OpenMW_BINARY_DIR}/openmw.desktop") endif() -if (APPLE) - if (${OGRE_PLUGIN_DIR_REL}}) - set(OGRE_PLUGINS_REL_FOUND TRUE) - endif () - - if (${OGRE_PLUGIN_DIR_DBG}) - set(OGRE_PLUGINS_DBG_FOUND TRUE) - endif () - - if (${OGRE_PLUGINS_REL_FOUND}) - set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) - else () - set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) - endif () - - set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") - - set(OGRE_PLUGIN_DIR_2 ${OGRE_PLUGIN_DIR}) - set(OGRE_PLUGIN_DIR "") - set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_2}) - - configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist - "${APP_BUNDLE_DIR}/Contents/Info.plist") - - configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns - "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) -endif (APPLE) - - # Compiler settings if (CMAKE_COMPILER_IS_GNUCC) add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 3c1d76f3d..296ef2c3e 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -116,11 +116,15 @@ bool GraphicsPage::setupOgre() } std::string glPlugin = std::string(pluginDir) + "/RenderSystem_GL" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(glPlugin + ".so") || boost::filesystem::exists(glPlugin + ".dll")) + if (boost::filesystem::exists(glPlugin + ".so") || + boost::filesystem::exists(glPlugin + ".dll") || + boost::filesystem::exists(glPlugin + ".dylib")) mOgre->loadPlugin (glPlugin); std::string dxPlugin = std::string(pluginDir) + "/RenderSystem_Direct3D9" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(dxPlugin + ".so") || boost::filesystem::exists(dxPlugin + ".dll")) + if (boost::filesystem::exists(dxPlugin + ".so") || + boost::filesystem::exists(dxPlugin + ".dll") || + boost::filesystem::exists(dxPlugin + ".dylib")) mOgre->loadPlugin (dxPlugin); #ifdef ENABLE_PLUGIN_GL diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index e40bdf708..58b363f7f 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -112,15 +112,21 @@ void OgreRenderer::configure(const std::string &logPath, } std::string glPlugin = std::string(pluginDir) + "/RenderSystem_GL" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(glPlugin + ".so") || boost::filesystem::exists(glPlugin + ".dll")) + if (boost::filesystem::exists(glPlugin + ".so") || + boost::filesystem::exists(glPlugin + ".dll") || + boost::filesystem::exists(glPlugin + ".dylib")) mRoot->loadPlugin (glPlugin); std::string dxPlugin = std::string(pluginDir) + "/RenderSystem_Direct3D9" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(dxPlugin + ".so") || boost::filesystem::exists(dxPlugin + ".dll")) + if (boost::filesystem::exists(dxPlugin + ".so") || + boost::filesystem::exists(dxPlugin + ".dll") || + boost::filesystem::exists(dxPlugin + ".dylib")) mRoot->loadPlugin (dxPlugin); std::string cgPlugin = std::string(pluginDir) + "/Plugin_CgProgramManager" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(cgPlugin + ".so") || boost::filesystem::exists(cgPlugin + ".dll")) + if (boost::filesystem::exists(cgPlugin + ".so") || + boost::filesystem::exists(cgPlugin + ".dll") || + boost::filesystem::exists(cgPlugin + ".dylib")) mRoot->loadPlugin (cgPlugin); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); From 9a7a629d0f82bd224a518901757d3282c23015ad Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Jul 2012 13:51:48 -0700 Subject: [PATCH 265/688] Add support for playing animation groups --- apps/openmw/mwrender/animation.cpp | 137 +++++++++++++++++++++++++---- apps/openmw/mwrender/animation.hpp | 8 ++ 2 files changed, 129 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 653a506e8..27ab3995a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -7,6 +7,7 @@ #include #include + namespace MWRender { std::map Animation::sUniqueIDs; @@ -28,16 +29,117 @@ Animation::~Animation() mEntityList.mEntities.clear(); } -void Animation::playGroup(std::string groupname, int mode, int loops) -{ - if(groupname == "all") + +struct checklow { + bool operator()(const char &a, const char &b) const { - mAnimate = loops; - mTime = 0.0f; + return ::tolower(a) == ::tolower(b); } +}; + +bool Animation::findGroupTimes(const std::string &groupname, float *starttime, float *stoptime, float *loopstarttime, float *loopstoptime) +{ + const std::string &start = groupname+": start"; + const std::string &startloop = groupname+": loop start"; + const std::string &stop = groupname+": stop"; + const std::string &stoploop = groupname+": loop stop"; + + *starttime = -1.0f; + *stoptime = -1.0f; + *loopstarttime = -1.0f; + *loopstoptime = -1.0f; + + NifOgre::TextKeyMap::const_iterator iter; + for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++) + { + if(*starttime >= 0.0f && *stoptime >= 0.0f && *loopstarttime >= 0.0f && *loopstoptime >= 0.0f) + return true; + + std::string::const_iterator strpos = iter->second.begin(); + std::string::const_iterator strend = iter->second.end(); + + while(strpos != strend) + { + size_t strlen = strend-strpos; + std::string::const_iterator striter; + + if(start.size() <= strlen && + ((striter=std::mismatch(strpos, strend, start.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) + { + *starttime = iter->first; + *loopstarttime = iter->first; + } + else if(startloop.size() <= strlen && + ((striter=std::mismatch(strpos, strend, startloop.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) + { + *loopstarttime = iter->first; + } + else if(stoploop.size() <= strlen && + ((striter=std::mismatch(strpos, strend, stoploop.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) + { + *loopstoptime = iter->first; + } + else if(stop.size() <= strlen && + ((striter=std::mismatch(strpos, strend, stop.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) + { + *stoptime = iter->first; + if(*loopstoptime < 0.0f) + *loopstoptime = iter->first; + break; + } + + strpos = std::find(strpos+1, strend, '\n'); + while(strpos != strend && *strpos == '\n') + strpos++; + } + } + + return (*starttime >= 0.0f && *stoptime >= 0.0f && *loopstarttime >= 0.0f && *loopstoptime >= 0.0f); } +void Animation::playGroup(std::string groupname, int mode, int loops) +{ + float start, stop, loopstart, loopstop; + + if(groupname == "all") + { + mLoopStartTime = mStartTime = 0.0f; + mLoopStopTime = mStopTime = 0.0f; + + if(mEntityList.mSkelBase) + { + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) + { + Ogre::AnimationState *state = as.getNext(); + mLoopStopTime = mStopTime = state->getLength(); + break; + } + } + + mAnimate = loops; + mTime = mStartTime; + } + else if(findGroupTimes(groupname, &start, &stop, &loopstart, &loopstop)) + { + mStartTime = start; + mStopTime = stop; + mLoopStartTime = loopstart; + mLoopStopTime = loopstop; + + mAnimate = loops; + mTime = mStartTime; + } + else + throw std::runtime_error("Failed to find animation group "+groupname); +} + void Animation::skipAnim() { mSkipFrame = true; @@ -45,9 +147,22 @@ void Animation::skipAnim() void Animation::runAnimation(float timepassed) { - if(mAnimate != 0 && !mSkipFrame) + if(mAnimate > 0 && !mSkipFrame) { mTime += timepassed; + if(mTime >= mLoopStopTime) + { + if(mAnimate > 1) + { + mAnimate--; + mTime = mTime - mLoopStopTime + mLoopStartTime; + } + else if(mTime >= mStopTime) + { + mAnimate--; + mTime = mStopTime; + } + } if(mEntityList.mSkelBase) { @@ -57,16 +172,6 @@ void Animation::runAnimation(float timepassed) { Ogre::AnimationState *state = as.getNext(); state->setTimePosition(mTime); - if(mTime >= state->getLength()) - { - if(mAnimate != -1) - mAnimate--; - //std::cout << "Stopping the animation\n"; - if(mAnimate == 0) - mTime = state->getLength(); - else - mTime = mTime - state->getLength(); - } } } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index dd1fdc95e..fb9330114 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -26,12 +26,20 @@ protected: static std::map sUniqueIDs; float mTime; + float mStartTime; + float mStopTime; + float mLoopStartTime; + float mLoopStopTime; + int mAnimate; bool mSkipFrame; NifOgre::EntityList mEntityList; NifOgre::TextKeyMap mTextKeys; + bool findGroupTimes(const std::string &groupname, float *starttime, float *stoptime, + float *loopstarttime, float *loopstoptime); + public: Animation(OEngine::Render::OgreRenderer& _rend); virtual ~Animation(); From 20121f3b0a1f5732cb4d76e66cc6e1039d0f048f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Jul 2012 13:56:28 -0700 Subject: [PATCH 266/688] Remove some unused stuff --- apps/openmw/mwrender/animation.cpp | 1 - apps/openmw/mwrender/animation.hpp | 6 ------ 2 files changed, 7 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 27ab3995a..6a04f62b5 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -10,7 +10,6 @@ namespace MWRender { -std::map Animation::sUniqueIDs; Animation::Animation(OEngine::Render::OgreRenderer& _rend) : mInsert(NULL) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index fb9330114..cb0e92367 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -14,16 +14,10 @@ namespace MWRender { -struct PosAndRot { - Ogre::Quaternion vecRot; - Ogre::Vector3 vecPos; -}; - class Animation { protected: Ogre::SceneNode* mInsert; OEngine::Render::OgreRenderer &mRend; - static std::map sUniqueIDs; float mTime; float mStartTime; From fd1e3f6ec55f1aee15fd0ab5514fdeb362370623 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Jul 2012 14:14:32 -0700 Subject: [PATCH 267/688] Add support for playgroup mode 2 --- apps/openmw/mwrender/animation.cpp | 33 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 6a04f62b5..f6beeb52f 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -107,8 +107,8 @@ void Animation::playGroup(std::string groupname, int mode, int loops) if(groupname == "all") { - mLoopStartTime = mStartTime = 0.0f; - mLoopStopTime = mStopTime = 0.0f; + start = loopstart = 0.0f; + loopstop = stop = 0.0f; if(mEntityList.mSkelBase) { @@ -117,26 +117,25 @@ void Animation::playGroup(std::string groupname, int mode, int loops) while(as.hasMoreElements()) { Ogre::AnimationState *state = as.getNext(); - mLoopStopTime = mStopTime = state->getLength(); + loopstop = stop = state->getLength(); break; } } - - mAnimate = loops; - mTime = mStartTime; } - else if(findGroupTimes(groupname, &start, &stop, &loopstart, &loopstop)) - { - mStartTime = start; - mStopTime = stop; - mLoopStartTime = loopstart; - mLoopStopTime = loopstop; - - mAnimate = loops; - mTime = mStartTime; - } - else + else if(!findGroupTimes(groupname, &start, &stop, &loopstart, &loopstop)) throw std::runtime_error("Failed to find animation group "+groupname); + + // FIXME: mode = 0 not yet supported + if(mode == 0) + mode = 1; + + mStartTime = start; + mStopTime = stop; + mLoopStartTime = loopstart; + mLoopStopTime = loopstop; + + mAnimate = loops; + mTime = ((mode==1) ? mStartTime : mLoopStartTime); } void Animation::skipAnim() From 13ab2baef0967333d8aad0eee6d964edb2a30734 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Jul 2012 14:42:01 -0700 Subject: [PATCH 268/688] Use a struct to hold the current animation times and remaining loop count --- apps/openmw/mwrender/animation.cpp | 61 +++++++++++++----------------- apps/openmw/mwrender/animation.hpp | 23 +++++++---- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f6beeb52f..fe77da52b 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -15,7 +15,6 @@ Animation::Animation(OEngine::Render::OgreRenderer& _rend) : mInsert(NULL) , mRend(_rend) , mTime(0.0f) - , mAnimate(0) , mSkipFrame(false) { } @@ -36,22 +35,17 @@ struct checklow { } }; -bool Animation::findGroupTimes(const std::string &groupname, float *starttime, float *stoptime, float *loopstarttime, float *loopstoptime) +bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTimes *times) { const std::string &start = groupname+": start"; const std::string &startloop = groupname+": loop start"; const std::string &stop = groupname+": stop"; const std::string &stoploop = groupname+": loop stop"; - *starttime = -1.0f; - *stoptime = -1.0f; - *loopstarttime = -1.0f; - *loopstoptime = -1.0f; - NifOgre::TextKeyMap::const_iterator iter; for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++) { - if(*starttime >= 0.0f && *stoptime >= 0.0f && *loopstarttime >= 0.0f && *loopstoptime >= 0.0f) + if(times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f) return true; std::string::const_iterator strpos = iter->second.begin(); @@ -66,28 +60,28 @@ bool Animation::findGroupTimes(const std::string &groupname, float *starttime, f ((striter=std::mismatch(strpos, strend, start.begin(), checklow()).first) == strend || *striter == '\r' || *striter == '\n')) { - *starttime = iter->first; - *loopstarttime = iter->first; + times->mStart = iter->first; + times->mLoopStart = iter->first; } else if(startloop.size() <= strlen && ((striter=std::mismatch(strpos, strend, startloop.begin(), checklow()).first) == strend || *striter == '\r' || *striter == '\n')) { - *loopstarttime = iter->first; + times->mLoopStart = iter->first; } else if(stoploop.size() <= strlen && ((striter=std::mismatch(strpos, strend, stoploop.begin(), checklow()).first) == strend || *striter == '\r' || *striter == '\n')) { - *loopstoptime = iter->first; + times->mLoopStop = iter->first; } else if(stop.size() <= strlen && ((striter=std::mismatch(strpos, strend, stop.begin(), checklow()).first) == strend || *striter == '\r' || *striter == '\n')) { - *stoptime = iter->first; - if(*loopstoptime < 0.0f) - *loopstoptime = iter->first; + times->mStop = iter->first; + if(times->mLoopStop < 0.0f) + times->mLoopStop = iter->first; break; } @@ -97,18 +91,19 @@ bool Animation::findGroupTimes(const std::string &groupname, float *starttime, f } } - return (*starttime >= 0.0f && *stoptime >= 0.0f && *loopstarttime >= 0.0f && *loopstoptime >= 0.0f); + return (times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f); } void Animation::playGroup(std::string groupname, int mode, int loops) { - float start, stop, loopstart, loopstop; + GroupTimes times; + times.mLoops = loops; if(groupname == "all") { - start = loopstart = 0.0f; - loopstop = stop = 0.0f; + times.mStart = times.mLoopStart = 0.0f; + times.mLoopStop = times.mStop = 0.0f; if(mEntityList.mSkelBase) { @@ -117,25 +112,21 @@ void Animation::playGroup(std::string groupname, int mode, int loops) while(as.hasMoreElements()) { Ogre::AnimationState *state = as.getNext(); - loopstop = stop = state->getLength(); + times.mLoopStop = times.mStop = state->getLength(); break; } } } - else if(!findGroupTimes(groupname, &start, &stop, &loopstart, &loopstop)) + else if(!findGroupTimes(groupname, ×)) throw std::runtime_error("Failed to find animation group "+groupname); // FIXME: mode = 0 not yet supported if(mode == 0) mode = 1; - mStartTime = start; - mStopTime = stop; - mLoopStartTime = loopstart; - mLoopStopTime = loopstop; + mCurGroup = times; - mAnimate = loops; - mTime = ((mode==1) ? mStartTime : mLoopStartTime); + mTime = ((mode==1) ? mCurGroup.mStart : mCurGroup.mLoopStart); } void Animation::skipAnim() @@ -145,20 +136,20 @@ void Animation::skipAnim() void Animation::runAnimation(float timepassed) { - if(mAnimate > 0 && !mSkipFrame) + if(mCurGroup.mLoops > 0 && !mSkipFrame) { mTime += timepassed; - if(mTime >= mLoopStopTime) + if(mTime >= mCurGroup.mLoopStop) { - if(mAnimate > 1) + if(mCurGroup.mLoops > 1) { - mAnimate--; - mTime = mTime - mLoopStopTime + mLoopStartTime; + mCurGroup.mLoops--; + mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart; } - else if(mTime >= mStopTime) + else if(mTime >= mCurGroup.mStop) { - mAnimate--; - mTime = mStopTime; + mCurGroup.mLoops--; + mTime = mCurGroup.mStop; } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index cb0e92367..c9b34a4e5 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -15,24 +15,33 @@ namespace MWRender { class Animation { + struct GroupTimes { + float mStart; + float mStop; + float mLoopStart; + float mLoopStop; + + size_t mLoops; + + GroupTimes() + : mStart(-1.0f), mStop(-1.0f), mLoopStart(-1.0f), mLoopStop(-1.0f), + mLoops(0) + { } + }; + protected: Ogre::SceneNode* mInsert; OEngine::Render::OgreRenderer &mRend; float mTime; - float mStartTime; - float mStopTime; - float mLoopStartTime; - float mLoopStopTime; + GroupTimes mCurGroup; - int mAnimate; bool mSkipFrame; NifOgre::EntityList mEntityList; NifOgre::TextKeyMap mTextKeys; - bool findGroupTimes(const std::string &groupname, float *starttime, float *stoptime, - float *loopstarttime, float *loopstoptime); + bool findGroupTimes(const std::string &groupname, GroupTimes *times); public: Animation(OEngine::Render::OgreRenderer& _rend); From 9f0c1eeb7b8562ac64206eebe81edd701eaba2e3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Jul 2012 14:54:12 -0700 Subject: [PATCH 269/688] Support playgroup mode 0 --- apps/openmw/mwrender/animation.cpp | 23 ++++++++++++++--------- apps/openmw/mwrender/animation.hpp | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index fe77da52b..46f3bdc0d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -120,13 +120,14 @@ void Animation::playGroup(std::string groupname, int mode, int loops) else if(!findGroupTimes(groupname, ×)) throw std::runtime_error("Failed to find animation group "+groupname); - // FIXME: mode = 0 not yet supported - if(mode == 0) - mode = 1; - - mCurGroup = times; - - mTime = ((mode==1) ? mCurGroup.mStart : mCurGroup.mLoopStart); + if(mode == 0 && mCurGroup.mLoops > 0) + mNextGroup = times; + else + { + mCurGroup = times; + mNextGroup = GroupTimes(); + mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart); + } } void Animation::skipAnim() @@ -148,8 +149,12 @@ void Animation::runAnimation(float timepassed) } else if(mTime >= mCurGroup.mStop) { - mCurGroup.mLoops--; - mTime = mCurGroup.mStop; + if(mNextGroup.mLoops > 0) + mTime = mTime - mCurGroup.mStop + mNextGroup.mStart; + else + mTime = mCurGroup.mStop; + mCurGroup = mNextGroup; + mNextGroup = GroupTimes(); } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index c9b34a4e5..3611d35c0 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -35,6 +35,7 @@ protected: float mTime; GroupTimes mCurGroup; + GroupTimes mNextGroup; bool mSkipFrame; From 4f46c8a8db6cbe532492be39d6369e5360662cea Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 24 Jul 2012 14:59:25 -0700 Subject: [PATCH 270/688] Use a functor for the mismatch compare function --- components/nifogre/ogre_nif_loader.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 9df8dd916..803300282 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1057,10 +1057,12 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke return entitylist; } -static bool checklow(const char &a, const char &b) -{ - return ::tolower(a) == ::tolower(b); -} +struct checklow { + bool operator()(const char &a, const char &b) const + { + return ::tolower(a) == ::tolower(b); + } +}; EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, @@ -1081,7 +1083,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(ent->hasSkeleton()) { if(meshes[i].second.length() < filter.length() || - std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow).first != filter.end()) + std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow()).first != filter.end()) { sceneMgr->destroyEntity(ent); meshes.erase(meshes.begin()+i); From 0f40e6fc65b4a983ef6a159cf6076d07a463324b Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 25 Jul 2012 00:47:08 +0200 Subject: [PATCH 271/688] find boost without components so we can use Boost_VERSION --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f124f1383..ea0c41fe2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,6 +186,9 @@ if (UNIX AND NOT APPLE) find_package (Threads) endif() +# find boost without components so we can use Boost_VERSION +find_package(Boost REQUIRED) + set(BOOST_COMPONENTS system filesystem program_options thread) if (Boost_VERSION LESS 104900) From 61cb012ee7dd1f356af408bccc1ed9a59b2461af Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 24 Jul 2012 11:15:20 +0400 Subject: [PATCH 272/688] moving Scene::insertObject to CellStore::insertObject, part 1 --- apps/openmw/mwworld/cellstore.cpp | 116 ++++++++++++++++++++++++++- apps/openmw/mwworld/cellstore.hpp | 9 +++ apps/openmw/mwworld/scene.cpp | 129 ------------------------------ apps/openmw/mwworld/scene.hpp | 4 - apps/openmw/mwworld/worldimp.cpp | 3 +- 5 files changed, 126 insertions(+), 135 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 60a7eb6e1..22c94cf75 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1,10 +1,12 @@ - #include "cellstore.hpp" #include #include +#include "ptr.hpp" +#include "manualref.hpp" + namespace MWWorld { CellStore::CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) @@ -121,4 +123,116 @@ namespace MWWorld } } } + + /// \todo this whole code needs major clean up + void CellStore::insertObject (const Ptr& ptr) + { + std::string type = ptr.getTypeName(); + + MWWorld::Ptr newPtr; + + // insert into the correct CellRefList + if (type == typeid(ESM::Potion).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&potions.insert(*ref), this); + } + else if (type == typeid(ESM::Apparatus).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&appas.insert(*ref), this); + } + else if (type == typeid(ESM::Armor).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&armors.insert(*ref), this); + } + else if (type == typeid(ESM::Book).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&books.insert(*ref), this); + } + else if (type == typeid(ESM::Clothing).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&clothes.insert(*ref), this); + } + else if (type == typeid(ESM::Ingredient).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&ingreds.insert(*ref), this); + } + else if (type == typeid(ESM::Light).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&lights.insert(*ref), this); + } + else if (type == typeid(ESM::Tool).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&lockpicks.insert(*ref), this); + } + else if (type == typeid(ESM::Repair).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&repairs.insert(*ref), this); + } + else if (type == typeid(ESM::Probe).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&probes.insert(*ref), this); + } + else if (type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef* ref = ptr.get(); + newPtr = MWWorld::Ptr(&weapons.insert(*ref), this); + } + else if (type == typeid(ESM::Miscellaneous).name()) + { + + // if this is gold, we need to fetch the correct mesh depending on the amount of gold. + if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + { + int goldAmount = ptr.getRefData().getCount(); + + std::string base = "Gold_001"; + if (goldAmount >= 100) + base = "Gold_100"; + else if (goldAmount >= 25) + base = "Gold_025"; + else if (goldAmount >= 10) + base = "Gold_010"; + else if (goldAmount >= 5) + base = "Gold_005"; + + MWWorld::ManualRef newRef( + MWBase::Environment::get().getWorld()->getStore(), + base); + + MWWorld::LiveCellRef* ref = + newRef.getPtr().get(); + + newPtr = MWWorld::Ptr(&miscItems.insert(*ref), this); + + ESM::Position& p = newPtr.getRefData().getPosition(); + p.pos[0] = ptr.getRefData().getPosition().pos[0]; + p.pos[1] = ptr.getRefData().getPosition().pos[1]; + p.pos[2] = ptr.getRefData().getPosition().pos[2]; + } + else + { + MWWorld::LiveCellRef* ref = + ptr.get(); + + newPtr = MWWorld::Ptr(&miscItems.insert(*ref), this); + } + } + else + throw std::runtime_error("Trying to insert object of unhandled type"); + + newPtr.getRefData().setCount(ptr.getRefData().getCount()); + ptr.getRefData().setCount(0); + newPtr.getRefData().enable(); + } + } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index de3ac12ae..3dcde9359 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -15,6 +15,8 @@ namespace ESMS namespace MWWorld { + class Ptr; + /// A reference to one object (of any type) in a cell. /// /// Constructing this with a CellRef instance in the constructor means that @@ -73,6 +75,11 @@ namespace MWWorld return 0; } + + LiveRef &insert(const LiveRef &item) { + list.push_back(item); + return list.back(); + } }; /// A storage struct for one single cell reference. @@ -165,6 +172,8 @@ namespace MWWorld void listRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm); void loadRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm); + + void insertObject(const MWWorld::Ptr &ptr); }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 33c67aad8..9786c24d9 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -9,8 +9,6 @@ #include "../mwgui/window_manager.hpp" -#include "../mwworld/manualref.hpp" /// FIXME - #include "player.hpp" #include "localscripts.hpp" @@ -334,133 +332,6 @@ namespace MWWorld insertCellRefList(mRendering, cell.weapons, cell, *mPhysics); } - - /// \todo this whole code needs major clean up, and doesn't belong in this class. - void Scene::insertObject (const Ptr& ptr, CellStore* cell) - { - std::string type = ptr.getTypeName(); - - MWWorld::Ptr newPtr; - - // insert into the correct CellRefList - if (type == typeid(ESM::Potion).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->potions.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); - } - else if (type == typeid(ESM::Apparatus).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->appas.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); - } - else if (type == typeid(ESM::Armor).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->armors.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); - } - else if (type == typeid(ESM::Book).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->books.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); - } - else if (type == typeid(ESM::Clothing).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->clothes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); - } - else if (type == typeid(ESM::Ingredient).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->ingreds.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); - } - else if (type == typeid(ESM::Light).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lights.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); - } - else if (type == typeid(ESM::Tool).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lockpicks.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); - } - else if (type == typeid(ESM::Repair).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->repairs.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); - } - else if (type == typeid(ESM::Probe).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->probes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); - } - else if (type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->weapons.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); - } - else if (type == typeid(ESM::Miscellaneous).name()) - { - - // if this is gold, we need to fetch the correct mesh depending on the amount of gold. - if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) - { - int goldAmount = ptr.getRefData().getCount(); - - std::string base = "Gold_001"; - if (goldAmount >= 100) - base = "Gold_100"; - else if (goldAmount >= 25) - base = "Gold_025"; - else if (goldAmount >= 10) - base = "Gold_010"; - else if (goldAmount >= 5) - base = "Gold_005"; - - MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); - - MWWorld::LiveCellRef* ref = newRef.getPtr().get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - - ESM::Position& p = newPtr.getRefData().getPosition(); - p.pos[0] = ptr.getRefData().getPosition().pos[0]; - p.pos[1] = ptr.getRefData().getPosition().pos[1]; - p.pos[2] = ptr.getRefData().getPosition().pos[2]; - } - else - { - MWWorld::LiveCellRef* ref = ptr.get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - } - } - else - throw std::runtime_error("Trying to insert object of unhandled type"); - - - - newPtr.getRefData().setCount(ptr.getRefData().getCount()); - ptr.getRefData().setCount(0); - newPtr.getRefData().enable(); - - mRendering.addObject(newPtr); - MWWorld::Class::get(newPtr).insertObject(newPtr, *mPhysics); - - } - void Scene::addObjectToScene (const Ptr& ptr) { mRendering.addObject (ptr); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index c0b93796a..f5f4b640b 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -88,10 +88,6 @@ namespace MWWorld void insertCell (Ptr::CellStore &cell); - /// this method is only meant for dropping objects into the gameworld from a container - /// and thus only handles object types that can be placed in a container - void insertObject (const Ptr& object, CellStore* cell); - void update (float duration); void addObjectToScene (const Ptr& ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a68f08e34..4ffcd33f8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1021,7 +1021,8 @@ namespace MWWorld pos.pos[1] = -result.second[2]; pos.pos[2] = result.second[1]; - mWorldScene->insertObject(object, cell); + cell->insertObject(object); + //TODO mWorldScene->addObjectToScene /// \todo retrieve the bounds of the object and translate it accordingly From 71253c64ab8f11205fef1dc191be78275ff80937 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 24 Jul 2012 12:30:59 +0400 Subject: [PATCH 273/688] moving part 2, adding position parameter, stable --- apps/openmw/mwworld/cellstore.cpp | 13 +++++++++++-- apps/openmw/mwworld/cellstore.hpp | 4 ++-- apps/openmw/mwworld/worldimp.cpp | 16 +++++++--------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 22c94cf75..088c0c623 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -4,8 +4,12 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "ptr.hpp" #include "manualref.hpp" +#include "class.hpp" namespace MWWorld { @@ -125,7 +129,7 @@ namespace MWWorld } /// \todo this whole code needs major clean up - void CellStore::insertObject (const Ptr& ptr) + const MWWorld::Ptr CellStore::insertObject (const Ptr& ptr, const ESM::Position &pos) { std::string type = ptr.getTypeName(); @@ -213,11 +217,12 @@ namespace MWWorld newRef.getPtr().get(); newPtr = MWWorld::Ptr(&miscItems.insert(*ref), this); - + /* ESM::Position& p = newPtr.getRefData().getPosition(); p.pos[0] = ptr.getRefData().getPosition().pos[0]; p.pos[1] = ptr.getRefData().getPosition().pos[1]; p.pos[2] = ptr.getRefData().getPosition().pos[2]; + */ } else { @@ -230,9 +235,13 @@ namespace MWWorld else throw std::runtime_error("Trying to insert object of unhandled type"); + newPtr.getRefData().getPosition() = pos; + newPtr.getRefData().setCount(ptr.getRefData().getCount()); ptr.getRefData().setCount(0); newPtr.getRefData().enable(); + + return newPtr; } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 3dcde9359..1bf233a50 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -155,6 +155,8 @@ namespace MWWorld forEachImp (functor, weapons); } + const MWWorld::Ptr insertObject(const MWWorld::Ptr &ptr, const ESM::Position &pos); + private: template @@ -172,8 +174,6 @@ namespace MWWorld void listRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm); void loadRefs(const ESMS::ESMStore &store, ESM::ESMReader &esm); - - void insertObject(const MWWorld::Ptr &ptr); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4ffcd33f8..efb17a709 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1016,13 +1016,13 @@ namespace MWWorld else cell = getPlayer().getPlayer().getCell(); - ESM::Position& pos = object.getRefData().getPosition(); + ESM::Position pos = getPlayer().getPlayer().getRefData().getPosition(); pos.pos[0] = result.second[0]; pos.pos[1] = -result.second[2]; pos.pos[2] = result.second[1]; - cell->insertObject(object); - //TODO mWorldScene->addObjectToScene + MWWorld::Ptr dropped = cell->insertObject(object, pos); + mWorldScene->addObjectToScene(dropped); /// \todo retrieve the bounds of the object and translate it accordingly @@ -1044,14 +1044,12 @@ namespace MWWorld { MWWorld::Ptr::CellStore* cell = getPlayer().getPlayer().getCell(); - float* playerPos = getPlayer().getPlayer().getRefData().getPosition().pos; + ESM::Position &pos = + getPlayer().getPlayer().getRefData().getPosition(); - ESM::Position& pos = object.getRefData().getPosition(); - pos.pos[0] = playerPos[0]; - pos.pos[1] = playerPos[1]; - pos.pos[2] = playerPos[2]; + MWWorld::Ptr dropped = cell->insertObject(object, pos); - mWorldScene->insertObject(object, cell); + mWorldScene->addObjectToScene(dropped); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) From 87050e48c8d757afe2c1fe9819c06bb9c7d978e6 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 24 Jul 2012 18:52:08 +0400 Subject: [PATCH 274/688] physics getObjectHeight(), MWWorld::Class::getModel() definition --- apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/physicssystem.cpp | 10 ++++++++++ apps/openmw/mwworld/physicssystem.hpp | 2 ++ libs/openengine/bullet/physic.cpp | 21 +++++++++++++++++++++ libs/openengine/bullet/physic.hpp | 2 ++ 6 files changed, 42 insertions(+) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 65fa91666..f973301c5 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -212,4 +212,9 @@ namespace MWWorld void Class::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const { } + + std::string Class::getModel() const + { + return ""; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 3d3ed71ec..5f16ef593 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -204,6 +204,8 @@ namespace MWWorld virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; + + virtual std::string getModel() const; }; } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index bf5c001db..db1db0505 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -14,6 +14,7 @@ #include "../mwbase/world.hpp" // FIXME #include "ptr.hpp" +#include "class.hpp" using namespace Ogre; namespace MWWorld @@ -365,4 +366,13 @@ namespace MWWorld addActor (node->getName(), model, node->getPosition()); } + float PhysicsSystem::getObjectHeight(const MWWorld::Ptr &ptr) { + std::string model = MWWorld::Class::get(ptr).getModel(); + if (model.empty()) { + return 0.0; + } + float scale = ptr.getRefData().getBaseNode()->getScale().x; + return mEngine->getObjectHeight(model, scale); + } + } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index a6b679833..61e2590bd 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -65,6 +65,8 @@ namespace MWWorld void setCurrentWater(bool hasWater, int waterHeight); + float getObjectHeight(const MWWorld::Ptr &ptr); + private: OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 61d0c7b0e..416d0c09d 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -568,4 +568,25 @@ namespace Physic return results2; } + + float PhysicEngine::getObjectHeight(const std::string &mesh, float scale) { + char uniqueID[8]; + sprintf( uniqueID, "%07.3f", scale ); + std::string sid = uniqueID; + std::string outputstring = mesh + uniqueID + "\"|"; + + mShapeLoader->load(outputstring, "General"); + BulletShapeManager::getSingletonPtr()->load(outputstring, "General"); + BulletShapePtr shape = + BulletShapeManager::getSingleton().getByName(outputstring, "General"); + + + btTransform trans; + btVector3 min, max; + + trans.setIdentity(); + shape->Shape->getAabb(trans, min, max); + + return max.z() - min.z(); + } }}; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 3988c75a4..22c5fa274 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -221,6 +221,8 @@ namespace Physic bool toggleDebugRendering(); + float getObjectHeight(const std::string &mesh, float scale); + /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). */ From 6a3a728a56f76d2a74277f1b52b77355c649b3e0 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 24 Jul 2012 20:22:11 +0400 Subject: [PATCH 275/688] Class::getModel implementation --- apps/openmw/mwclass/activator.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/activator.hpp | 2 ++ apps/openmw/mwclass/apparatus.cpp | 31 ++++++++++++++------------- apps/openmw/mwclass/apparatus.hpp | 2 ++ apps/openmw/mwclass/armor.cpp | 28 +++++++++++++----------- apps/openmw/mwclass/armor.hpp | 1 + apps/openmw/mwclass/book.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/book.hpp | 2 ++ apps/openmw/mwclass/clothing.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/clothing.hpp | 2 ++ apps/openmw/mwclass/container.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/container.hpp | 2 ++ apps/openmw/mwclass/creature.cpp | 18 +++++++++++----- apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/door.cpp | 29 ++++++++++++++----------- apps/openmw/mwclass/door.hpp | 2 ++ apps/openmw/mwclass/ingredient.cpp | 27 ++++++++++++----------- apps/openmw/mwclass/ingredient.hpp | 2 ++ apps/openmw/mwclass/light.cpp | 25 +++++++++++++++------ apps/openmw/mwclass/light.hpp | 2 ++ apps/openmw/mwclass/lockpick.cpp | 30 +++++++++++++------------- apps/openmw/mwclass/lockpick.hpp | 2 ++ apps/openmw/mwclass/misc.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/misc.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 30 +++++++++++++++----------- apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwclass/potion.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/potion.hpp | 2 ++ apps/openmw/mwclass/probe.cpp | 30 +++++++++++++------------- apps/openmw/mwclass/probe.hpp | 2 ++ apps/openmw/mwclass/repair.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/repair.hpp | 2 ++ apps/openmw/mwclass/static.cpp | 28 +++++++++++++----------- apps/openmw/mwclass/static.hpp | 2 ++ apps/openmw/mwclass/weapon.cpp | 29 +++++++++++++------------ apps/openmw/mwclass/weapon.hpp | 1 + apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/physicssystem.cpp | 3 +-- 39 files changed, 312 insertions(+), 237 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 81a47ccb0..33baad7ca 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -19,32 +19,33 @@ namespace MWClass { void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Activator::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Activator::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Activator::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 223dd0a36..2e947a9fc 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -28,6 +28,8 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 7e3c3b8f9..dd255f9f8 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -22,34 +22,35 @@ namespace MWClass { - void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Apparatus::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Apparatus::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index f33f92e2c..fa77eec93 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -48,6 +48,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 380c596d7..8bbb198cc 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -27,31 +27,33 @@ namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Armor::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Armor::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Armor::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index a63806162..181a4ce61 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -66,6 +66,7 @@ namespace MWClass const; ///< Generate action for using via inventory menu + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index a37da0fd7..62b6c8204 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -23,32 +23,33 @@ namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Book::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Book::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Book::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index ee3aac8d8..20ea89274 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -50,6 +50,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 6c34b5e56..e1ec5a61c 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -25,32 +25,33 @@ namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Clothing::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Clothing::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Clothing::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index aba317be0..6b5fe1e96 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -59,6 +59,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 8dd27db42..7c5719b8e 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -54,32 +54,33 @@ namespace MWClass void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Container::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Container::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } boost::shared_ptr Container::activate (const MWWorld::Ptr& ptr, diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 739c75c77..17a9ac4ea 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -51,6 +51,8 @@ namespace MWClass ///< Unlock object static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 83370478f..178a6f536 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -86,17 +86,25 @@ namespace MWClass } void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()){ + physics.insertActorPhysics(ptr, model); + } + MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + } + + std::string Creature::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert (ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertActorPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - - MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + return ""; } std::string Creature::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 1274be09a..b994e0831 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -54,6 +54,8 @@ namespace MWClass /// effects). Throws an exception, if the object can't hold other objects. static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index b0bba2c03..1274219a9 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -25,30 +25,33 @@ namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Door::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Door::getModel(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } + return ""; } std::string Door::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 63d1c1ab8..2612df41c 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -38,6 +38,8 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 01146fe67..a2f14c02c 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -23,30 +23,33 @@ namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Ingredient::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } + return ""; } std::string Ingredient::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 4c45bd69c..ad87dd46c 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -44,6 +44,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 15cd89ac2..37d7d1eec 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -28,8 +28,8 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); - assert (ref->base != NULL); + const std::string &model = ref->base->model; MWRender::Objects& objects = renderingInterface.getObjects(); @@ -50,20 +50,31 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); - assert (ref->base != NULL); + const std::string &model = ref->base->model; - if(!model.empty()){ + if(!model.empty()) { physics.insertObjectPhysics(ptr, "meshes\\" + model); } - - if (!ref->base->sound.empty()) - { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop); + if (!ref->base->sound.empty()) { + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop); } } + std::string Light::getModel(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + assert (ref->base != NULL); + + const std::string &model = ref->base->model; + if (!model.empty()) { + return "meshes\\" + model; + } + return ""; + } + std::string Light::getName (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 91193dfdc..ab8854efd 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -52,6 +52,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index d3d60315f..ae2b5de89 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -25,35 +25,35 @@ namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Lockpick::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } - std::string Lockpick::getName (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 26aab584c..45a02d40d 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -52,6 +52,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 8484a5dd1..89c1ef25f 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -27,32 +27,33 @@ namespace MWClass { void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index da5f0df96..23b5e9564 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -44,6 +44,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 80bff73fa..a5525fcda 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -125,25 +125,29 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { + physics.insertActorPhysics(ptr, getModel(ptr)); + MWBase::Environment::get().getMechanicsManager()->addActor(ptr); + } + std::string Npc::getModel(const MWWorld::Ptr &ptr) const + { MWWorld::LiveCellRef *ref = ptr.get(); - - assert (ref->base != NULL); - - + assert(ref->base != NULL); std::string headID = ref->base->head; - std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); - bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; + int end = headID.find_last_of("head_") - 4; + std::string bodyRaceID = headID.substr(0, end); - std::string smodel = "meshes\\base_anim.nif"; - if(beast) - smodel = "meshes\\base_animkna.nif"; - physics.insertActorPhysics(ptr, smodel); - - - MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + std::string model = "meshes\\base_anim.nif"; + if (bodyRaceID == "b_n_khajiit_m_" || + bodyRaceID == "b_n_khajiit_f_" || + bodyRaceID == "b_n_argonian_m_" || + bodyRaceID == "b_n_argonian_f_") + { + model = "meshes\\base_animkna.nif"; + } + return model; } std::string Npc::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index b32a162a1..0ea45d132 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -88,6 +88,8 @@ namespace MWClass virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 45cb07840..92a951d03 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -25,32 +25,33 @@ namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Potion::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Potion::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Potion::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 101f4cefa..39edfd760 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -47,6 +47,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index f3a8406f5..033eee4c5 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -25,35 +25,35 @@ namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Probe::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Probe::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } - std::string Probe::getName (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 51b046fda..ff10eb9d6 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -52,6 +52,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 464ba1091..4258ad106 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -23,32 +23,33 @@ namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Repair::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Repair::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Repair::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 1e935e154..689850b90 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -44,6 +44,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 9b166b076..a14adc300 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -13,31 +13,33 @@ namespace MWClass { void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), true); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Static::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Static::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); - assert (ref->base != NULL); const std::string &model = ref->base->model; - - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } + return ""; } std::string Static::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index c223df1ac..3066933e4 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -19,6 +19,8 @@ namespace MWClass /// can return an empty string. static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 099312d2c..6412a46b1 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -25,32 +25,33 @@ namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Weapon::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Weapon::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Weapon::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 92d703b4a..eaf5b60a4 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -66,6 +66,7 @@ namespace MWClass const; ///< Generate action for using via inventory menu + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index f973301c5..729746f6b 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -213,7 +213,7 @@ namespace MWWorld { } - std::string Class::getModel() const + std::string Class::getModel(const MWWorld::Ptr &ptr) const { return ""; } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 5f16ef593..c8b51eeab 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -205,7 +205,7 @@ namespace MWWorld virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; - virtual std::string getModel() const; + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index db1db0505..1eba439a8 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -367,12 +367,11 @@ namespace MWWorld } float PhysicsSystem::getObjectHeight(const MWWorld::Ptr &ptr) { - std::string model = MWWorld::Class::get(ptr).getModel(); + std::string model = MWWorld::Class::get(ptr).getModel(ptr); if (model.empty()) { return 0.0; } float scale = ptr.getRefData().getBaseNode()->getScale().x; return mEngine->getObjectHeight(model, scale); } - } From e1c7d1f52936988f2093db7a9800093c1cfac927 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 24 Jul 2012 22:08:23 +0400 Subject: [PATCH 276/688] fixed item sinking --- apps/openmw/mwworld/physicssystem.cpp | 31 ++++++++++++++------------- apps/openmw/mwworld/scene.cpp | 6 ++++++ apps/openmw/mwworld/worldimp.cpp | 4 ++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1eba439a8..2b3d4b0f4 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -349,29 +349,30 @@ namespace MWWorld throw std::logic_error ("can't find player"); } - void PhysicsSystem::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string model){ + void PhysicsSystem::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string model){ - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - // unused - //Ogre::Vector3 objPos = node->getPosition(); + addObject( + node->getName(), + model, + node->getOrientation(), + node->getScale().x, + node->getPosition()); + } - addObject (node->getName(), model, node->getOrientation(), - node->getScale().x, node->getPosition()); - } + void PhysicsSystem::insertActorPhysics(const MWWorld::Ptr& ptr, const std::string model){ + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + addActor (node->getName(), model, node->getPosition()); + } - void PhysicsSystem::insertActorPhysics(const MWWorld::Ptr& ptr, const std::string model){ - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - // std::cout << "Adding node with name" << node->getName(); - addActor (node->getName(), model, node->getPosition()); - } - - float PhysicsSystem::getObjectHeight(const MWWorld::Ptr &ptr) { + float PhysicsSystem::getObjectHeight(const MWWorld::Ptr &ptr) + { std::string model = MWWorld::Class::get(ptr).getModel(ptr); if (model.empty()) { return 0.0; } float scale = ptr.getRefData().getBaseNode()->getScale().x; return mEngine->getObjectHeight(model, scale); - } + } } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 9786c24d9..b78669ef5 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -335,6 +335,12 @@ namespace MWWorld void Scene::addObjectToScene (const Ptr& ptr) { mRendering.addObject (ptr); + + float *pos = ptr.getRefData().getPosition().pos; + pos[2] += mPhysics->getObjectHeight(ptr) / 2; + + ptr.getRefData().getBaseNode()->setPosition(pos[0], pos[1], pos[2]); + MWWorld::Class::get (ptr).insertObject (ptr, *mPhysics); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index efb17a709..3d409570b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1024,8 +1024,6 @@ namespace MWWorld MWWorld::Ptr dropped = cell->insertObject(object, pos); mWorldScene->addObjectToScene(dropped); - /// \todo retrieve the bounds of the object and translate it accordingly - return true; } @@ -1047,6 +1045,8 @@ namespace MWWorld ESM::Position &pos = getPlayer().getPlayer().getRefData().getPosition(); + /// \todo fix item dropping at player object center position + MWWorld::Ptr dropped = cell->insertObject(object, pos); mWorldScene->addObjectToScene(dropped); From aff05b9d36f102634334219b61ea253a4e7a8dc7 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 25 Jul 2012 00:03:31 +0400 Subject: [PATCH 277/688] local script registration --- apps/openmw/mwworld/worldimp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3d409570b..670b3e512 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1023,6 +1023,9 @@ namespace MWWorld MWWorld::Ptr dropped = cell->insertObject(object, pos); mWorldScene->addObjectToScene(dropped); + mLocalScripts.add( + MWWorld::Class::get(dropped).getScript(), + dropped); return true; } @@ -1050,6 +1053,9 @@ namespace MWWorld MWWorld::Ptr dropped = cell->insertObject(object, pos); mWorldScene->addObjectToScene(dropped); + mLocalScripts.add( + MWWorld::Class::get(dropped).getScript(), + dropped); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) From d5fe378a6622445076dea86779a318d6b18e57c5 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 25 Jul 2012 10:02:43 +0400 Subject: [PATCH 278/688] Revert "local script registration" This reverts commit 0686399a306319a4c49e1009d10215a9c0e65ec2. --- apps/openmw/mwworld/worldimp.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 670b3e512..3d409570b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1023,9 +1023,6 @@ namespace MWWorld MWWorld::Ptr dropped = cell->insertObject(object, pos); mWorldScene->addObjectToScene(dropped); - mLocalScripts.add( - MWWorld::Class::get(dropped).getScript(), - dropped); return true; } @@ -1053,9 +1050,6 @@ namespace MWWorld MWWorld::Ptr dropped = cell->insertObject(object, pos); mWorldScene->addObjectToScene(dropped); - mLocalScripts.add( - MWWorld::Class::get(dropped).getScript(), - dropped); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) From bcc47cd5fb00858c481fff542fad19cac52a79f8 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 25 Jul 2012 10:47:59 +0400 Subject: [PATCH 279/688] local scripts, move placing to World::placeObject(Ptr, CellStore, Position) --- apps/openmw/mwbase/world.hpp | 8 ++++++++ apps/openmw/mwworld/worldimp.cpp | 22 ++++++++++++++++------ apps/openmw/mwworld/worldimp.hpp | 3 +++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6937cbf3b..f257b723e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -54,6 +54,14 @@ namespace MWBase World& operator= (const World&); ///< not implemented + protected: + + virtual void + placeObject( + const MWWorld::Ptr &ptr, + MWWorld::CellStore &cell, + const ESM::Position &pos) = 0; + public: enum RenderMode diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3d409570b..19cdcc334 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1021,8 +1021,7 @@ namespace MWWorld pos.pos[1] = -result.second[2]; pos.pos[2] = result.second[1]; - MWWorld::Ptr dropped = cell->insertObject(object, pos); - mWorldScene->addObjectToScene(dropped); + placeObject(object, *cell, pos); return true; } @@ -1038,6 +1037,20 @@ namespace MWWorld return true; } + void + World::placeObject(const Ptr &object, CellStore &cell, const ESM::Position &pos) + { + mLocalScripts.remove(object); + + MWWorld::Ptr dropped = cell.insertObject(object, pos); + mWorldScene->addObjectToScene(dropped); + + std::string script = MWWorld::Class::get(dropped).getScript(dropped); + if (!script.empty()) { + mLocalScripts.add(script, dropped); + } + } + void World::dropObjectOnGround (const Ptr& object) { MWWorld::Ptr::CellStore* cell = getPlayer().getPlayer().getCell(); @@ -1046,10 +1059,7 @@ namespace MWWorld getPlayer().getPlayer().getRefData().getPosition(); /// \todo fix item dropping at player object center position - - MWWorld::Ptr dropped = cell->insertObject(object, pos); - - mWorldScene->addObjectToScene(dropped); + placeObject(object, *cell, pos); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 43b178fe3..d39871c21 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -89,6 +89,9 @@ namespace MWWorld bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed + virtual void + placeObject(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + public: World (OEngine::Render::OgreRenderer& renderer, From e7666d3a7fd22a9489ec60244678cd3664532003 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 25 Jul 2012 17:18:17 +0400 Subject: [PATCH 280/688] move responsibility for cell changing from CellStore::insertObject to Class::moveToCell --- apps/openmw/mwclass/activator.cpp | 10 +++ apps/openmw/mwclass/activator.hpp | 4 + apps/openmw/mwclass/apparatus.cpp | 9 +++ apps/openmw/mwclass/apparatus.hpp | 4 + apps/openmw/mwclass/armor.cpp | 9 +++ apps/openmw/mwclass/armor.hpp | 3 + apps/openmw/mwclass/book.cpp | 8 ++ apps/openmw/mwclass/book.hpp | 3 + apps/openmw/mwclass/clothing.cpp | 9 +++ apps/openmw/mwclass/clothing.hpp | 3 + apps/openmw/mwclass/container.cpp | 9 +++ apps/openmw/mwclass/container.hpp | 4 + apps/openmw/mwclass/creature.cpp | 9 +++ apps/openmw/mwclass/creature.hpp | 3 + apps/openmw/mwclass/door.cpp | 9 +++ apps/openmw/mwclass/door.hpp | 3 + apps/openmw/mwclass/ingredient.cpp | 9 +++ apps/openmw/mwclass/ingredient.hpp | 3 + apps/openmw/mwclass/light.cpp | 9 +++ apps/openmw/mwclass/light.hpp | 3 + apps/openmw/mwclass/lockpick.cpp | 9 +++ apps/openmw/mwclass/lockpick.hpp | 3 + apps/openmw/mwclass/misc.cpp | 36 +++++++++ apps/openmw/mwclass/misc.hpp | 3 + apps/openmw/mwclass/npc.cpp | 9 +++ apps/openmw/mwclass/npc.hpp | 3 + apps/openmw/mwclass/potion.cpp | 10 +++ apps/openmw/mwclass/potion.hpp | 3 + apps/openmw/mwclass/probe.cpp | 10 +++ apps/openmw/mwclass/probe.hpp | 5 +- apps/openmw/mwclass/repair.cpp | 9 +++ apps/openmw/mwclass/repair.hpp | 5 +- apps/openmw/mwclass/static.cpp | 9 +++ apps/openmw/mwclass/static.hpp | 3 + apps/openmw/mwclass/weapon.cpp | 9 +++ apps/openmw/mwclass/weapon.hpp | 3 + apps/openmw/mwworld/cellstore.cpp | 119 ----------------------------- apps/openmw/mwworld/cellstore.hpp | 6 +- apps/openmw/mwworld/class.cpp | 31 ++++++++ apps/openmw/mwworld/class.hpp | 14 ++++ apps/openmw/mwworld/worldimp.cpp | 4 +- 41 files changed, 300 insertions(+), 126 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 33baad7ca..d0f09e80e 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -94,4 +94,14 @@ namespace MWClass return info; } + + MWWorld::Ptr + Activator::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.activators.insert(*ref), &cell); + } } + diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 2e947a9fc..26aafb717 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -7,6 +7,10 @@ namespace MWClass { class Activator : public MWWorld::Class { + + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index dd255f9f8..6322c963e 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -149,4 +149,13 @@ namespace MWClass { return boost::shared_ptr(new MWWorld::ActionAlchemy()); } + + MWWorld::Ptr + Apparatus::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.appas.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index fa77eec93..9352e0617 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -7,6 +7,10 @@ namespace MWClass { class Apparatus : public MWWorld::Class { + + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 8bbb198cc..aeefcd42b 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -276,4 +276,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Armor::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.armors.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 181a4ce61..960b6dad5 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Armor : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 62b6c8204..1517f090a 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -157,4 +157,12 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionRead(ptr)); } + MWWorld::Ptr + Book::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.books.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 20ea89274..adbf2d64b 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Book : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index e1ec5a61c..86c031db0 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -227,4 +227,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Clothing::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.clothes.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 6b5fe1e96..2c0d0b8a5 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Clothing : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 7c5719b8e..2543f6acd 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -207,4 +207,13 @@ namespace MWClass { ptr.getCellRef().lockLevel = 0; } + + MWWorld::Ptr + Container::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.containers.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 17a9ac4ea..2f15d03d8 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -9,6 +9,10 @@ namespace MWClass { void ensureCustomData (const MWWorld::Ptr& ptr) const; + + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 178a6f536..8f885bf62 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -195,4 +195,13 @@ namespace MWClass return weight; } + + MWWorld::Ptr + Creature::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.creatures.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index b994e0831..38be98533 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -11,6 +11,9 @@ namespace MWClass { void ensureCustomData (const MWWorld::Ptr& ptr) const; + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 1274219a9..8eef54725 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -209,4 +209,13 @@ namespace MWClass return info; } + + MWWorld::Ptr + Door::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.doors.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 2612df41c..b89858556 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Door : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index a2f14c02c..02e77fa25 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -156,4 +156,13 @@ namespace MWClass return info; } + + MWWorld::Ptr + Ingredient::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.ingreds.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index ad87dd46c..44ee0ccd0 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Ingredient : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 37d7d1eec..e7b3af2b9 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -196,4 +196,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Light::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.lights.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index ab8854efd..953078a35 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Light : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index ae2b5de89..e72e96822 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -165,4 +165,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Lockpick::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.lockpicks.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 45a02d40d..216dc10c9 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Lockpick : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 89c1ef25f..37f608ed2 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -12,6 +12,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/manualref.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -183,4 +184,39 @@ namespace MWClass return info; } + + MWWorld::Ptr + Miscellaneous::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::Ptr newPtr; + + const ESMS::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + if (MWWorld::Class::get(ptr).getName(ptr) == store.gameSettings.search("sGold")->str) { + int goldAmount = ptr.getRefData().getCount(); + + std::string base = "Gold_001"; + if (goldAmount >= 100) + base = "Gold_100"; + else if (goldAmount >= 25) + base = "Gold_025"; + else if (goldAmount >= 10) + base = "Gold_010"; + else if (goldAmount >= 5) + base = "Gold_005"; + + // Really, I have no idea why moving ref out of conditional + // scope causes list::push_back throwing std::bad_alloc + MWWorld::ManualRef newRef(store, base); + MWWorld::LiveCellRef *ref = + newRef.getPtr().get(); + newPtr = MWWorld::Ptr(&cell.miscItems.insert(*ref), &cell); + } else { + MWWorld::LiveCellRef *ref = + ptr.get(); + newPtr = MWWorld::Ptr(&cell.miscItems.insert(*ref), &cell); + } + return newPtr; + } } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 23b5e9564..2a314758f 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Miscellaneous : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a5525fcda..c81397753 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -380,4 +380,13 @@ namespace MWClass y = 0; x = 0; } + + MWWorld::Ptr + Npc::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.npcs.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 0ea45d132..46fdc9b04 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -9,6 +9,9 @@ namespace MWClass { void ensureCustomData (const MWWorld::Ptr& ptr) const; + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 92a951d03..e0f9008c4 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -160,4 +160,14 @@ namespace MWClass return boost::shared_ptr ( new MWWorld::ActionApply (actor, ref->base->mId, actor)); } + + MWWorld::Ptr + Potion::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.potions.insert(*ref), &cell); + } } + diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 39edfd760..0cb40ead0 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Potion : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 033eee4c5..8e5fcfd19 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -164,4 +164,14 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Probe::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.probes.insert(*ref), &cell); + } } + diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index ff10eb9d6..67831d766 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -7,9 +7,12 @@ namespace MWClass { class Probe : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 4258ad106..84d6e3b9e 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -146,4 +146,13 @@ namespace MWClass return info; } + + MWWorld::Ptr + Repair::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.repairs.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 689850b90..74d1d7378 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -7,9 +7,12 @@ namespace MWClass { class Repair : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index a14adc300..ee8bcfe81 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -53,4 +53,13 @@ namespace MWClass registerClass (typeid (ESM::Static).name(), instance); } + + MWWorld::Ptr + Static::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.statics.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index 3066933e4..52b87abcd 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Static : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 6412a46b1..ccc5ff6ea 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -365,4 +365,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Weapon::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.weapons.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index eaf5b60a4..b18405488 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Weapon : public MWWorld::Class { + virtual MWWorld::Ptr + moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 088c0c623..eceb5ddc1 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -8,8 +8,6 @@ #include "../mwbase/world.hpp" #include "ptr.hpp" -#include "manualref.hpp" -#include "class.hpp" namespace MWWorld { @@ -127,121 +125,4 @@ namespace MWWorld } } } - - /// \todo this whole code needs major clean up - const MWWorld::Ptr CellStore::insertObject (const Ptr& ptr, const ESM::Position &pos) - { - std::string type = ptr.getTypeName(); - - MWWorld::Ptr newPtr; - - // insert into the correct CellRefList - if (type == typeid(ESM::Potion).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&potions.insert(*ref), this); - } - else if (type == typeid(ESM::Apparatus).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&appas.insert(*ref), this); - } - else if (type == typeid(ESM::Armor).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&armors.insert(*ref), this); - } - else if (type == typeid(ESM::Book).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&books.insert(*ref), this); - } - else if (type == typeid(ESM::Clothing).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&clothes.insert(*ref), this); - } - else if (type == typeid(ESM::Ingredient).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&ingreds.insert(*ref), this); - } - else if (type == typeid(ESM::Light).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&lights.insert(*ref), this); - } - else if (type == typeid(ESM::Tool).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&lockpicks.insert(*ref), this); - } - else if (type == typeid(ESM::Repair).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&repairs.insert(*ref), this); - } - else if (type == typeid(ESM::Probe).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&probes.insert(*ref), this); - } - else if (type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - newPtr = MWWorld::Ptr(&weapons.insert(*ref), this); - } - else if (type == typeid(ESM::Miscellaneous).name()) - { - - // if this is gold, we need to fetch the correct mesh depending on the amount of gold. - if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) - { - int goldAmount = ptr.getRefData().getCount(); - - std::string base = "Gold_001"; - if (goldAmount >= 100) - base = "Gold_100"; - else if (goldAmount >= 25) - base = "Gold_025"; - else if (goldAmount >= 10) - base = "Gold_010"; - else if (goldAmount >= 5) - base = "Gold_005"; - - MWWorld::ManualRef newRef( - MWBase::Environment::get().getWorld()->getStore(), - base); - - MWWorld::LiveCellRef* ref = - newRef.getPtr().get(); - - newPtr = MWWorld::Ptr(&miscItems.insert(*ref), this); - /* - ESM::Position& p = newPtr.getRefData().getPosition(); - p.pos[0] = ptr.getRefData().getPosition().pos[0]; - p.pos[1] = ptr.getRefData().getPosition().pos[1]; - p.pos[2] = ptr.getRefData().getPosition().pos[2]; - */ - } - else - { - MWWorld::LiveCellRef* ref = - ptr.get(); - - newPtr = MWWorld::Ptr(&miscItems.insert(*ref), this); - } - } - else - throw std::runtime_error("Trying to insert object of unhandled type"); - - newPtr.getRefData().getPosition() = pos; - - newPtr.getRefData().setCount(ptr.getRefData().getCount()); - ptr.getRefData().setCount(0); - newPtr.getRefData().enable(); - - return newPtr; - } - } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 1bf233a50..0be0ef651 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -113,9 +113,9 @@ namespace MWWorld CellRefList ingreds; CellRefList creatureLists; CellRefList itemLists; - CellRefList lights; + CellRefList lights; CellRefList lockpicks; - CellRefList miscItems; + CellRefList miscItems; CellRefList npcs; CellRefList probes; CellRefList repairs; @@ -155,8 +155,6 @@ namespace MWWorld forEachImp (functor, weapons); } - const MWWorld::Ptr insertObject(const MWWorld::Ptr &ptr, const ESM::Position &pos); - private: template diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 729746f6b..6f2306fbe 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -5,7 +5,10 @@ #include +#include + #include "ptr.hpp" +#include "refdata.hpp" #include "nullaction.hpp" #include "containerstore.hpp" @@ -217,4 +220,32 @@ namespace MWWorld { return ""; } + + MWWorld::Ptr + Class::moveToCellImpl(const Ptr &ptr, CellStore &cell) const + { + throw std::runtime_error("unable to move class to cell"); + } + + MWWorld::Ptr + Class::moveToCell(const Ptr &ptr, CellStore &cell) const + { + Ptr newPtr = moveToCellImpl(ptr, cell); + + newPtr.getRefData().setCount(ptr.getRefData().getCount()); + ptr.getRefData().setCount(0); + newPtr.getRefData().enable(); + + return newPtr; + } + + MWWorld::Ptr + Class::moveToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const + { + Ptr newPtr = moveToCell(ptr, cell); + newPtr.getRefData().getPosition() = pos; + + return newPtr; + } } + diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index c8b51eeab..509433433 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -31,12 +31,18 @@ namespace MWGui struct ToolTipInfo; } +namespace ESM +{ + struct Position; +} + namespace MWWorld { class Ptr; class ContainerStore; class InventoryStore; class PhysicsSystem; + class CellStore; /// \brief Base class for referenceable esm records class Class @@ -51,6 +57,8 @@ namespace MWWorld Class(); + virtual Ptr moveToCellImpl(const Ptr &ptr, CellStore &cell) const; + public: /// NPC-stances. @@ -206,6 +214,12 @@ namespace MWWorld virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual Ptr + moveToCell(const Ptr &ptr, CellStore &cell) const; + + virtual Ptr + moveToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 19cdcc334..394a9e493 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1042,7 +1042,9 @@ namespace MWWorld { mLocalScripts.remove(object); - MWWorld::Ptr dropped = cell.insertObject(object, pos); + MWWorld::Ptr dropped = + MWWorld::Class::get(object).moveToCell(object, cell, pos); + mWorldScene->addObjectToScene(dropped); std::string script = MWWorld::Class::get(dropped).getScript(dropped); From 49b1d5e1275bfebcd508afb910d4d55644184c20 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 25 Jul 2012 18:58:55 +0400 Subject: [PATCH 281/688] fix object placing --- apps/openmw/mwworld/physicssystem.cpp | 8 +++++--- apps/openmw/mwworld/physicssystem.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 7 ++++++- libs/openengine/bullet/physic.cpp | 17 +++++++++++++---- libs/openengine/bullet/physic.hpp | 2 +- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 2b3d4b0f4..e4a019b69 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -366,13 +366,15 @@ namespace MWWorld addActor (node->getName(), model, node->getPosition()); } - float PhysicsSystem::getObjectHeight(const MWWorld::Ptr &ptr) + bool PhysicsSystem::getObjectAABB(const MWWorld::Ptr &ptr, float *min, float *max) { std::string model = MWWorld::Class::get(ptr).getModel(ptr); if (model.empty()) { - return 0.0; + return false; } float scale = ptr.getRefData().getBaseNode()->getScale().x; - return mEngine->getObjectHeight(model, scale); + mEngine->getObjectAABB(model, scale, min, max); + + return true; } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 61e2590bd..f81bcc373 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -65,7 +65,7 @@ namespace MWWorld void setCurrentWater(bool hasWater, int waterHeight); - float getObjectHeight(const MWWorld::Ptr &ptr); + bool getObjectAABB(const MWWorld::Ptr &ptr, float *min, float *max); private: OEngine::Render::OgreRenderer &mRender; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b78669ef5..ce0acfe47 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -337,7 +337,12 @@ namespace MWWorld mRendering.addObject (ptr); float *pos = ptr.getRefData().getPosition().pos; - pos[2] += mPhysics->getObjectHeight(ptr) / 2; + float min[3], max[3]; + if (mPhysics->getObjectAABB(ptr, min, max)) { + pos[0] -= (min[0] + max[0]) / 2; + pos[1] -= (min[1] + max[1]) / 2; + pos[2] -= min[2]; + } ptr.getRefData().getBaseNode()->setPosition(pos[0], pos[1], pos[2]); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 416d0c09d..611c87d9f 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -12,6 +12,8 @@ #include +#include + #define BIT(x) (1<<(x)) namespace OEngine { @@ -569,7 +571,8 @@ namespace Physic return results2; } - float PhysicEngine::getObjectHeight(const std::string &mesh, float scale) { + void PhysicEngine::getObjectAABB(const std::string &mesh, float scale, float *min, float *max) + { char uniqueID[8]; sprintf( uniqueID, "%07.3f", scale ); std::string sid = uniqueID; @@ -582,11 +585,17 @@ namespace Physic btTransform trans; - btVector3 min, max; + btVector3 btmin, btmax; trans.setIdentity(); - shape->Shape->getAabb(trans, min, max); + shape->Shape->getAabb(trans, btmin, btmax); - return max.z() - min.z(); + min[0] = btmin.x(); + min[1] = btmin.y(); + min[2] = btmin.z(); + + max[0] = btmax.x(); + max[1] = btmax.y(); + max[2] = btmax.z(); } }}; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 22c5fa274..8701adc84 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -221,7 +221,7 @@ namespace Physic bool toggleDebugRendering(); - float getObjectHeight(const std::string &mesh, float scale); + void getObjectAABB(const std::string &mesh, float scale, float *min, float *max); /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). From cd04911f3c463ce6e8eb94f62020ee689b661c4f Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 25 Jul 2012 20:25:53 +0400 Subject: [PATCH 282/688] dropping on the ground --- apps/openmw/mwworld/physicssystem.cpp | 16 ++++++++++++++++ apps/openmw/mwworld/physicssystem.hpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 13 ++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index e4a019b69..27d48fa39 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -122,6 +122,22 @@ namespace MWWorld return !(result.first == ""); } + std::pair + PhysicsSystem::castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len) + { + Ogre::Ray ray = Ogre::Ray(orig, dir); + Ogre::Vector3 to = ray.getPoint(len); + + btVector3 btFrom = btVector3(orig.x, orig.y, orig.z); + btVector3 btTo = btVector3(to.x, to.y, to.z); + + std::pair test = mEngine->rayTest(btFrom, btTo); + if (test.first == "") { + return std::make_pair(false, Ogre::Vector3()); + } + return std::make_pair(true, ray.getPoint(len * test.second)); + } + std::pair PhysicsSystem::castRay(float mouseX, float mouseY) { Ogre::Ray ray = mRender.getCamera()->getCameraToViewportRay( diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index f81bcc373..375a7c9de 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -54,6 +54,9 @@ namespace MWWorld // cast ray, return true if it hit something bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); + std::pair + castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len); + std::pair castRay(float mouseX, float mouseY); ///< cast ray from the mouse, return true if it hit something and the first result (in OGRE coordinates) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 394a9e493..b9ab6ade2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1057,9 +1057,20 @@ namespace MWWorld { MWWorld::Ptr::CellStore* cell = getPlayer().getPlayer().getCell(); - ESM::Position &pos = + ESM::Position pos = getPlayer().getPlayer().getRefData().getPosition(); + Ogre::Vector3 orig = + Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); + + float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; + len += 100.0; + + std::pair hit = + mPhysics->castRay(orig, dir, len); + pos.pos[2] = hit.second.z; + /// \todo fix item dropping at player object center position placeObject(object, *cell, pos); } From 3feb4ce61b5c4c402cff2d47d294712d263beefb Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jul 2012 19:33:21 +0200 Subject: [PATCH 283/688] terrain lod morph fix --- files/materials/terrain.shader | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 601f287a0..0458c4db9 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -55,7 +55,7 @@ shUniform(float2, lodMorph) @shAutoConstant(lodMorph, custom, 1001) shVertexInput(float2, uv0) - shVertexInput(float2, delta) // lodDelta, lodThreshold + shVertexInput(float2, uv1) // lodDelta, lodThreshold #if SHADOWS shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) @@ -85,11 +85,11 @@ // result is negative (it will only be -1 in fact, since after that // the vertex will never be indexed), we will achieve our aim. // sign(vertexLOD - targetLOD) == -1 is to morph - float toMorph = -min(0, sign(delta.y - lodMorph.y)); + float toMorph = -min(0, sign(uv1.y - lodMorph.y)); // morph // this assumes XZ terrain alignment - worldPos.y += delta.x * toMorph * lodMorph.x; + worldPos.y += uv1.x * toMorph * lodMorph.x; shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); From 9f813aa26c3ed3a31767f1b75d637027b2fb47b4 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 25 Jul 2012 23:28:42 +0400 Subject: [PATCH 284/688] update resource naming in getObjectAABB() --- libs/openengine/bullet/physic.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 611c87d9f..d4b63676d 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -12,8 +12,6 @@ #include -#include - #define BIT(x) (1<<(x)) namespace OEngine { @@ -576,14 +574,13 @@ namespace Physic char uniqueID[8]; sprintf( uniqueID, "%07.3f", scale ); std::string sid = uniqueID; - std::string outputstring = mesh + uniqueID + "\"|"; + std::string outputstring = mesh + uniqueID; mShapeLoader->load(outputstring, "General"); BulletShapeManager::getSingletonPtr()->load(outputstring, "General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring, "General"); - btTransform trans; btVector3 btmin, btmax; From 37c7becb07326240881dc9d4bdfcb35404a68d94 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jul 2012 23:53:06 +0200 Subject: [PATCH 285/688] some potential compability fixes --- files/materials/objects.shader | 15 +++++++-------- files/materials/shadows.h | 10 +++++++--- files/materials/terrain.shader | 17 +++++++++-------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 238ccdcf6..dba239c14 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -192,7 +192,7 @@ #if SHADOWS || SHADOWS_PSSM float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); - shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); + shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); #endif #if !SHADOWS && !SHADOWS_PSSM @@ -205,13 +205,12 @@ #if UNDERWATER float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)).xyz; float3 waterEyePos = float3(1,1,1); - if (worldPos.y < waterLevel && waterEnabled == 1) - { - // NOTE: this calculation would be wrong for non-uniform scaling - float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); - caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - } + // NOTE: this calculation would be wrong for non-uniform scaling + float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); + caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + if (worldPos.y >= waterLevel || waterEnabled != 1) + caustics = float3(1,1,1); #endif diff --git a/files/materials/shadows.h b/files/materials/shadows.h index 769a4fea7..b1dc92d21 100644 --- a/files/materials/shadows.h +++ b/files/materials/shadows.h @@ -36,12 +36,16 @@ float pssmDepthShadow ( { float shadow; + float pcf1 = depthShadowPCF(shadowMap0, lightSpacePos0, invShadowmapSize0); + float pcf2 = depthShadowPCF(shadowMap1, lightSpacePos1, invShadowmapSize1); + float pcf3 = depthShadowPCF(shadowMap2, lightSpacePos2, invShadowmapSize2); + if (depth < pssmSplitPoints.x) - shadow = depthShadowPCF(shadowMap0, lightSpacePos0, invShadowmapSize0); + shadow = pcf1; else if (depth < pssmSplitPoints.y) - shadow = depthShadowPCF(shadowMap1, lightSpacePos1, invShadowmapSize1); + shadow = pcf2; else - shadow = depthShadowPCF(shadowMap2, lightSpacePos2, invShadowmapSize2); + shadow = pcf3; return shadow; } diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 0458c4db9..7ef26d035 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -224,13 +224,14 @@ float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)).xyz; float3 waterEyePos = float3(1,1,1); - if (worldPos.y < waterLevel) - { - // NOTE: this calculation would be wrong for non-uniform scaling - float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); - caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - } + // NOTE: this calculation would be wrong for non-uniform scaling + float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); + caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + if (worldPos.y >= waterLevel) + caustics = float3(1,1,1); + + #endif @@ -285,7 +286,7 @@ #if SHADOWS || SHADOWS_PSSM float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; float fade = 1-((depth - shadowFar_fadeStart.y) / fadeRange); - shadow = (depth > shadowFar_fadeStart.x) ? 1 : ((depth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); + shadow = (depth > shadowFar_fadeStart.x) ? 1.0 : ((depth > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); #endif #if !SHADOWS && !SHADOWS_PSSM From 26595f22f66efcb84c97b3fd7baac9eb03895e24 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 26 Jul 2012 16:14:11 +0400 Subject: [PATCH 286/688] float* -> Vector3, moveToCell -> copyToCell, fixed placeObject() --- apps/openmw/mwclass/activator.cpp | 2 +- apps/openmw/mwclass/activator.hpp | 2 +- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/apparatus.hpp | 2 +- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/armor.hpp | 2 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/book.hpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/clothing.hpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/container.hpp | 2 +- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/door.hpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/ingredient.hpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/light.hpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/lockpick.hpp | 2 +- apps/openmw/mwclass/misc.cpp | 2 +- apps/openmw/mwclass/misc.hpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/potion.hpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/probe.hpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/repair.hpp | 2 +- apps/openmw/mwclass/static.cpp | 2 +- apps/openmw/mwclass/static.hpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwclass/weapon.hpp | 2 +- apps/openmw/mwworld/class.cpp | 11 +++++---- apps/openmw/mwworld/class.hpp | 6 ++--- apps/openmw/mwworld/physicssystem.cpp | 15 ++++++++++--- apps/openmw/mwworld/physicssystem.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 32 +++++++++++++++------------ apps/openmw/mwworld/scene.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 26 +++++++++++++++------- libs/openengine/bullet/physic.cpp | 13 ++--------- libs/openengine/bullet/physic.hpp | 2 +- 45 files changed, 98 insertions(+), 83 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index d0f09e80e..9b0082efc 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -96,7 +96,7 @@ namespace MWClass } MWWorld::Ptr - Activator::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Activator::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 26aafb717..4165fbc08 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -9,7 +9,7 @@ namespace MWClass { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 6322c963e..9814b140c 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -151,7 +151,7 @@ namespace MWClass } MWWorld::Ptr - Apparatus::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Apparatus::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 9352e0617..7045f62d6 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -9,7 +9,7 @@ namespace MWClass { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index aeefcd42b..4624b94d9 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -278,7 +278,7 @@ namespace MWClass } MWWorld::Ptr - Armor::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Armor::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 960b6dad5..51c0ea21c 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -8,7 +8,7 @@ namespace MWClass class Armor : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 1517f090a..d8166347e 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -158,7 +158,7 @@ namespace MWClass } MWWorld::Ptr - Book::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Book::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index adbf2d64b..acb1aac06 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -8,7 +8,7 @@ namespace MWClass class Book : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 86c031db0..f55d6ed88 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -229,7 +229,7 @@ namespace MWClass } MWWorld::Ptr - Clothing::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Clothing::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 2c0d0b8a5..f7801848f 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -8,7 +8,7 @@ namespace MWClass class Clothing : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 2543f6acd..c6d22b3a5 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -209,7 +209,7 @@ namespace MWClass } MWWorld::Ptr - Container::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 2f15d03d8..006e4bd22 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -11,7 +11,7 @@ namespace MWClass virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 8f885bf62..0f3141f5c 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -197,7 +197,7 @@ namespace MWClass } MWWorld::Ptr - Creature::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 38be98533..f7a5e5874 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -12,7 +12,7 @@ namespace MWClass void ensureCustomData (const MWWorld::Ptr& ptr) const; virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 8eef54725..359e50ffb 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -211,7 +211,7 @@ namespace MWClass } MWWorld::Ptr - Door::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Door::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index b89858556..b0f86f12d 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -8,7 +8,7 @@ namespace MWClass class Door : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 02e77fa25..d8c8b4b3b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -158,7 +158,7 @@ namespace MWClass } MWWorld::Ptr - Ingredient::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Ingredient::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 44ee0ccd0..1365c4a71 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -8,7 +8,7 @@ namespace MWClass class Ingredient : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index e7b3af2b9..f09d3ce36 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -198,7 +198,7 @@ namespace MWClass } MWWorld::Ptr - Light::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Light::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 953078a35..640e1705b 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -8,7 +8,7 @@ namespace MWClass class Light : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index e72e96822..20441b520 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -167,7 +167,7 @@ namespace MWClass } MWWorld::Ptr - Lockpick::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Lockpick::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 216dc10c9..0961b55b2 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -8,7 +8,7 @@ namespace MWClass class Lockpick : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 37f608ed2..cb6d40c43 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -186,7 +186,7 @@ namespace MWClass } MWWorld::Ptr - Miscellaneous::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Miscellaneous::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::Ptr newPtr; diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 2a314758f..a5a79a8f6 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -8,7 +8,7 @@ namespace MWClass class Miscellaneous : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c81397753..81c0c85f5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -382,7 +382,7 @@ namespace MWClass } MWWorld::Ptr - Npc::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 46fdc9b04..e494fbaa7 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -10,7 +10,7 @@ namespace MWClass void ensureCustomData (const MWWorld::Ptr& ptr) const; virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index e0f9008c4..993dac6f6 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -162,7 +162,7 @@ namespace MWClass } MWWorld::Ptr - Potion::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Potion::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 0cb40ead0..d595f7e69 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -8,7 +8,7 @@ namespace MWClass class Potion : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 8e5fcfd19..450016209 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -166,7 +166,7 @@ namespace MWClass } MWWorld::Ptr - Probe::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Probe::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 67831d766..d9f90baf6 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -8,7 +8,7 @@ namespace MWClass class Probe : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 84d6e3b9e..829fe311a 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -148,7 +148,7 @@ namespace MWClass } MWWorld::Ptr - Repair::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Repair::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 74d1d7378..c58e38f96 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -8,7 +8,7 @@ namespace MWClass class Repair : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index ee8bcfe81..e317b740c 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -55,7 +55,7 @@ namespace MWClass } MWWorld::Ptr - Static::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Static::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index 52b87abcd..e36b3d142 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -8,7 +8,7 @@ namespace MWClass class Static : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index ccc5ff6ea..b45953130 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -367,7 +367,7 @@ namespace MWClass } MWWorld::Ptr - Weapon::moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + Weapon::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index b18405488..06cf88c5f 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -8,7 +8,7 @@ namespace MWClass class Weapon : public MWWorld::Class { virtual MWWorld::Ptr - moveToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; public: diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6f2306fbe..6676ce1cf 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -222,27 +222,26 @@ namespace MWWorld } MWWorld::Ptr - Class::moveToCellImpl(const Ptr &ptr, CellStore &cell) const + Class::copyToCellImpl(const Ptr &ptr, CellStore &cell) const { throw std::runtime_error("unable to move class to cell"); } MWWorld::Ptr - Class::moveToCell(const Ptr &ptr, CellStore &cell) const + Class::copyToCell(const Ptr &ptr, CellStore &cell) const { - Ptr newPtr = moveToCellImpl(ptr, cell); + Ptr newPtr = copyToCellImpl(ptr, cell); newPtr.getRefData().setCount(ptr.getRefData().getCount()); - ptr.getRefData().setCount(0); newPtr.getRefData().enable(); return newPtr; } MWWorld::Ptr - Class::moveToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const + Class::copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const { - Ptr newPtr = moveToCell(ptr, cell); + Ptr newPtr = copyToCell(ptr, cell); newPtr.getRefData().getPosition() = pos; return newPtr; diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 509433433..1bc592798 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -57,7 +57,7 @@ namespace MWWorld Class(); - virtual Ptr moveToCellImpl(const Ptr &ptr, CellStore &cell) const; + virtual Ptr copyToCellImpl(const Ptr &ptr, CellStore &cell) const; public: @@ -216,10 +216,10 @@ namespace MWWorld virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual Ptr - moveToCell(const Ptr &ptr, CellStore &cell) const; + copyToCell(const Ptr &ptr, CellStore &cell) const; virtual Ptr - moveToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; + copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; }; } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 27d48fa39..45cfdd123 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -382,14 +382,23 @@ namespace MWWorld addActor (node->getName(), model, node->getPosition()); } - bool PhysicsSystem::getObjectAABB(const MWWorld::Ptr &ptr, float *min, float *max) + bool PhysicsSystem::getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max) { std::string model = MWWorld::Class::get(ptr).getModel(ptr); if (model.empty()) { return false; } - float scale = ptr.getRefData().getBaseNode()->getScale().x; - mEngine->getObjectAABB(model, scale, min, max); + btVector3 btMin, btMax; + float scale = ptr.getCellRef().scale; + mEngine->getObjectAABB(model, scale, btMin, btMax); + + min.x = btMin.x(); + min.y = btMin.y(); + min.z = btMin.z(); + + max.x = btMax.x(); + max.y = btMax.y(); + max.z = btMax.z(); return true; } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 375a7c9de..e42fa536b 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -68,7 +68,7 @@ namespace MWWorld void setCurrentWater(bool hasWater, int waterHeight); - bool getObjectAABB(const MWWorld::Ptr &ptr, float *min, float *max); + bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); private: OEngine::Render::OgreRenderer &mRender; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ce0acfe47..13e5ecb85 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -334,21 +334,10 @@ namespace MWWorld void Scene::addObjectToScene (const Ptr& ptr) { - mRendering.addObject (ptr); - - float *pos = ptr.getRefData().getPosition().pos; - float min[3], max[3]; - if (mPhysics->getObjectAABB(ptr, min, max)) { - pos[0] -= (min[0] + max[0]) / 2; - pos[1] -= (min[1] + max[1]) / 2; - pos[2] -= min[2]; - } - - ptr.getRefData().getBaseNode()->setPosition(pos[0], pos[1], pos[2]); - - MWWorld::Class::get (ptr).insertObject (ptr, *mPhysics); + mRendering.addObject(ptr); + MWWorld::Class::get(ptr).insertObject(ptr, *mPhysics); } - + void Scene::removeObjectFromScene (const Ptr& ptr) { MWBase::Environment::get().getMechanicsManager()->removeActor (ptr); @@ -356,4 +345,19 @@ namespace MWWorld mPhysics->removeObject (ptr.getRefData().getHandle()); mRendering.removeObject (ptr); } + + bool Scene::isCellActive(const CellStore &cell) + { + CellStoreCollection::iterator active = mActiveCells.begin(); + while (active != mActiveCells.end()) { + if ((*active)->cell->name == cell.cell->name && + (*active)->cell->data.gridX == cell.cell->data.gridX && + (*active)->cell->data.gridY == cell.cell->data.gridY) + { + return true; + } + ++active; + } + return false; + } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index f5f4b640b..59e13dafe 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -95,6 +95,8 @@ namespace MWWorld void removeObjectFromScene (const Ptr& ptr); ///< Remove an object from the scene, but not from the world model. + + bool isCellActive(const CellStore &cell); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b9ab6ade2..59f6d7e86 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1022,6 +1022,7 @@ namespace MWWorld pos.pos[2] = result.second[1]; placeObject(object, *cell, pos); + object.getRefData().setCount(0); return true; } @@ -1040,16 +1041,25 @@ namespace MWWorld void World::placeObject(const Ptr &object, CellStore &cell, const ESM::Position &pos) { - mLocalScripts.remove(object); - + /// \todo add searching correct cell for position specified MWWorld::Ptr dropped = - MWWorld::Class::get(object).moveToCell(object, cell, pos); + MWWorld::Class::get(object).copyToCell(object, cell, pos); - mWorldScene->addObjectToScene(dropped); + Ogre::Vector3 min, max; + if (mPhysics->getObjectAABB(object, min, max)) { + float *pos = dropped.getRefData().getPosition().pos; + pos[0] -= (min.x + max.x) / 2; + pos[1] -= (min.y + max.y) / 2; + pos[2] -= min.z; + } - std::string script = MWWorld::Class::get(dropped).getScript(dropped); - if (!script.empty()) { - mLocalScripts.add(script, dropped); + if (mWorldScene->isCellActive(cell)) { + mWorldScene->addObjectToScene(dropped); + + std::string script = MWWorld::Class::get(dropped).getScript(dropped); + if (!script.empty()) { + mLocalScripts.add(script, dropped); + } } } @@ -1071,8 +1081,8 @@ namespace MWWorld mPhysics->castRay(orig, dir, len); pos.pos[2] = hit.second.z; - /// \todo fix item dropping at player object center position placeObject(object, *cell, pos); + object.getRefData().setCount(0); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d4b63676d..089825a9d 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -569,7 +569,7 @@ namespace Physic return results2; } - void PhysicEngine::getObjectAABB(const std::string &mesh, float scale, float *min, float *max) + void PhysicEngine::getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max) { char uniqueID[8]; sprintf( uniqueID, "%07.3f", scale ); @@ -582,17 +582,8 @@ namespace Physic BulletShapeManager::getSingleton().getByName(outputstring, "General"); btTransform trans; - btVector3 btmin, btmax; - trans.setIdentity(); - shape->Shape->getAabb(trans, btmin, btmax); - min[0] = btmin.x(); - min[1] = btmin.y(); - min[2] = btmin.z(); - - max[0] = btmax.x(); - max[1] = btmax.y(); - max[2] = btmax.z(); + shape->Shape->getAabb(trans, min, max); } }}; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 8701adc84..9ae8e7607 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -221,7 +221,7 @@ namespace Physic bool toggleDebugRendering(); - void getObjectAABB(const std::string &mesh, float scale, float *min, float *max); + void getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max); /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). From aa5f63ffcc2f3bc52b27c13e84f938f303b98a34 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 26 Jul 2012 19:06:48 +0400 Subject: [PATCH 287/688] disabled items placing handling --- apps/openmw/mwworld/class.cpp | 6 +++++- apps/openmw/mwworld/worldimp.cpp | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6676ce1cf..bb9b7d982 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -233,7 +233,11 @@ namespace MWWorld Ptr newPtr = copyToCellImpl(ptr, cell); newPtr.getRefData().setCount(ptr.getRefData().getCount()); - newPtr.getRefData().enable(); + if (ptr.getRefData().isEnabled()) { + newPtr.getRefData().enable(); + } else { + newPtr.getRefData().disable(); + } return newPtr; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 59f6d7e86..67d8a7cec 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1054,8 +1054,9 @@ namespace MWWorld } if (mWorldScene->isCellActive(cell)) { - mWorldScene->addObjectToScene(dropped); - + if (dropped.getRefData().isEnabled()) { + mWorldScene->addObjectToScene(dropped); + } std::string script = MWWorld::Class::get(dropped).getScript(dropped); if (!script.empty()) { mLocalScripts.add(script, dropped); From 620b6bf27b8605d69bf5aae24f8d7bb66c72ef67 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jul 2012 17:08:39 +0200 Subject: [PATCH 288/688] change url to work without publickey --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index f53c97677..d2a4cf0d3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "extern/shiny"] path = extern/shiny - url = git@github.com:scrawl/shiny.git + url = git://github.com/scrawl/shiny.git From d5e63a767ec05360cc7fdd974fe486140a1fa3cf Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 26 Jul 2012 19:38:33 +0400 Subject: [PATCH 289/688] replace sprintf() with boost::format --- libs/openengine/bullet/physic.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 089825a9d..a778aef3a 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -11,6 +11,7 @@ #include "BtOgreExtras.h" #include +#include #define BIT(x) (1<<(x)) @@ -333,10 +334,8 @@ namespace Physic RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { - char uniqueID[8]; - sprintf( uniqueID, "%07.3f", scale ); - std::string sid = uniqueID; - std::string outputstring = mesh + uniqueID; + std::string sid = (boost::format("%07.3f") % scale).str(); + std::string outputstring = mesh + sid; //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif @@ -571,10 +570,8 @@ namespace Physic void PhysicEngine::getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max) { - char uniqueID[8]; - sprintf( uniqueID, "%07.3f", scale ); - std::string sid = uniqueID; - std::string outputstring = mesh + uniqueID; + std::string sid = (boost::format("%07.3f") % scale).str(); + std::string outputstring = mesh + sid; mShapeLoader->load(outputstring, "General"); BulletShapeManager::getSingletonPtr()->load(outputstring, "General"); From 7725190f89dec2f67c3e3ad5412584d575263192 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 26 Jul 2012 19:09:45 +0200 Subject: [PATCH 290/688] removed some redundant code --- apps/openmw/mwworld/class.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index bb9b7d982..5267e368d 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -232,13 +232,6 @@ namespace MWWorld { Ptr newPtr = copyToCellImpl(ptr, cell); - newPtr.getRefData().setCount(ptr.getRefData().getCount()); - if (ptr.getRefData().isEnabled()) { - newPtr.getRefData().enable(); - } else { - newPtr.getRefData().disable(); - } - return newPtr; } @@ -251,4 +244,3 @@ namespace MWWorld return newPtr; } } - From 6077965d27c036b7a1aad512490db253aa4251ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jul 2012 23:09:46 +0200 Subject: [PATCH 291/688] fix the directx startup issue on windows --- apps/openmw/mwrender/renderingmanager.cpp | 1 + extern/shiny | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bee4bd68a..ae0e57219 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -72,6 +72,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const else lang = sh::Language_CG; mFactory->setCurrentLanguage (lang); + mFactory->loadAllFiles(); //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog diff --git a/extern/shiny b/extern/shiny index 5546a5bd8..164bc8d3b 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 5546a5bd8474ef328dfedae2df42126cdaf9515f +Subproject commit 164bc8d3bfe860bd16ad89c0bd1b59f465c9bb24 From 281e15f58e0a290b2f8c79135f32d5315fe17ebd Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 26 Jul 2012 23:40:55 +0200 Subject: [PATCH 292/688] consider all material properties for nif material sharing, instead of just the texture --- components/nifogre/ogre_nif_loader.cpp | 47 ++++++++++++++++---------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 803300282..4393749fb 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -39,6 +39,7 @@ #include #include +#include #include @@ -441,7 +442,7 @@ static CompareFunction getTestMode(int mode) class NIFMaterialLoader { -static std::multimap MaterialMap; +static std::map MaterialMap; static void warn(const std::string &msg) { @@ -473,6 +474,8 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam ubyte alphaTest = 0; Ogre::String texName; + bool vertexColour = (shape->data->colors.size() != 0); + // These are set below if present const NiTexturingProperty *t = NULL; const NiMaterialProperty *m = NULL; @@ -537,23 +540,32 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam Ogre::String matname = name; if (m || !texName.empty()) { - // If we're here, then this mesh has a material. Thus we - // need to calculate a snappy material name. It should - // contain the mesh name (mesh->getName()) but also has to - // be unique. One mesh may use many materials. - std::multimap::iterator itr = MaterialMap.find(texName); - std::multimap::iterator lastElement; - lastElement = MaterialMap.upper_bound(texName); + // Generate a hash out of all properties that can affect the material. + size_t h = 0; + boost::hash_combine(h, ambient.x); + boost::hash_combine(h, ambient.y); + boost::hash_combine(h, ambient.z); + boost::hash_combine(h, diffuse.x); + boost::hash_combine(h, diffuse.y); + boost::hash_combine(h, diffuse.z); + boost::hash_combine(h, specular.x); + boost::hash_combine(h, specular.y); + boost::hash_combine(h, specular.z); + boost::hash_combine(h, emissive.x); + boost::hash_combine(h, emissive.y); + boost::hash_combine(h, emissive.z); + boost::hash_combine(h, texName); + boost::hash_combine(h, vertexColour); + boost::hash_combine(h, alphaFlags); + + std::map::iterator itr = MaterialMap.find(h); if (itr != MaterialMap.end()) { - for ( ; itr != lastElement; ++itr) - { - //std::cout << "OK!"; - //MaterialPtr mat = MaterialManager::getSingleton().getByName(itr->second,recourceGroup); - return itr->second; - //if( mat->getA - } + // a suitable material exists already - use it + return itr->second; } + // not found, create a new one + MaterialMap.insert(std::make_pair(h, matname)); } // No existing material like this. Create a new one. @@ -572,7 +584,7 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam instance->setProperty ("diffuseMap", sh::makeProperty(texName)); - if (shape->data->colors.size() != 0) + if (vertexColour) instance->setProperty ("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); // Add transparency if NiAlphaProperty was present @@ -628,12 +640,11 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam } */ - MaterialMap.insert(std::make_pair(texName, matname)); return matname; } }; -std::multimap NIFMaterialLoader::MaterialMap; +std::map NIFMaterialLoader::MaterialMap; class NIFMeshLoader : Ogre::ManualResourceLoader From 78fe6fdce5a837b6f8c2eb31a42390d99fdc25e5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jul 2012 12:00:10 +0200 Subject: [PATCH 293/688] Issue #351: Refactoring Action class --- apps/openmw/mwclass/door.cpp | 6 +++--- apps/openmw/mwclass/potion.cpp | 5 ++--- apps/openmw/mwgui/bookwindow.cpp | 4 +++- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 4 +++- apps/openmw/mwscript/interpretercontext.cpp | 3 ++- apps/openmw/mwworld/action.cpp | 11 +++++++++++ apps/openmw/mwworld/action.hpp | 10 +++++++--- apps/openmw/mwworld/actionalchemy.cpp | 2 +- apps/openmw/mwworld/actionalchemy.hpp | 3 +-- apps/openmw/mwworld/actionapply.cpp | 18 +++++++++--------- apps/openmw/mwworld/actionapply.hpp | 14 ++++++-------- apps/openmw/mwworld/actionequip.cpp | 2 +- apps/openmw/mwworld/actionequip.hpp | 4 ++-- apps/openmw/mwworld/actionopen.cpp | 2 +- apps/openmw/mwworld/actionopen.hpp | 3 ++- apps/openmw/mwworld/actionread.cpp | 2 +- apps/openmw/mwworld/actionread.hpp | 4 ++-- apps/openmw/mwworld/actiontake.cpp | 2 +- apps/openmw/mwworld/actiontake.hpp | 4 ++-- apps/openmw/mwworld/actiontalk.cpp | 2 +- apps/openmw/mwworld/actiontalk.hpp | 4 ++-- apps/openmw/mwworld/actionteleport.cpp | 4 ++-- apps/openmw/mwworld/actionteleport.hpp | 8 ++++---- apps/openmw/mwworld/nullaction.hpp | 4 +--- 25 files changed, 71 insertions(+), 56 deletions(-) create mode 100644 apps/openmw/mwworld/action.cpp diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 359e50ffb..6939c356e 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -40,7 +40,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Door::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -97,18 +97,18 @@ namespace MWClass if (ref->ref.teleport) { // teleport door + /// \todo remove this if clause once ActionTeleport can also support other actors if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) { // the player is using the door // The reason this is not 3D is that it would get interrupted when you teleport MWBase::Environment::get().getSoundManager()->playSound(openSound, 1.0, 1.0); return boost::shared_ptr ( - new MWWorld::ActionTeleportPlayer (ref->ref.destCell, ref->ref.doorDest)); + new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); } else { // another NPC or a creature is using the door - // TODO return action for teleporting other NPC/creature return boost::shared_ptr (new MWWorld::NullAction); } } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 993dac6f6..3d370ac32 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -40,7 +40,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Potion::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -158,7 +158,7 @@ namespace MWClass MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); return boost::shared_ptr ( - new MWWorld::ActionApply (actor, ref->base->mId, actor)); + new MWWorld::ActionApply (actor, ref->base->mId)); } MWWorld::Ptr @@ -170,4 +170,3 @@ namespace MWClass return MWWorld::Ptr(&cell.potions.insert(*ref), &cell); } } - diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 1ea3da839..1e0301d8e 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -3,9 +3,11 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwinput/inputmanager.hpp" #include "../mwsound/soundmanager.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/player.hpp" #include "formatting.hpp" #include "window_manager.hpp" @@ -99,7 +101,7 @@ void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); MWWorld::ActionTake take(mBook); - take.execute(); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); mWindowManager.removeGuiMode(GM_Book); } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 23a96b1d3..926c35172 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -175,7 +175,7 @@ namespace MWGui boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); - action->execute(); + action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); // this is necessary for books/scrolls: if they are already in the player's inventory, // the "Take" button should not be visible. diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index e6ff71a14..00e5a01dc 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -1,8 +1,10 @@ #include "scrollwindow.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwinput/inputmanager.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/player.hpp" #include "../mwsound/soundmanager.hpp" #include "formatting.hpp" @@ -63,7 +65,7 @@ void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); MWWorld::ActionTake take(mScroll); - take.execute(); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); mWindowManager.removeGuiMode(GM_Scroll); } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 5da512778..3a824473e 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -10,6 +10,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "../mwgui/window_manager.hpp" @@ -236,7 +237,7 @@ namespace MWScript if (!mAction.get()) throw std::runtime_error ("activation failed, because no action to perform"); - mAction->execute(); + mAction->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); mActivationHandled = true; } diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp new file mode 100644 index 000000000..0fcbf4a6f --- /dev/null +++ b/apps/openmw/mwworld/action.cpp @@ -0,0 +1,11 @@ + +#include "action.hpp" + +MWWorld::Action::Action() {} + +MWWorld::Action::~Action() {} + +void MWWorld::Action::execute (const Ptr& actor) +{ + executeImp (actor); +} diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index d5cf12779..c95648135 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -3,6 +3,8 @@ namespace MWWorld { + class Ptr; + /// \brief Abstract base for actions class Action { @@ -10,13 +12,15 @@ namespace MWWorld Action (const Action& action); Action& operator= (const Action& action); + virtual void executeImp (const Ptr& actor) = 0; + public: - Action() {} + Action(); - virtual ~Action() {} + virtual ~Action(); - virtual void execute() = 0; + void execute (const Ptr& actor); }; } diff --git a/apps/openmw/mwworld/actionalchemy.cpp b/apps/openmw/mwworld/actionalchemy.cpp index eb91b6946..a7ee4fd0e 100644 --- a/apps/openmw/mwworld/actionalchemy.cpp +++ b/apps/openmw/mwworld/actionalchemy.cpp @@ -5,7 +5,7 @@ namespace MWWorld { - void ActionAlchemy::execute() + void ActionAlchemy::executeImp (const Ptr& actor) { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Alchemy); } diff --git a/apps/openmw/mwworld/actionalchemy.hpp b/apps/openmw/mwworld/actionalchemy.hpp index 739de419b..e6d1a7976 100644 --- a/apps/openmw/mwworld/actionalchemy.hpp +++ b/apps/openmw/mwworld/actionalchemy.hpp @@ -7,8 +7,7 @@ namespace MWWorld { class ActionAlchemy : public Action { - public: - virtual void execute (); + virtual void executeImp (const Ptr& actor); }; } diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp index b330a70e7..595ee6cb3 100644 --- a/apps/openmw/mwworld/actionapply.cpp +++ b/apps/openmw/mwworld/actionapply.cpp @@ -5,24 +5,24 @@ namespace MWWorld { - ActionApply::ActionApply (const Ptr& target, const std::string& id, const Ptr& actor) - : mTarget (target), mId (id), mActor (actor) + ActionApply::ActionApply (const Ptr& target, const std::string& id) + : mTarget (target), mId (id) {} - void ActionApply::execute() + void ActionApply::executeImp (const Ptr& actor) { - MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor); + MWWorld::Class::get (mTarget).apply (mTarget, mId, actor); } ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id, - const Ptr& actor, int skillIndex, int usageType) - : mTarget (target), mId (id), mActor (actor), mSkillIndex (skillIndex), mUsageType (usageType) + int skillIndex, int usageType) + : mTarget (target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) {} - void ActionApplyWithSkill::execute() + void ActionApplyWithSkill::executeImp (const Ptr& actor) { - if (MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor) && mUsageType!=-1) - MWWorld::Class::get (mTarget).skillUsageSucceeded (mActor, mSkillIndex, mUsageType); + if (MWWorld::Class::get (mTarget).apply (mTarget, mId, actor) && mUsageType!=-1) + MWWorld::Class::get (mTarget).skillUsageSucceeded (actor, mSkillIndex, mUsageType); } } diff --git a/apps/openmw/mwworld/actionapply.hpp b/apps/openmw/mwworld/actionapply.hpp index 972417e02..523bf9373 100644 --- a/apps/openmw/mwworld/actionapply.hpp +++ b/apps/openmw/mwworld/actionapply.hpp @@ -13,29 +13,27 @@ namespace MWWorld { Ptr mTarget; std::string mId; - Ptr mActor; + + virtual void executeImp (const Ptr& actor); public: - ActionApply (const Ptr& target, const std::string& id, const Ptr& actor); - - virtual void execute(); + ActionApply (const Ptr& target, const std::string& id); }; class ActionApplyWithSkill : public Action { Ptr mTarget; std::string mId; - Ptr mActor; int mSkillIndex; int mUsageType; + virtual void executeImp (const Ptr& actor); + public: - ActionApplyWithSkill (const Ptr& target, const std::string& id, const Ptr& actor, + ActionApplyWithSkill (const Ptr& target, const std::string& id, int skillIndex, int usageType); - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 52b9437fd..20e0afb68 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -13,7 +13,7 @@ namespace MWWorld { } - void ActionEquip::execute () + void ActionEquip::executeImp (const Ptr& actor) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); diff --git a/apps/openmw/mwworld/actionequip.hpp b/apps/openmw/mwworld/actionequip.hpp index 6cf3640f8..5685a294a 100644 --- a/apps/openmw/mwworld/actionequip.hpp +++ b/apps/openmw/mwworld/actionequip.hpp @@ -10,11 +10,11 @@ namespace MWWorld { Ptr mObject; + virtual void executeImp (const Ptr& actor); + public: /// @param item to equip ActionEquip (const Ptr& object); - - virtual void execute (); }; } diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index a70773af9..c73ef9149 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -15,7 +15,7 @@ namespace MWWorld mContainer = container; } - void ActionOpen::execute () + void ActionOpen::executeImp (const MWWorld::Ptr& actor) { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return; diff --git a/apps/openmw/mwworld/actionopen.hpp b/apps/openmw/mwworld/actionopen.hpp index eff26c78c..5666ff293 100644 --- a/apps/openmw/mwworld/actionopen.hpp +++ b/apps/openmw/mwworld/actionopen.hpp @@ -12,10 +12,11 @@ namespace MWWorld { Ptr mContainer; + virtual void executeImp (const MWWorld::Ptr& actor); + public: ActionOpen (const Ptr& container); ///< \param The Container the Player has activated. - virtual void execute (); }; } diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 1e03230c7..c81d79e03 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -11,7 +11,7 @@ namespace MWWorld { } - void ActionRead::execute () + void ActionRead::executeImp (const MWWorld::Ptr& actor) { LiveCellRef *ref = mObject.get(); diff --git a/apps/openmw/mwworld/actionread.hpp b/apps/openmw/mwworld/actionread.hpp index a4b495f79..9bb74fb88 100644 --- a/apps/openmw/mwworld/actionread.hpp +++ b/apps/openmw/mwworld/actionread.hpp @@ -10,11 +10,11 @@ namespace MWWorld { Ptr mObject; // book or scroll to read + virtual void executeImp (const MWWorld::Ptr& actor); + public: /// @param book or scroll to read ActionRead (const Ptr& object); - - virtual void execute (); }; } diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 39544b35d..5207f1a10 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -13,7 +13,7 @@ namespace MWWorld { ActionTake::ActionTake (const MWWorld::Ptr& object) : mObject (object) {} - void ActionTake::execute() + void ActionTake::executeImp (const Ptr& actor) { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return; diff --git a/apps/openmw/mwworld/actiontake.hpp b/apps/openmw/mwworld/actiontake.hpp index f495fc3c4..52a114acf 100644 --- a/apps/openmw/mwworld/actiontake.hpp +++ b/apps/openmw/mwworld/actiontake.hpp @@ -10,11 +10,11 @@ namespace MWWorld { MWWorld::Ptr mObject; + virtual void executeImp (const Ptr& actor); + public: ActionTake (const MWWorld::Ptr& object); - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index a0f9d8c4c..78171bbe5 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -9,7 +9,7 @@ namespace MWWorld { ActionTalk::ActionTalk (const Ptr& actor) : mActor (actor) {} - void ActionTalk::execute() + void ActionTalk::executeImp (const Ptr& actor) { MWBase::Environment::get().getDialogueManager()->startDialogue (mActor); } diff --git a/apps/openmw/mwworld/actiontalk.hpp b/apps/openmw/mwworld/actiontalk.hpp index 1b7b9b6d6..53adf9e53 100644 --- a/apps/openmw/mwworld/actiontalk.hpp +++ b/apps/openmw/mwworld/actiontalk.hpp @@ -10,12 +10,12 @@ namespace MWWorld { Ptr mActor; + virtual void executeImp (const Ptr& actor); + public: ActionTalk (const Ptr& actor); ///< \param actor The actor the player is talking to - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 8ae3244f8..9c87d37ae 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -6,12 +6,12 @@ namespace MWWorld { - ActionTeleportPlayer::ActionTeleportPlayer (const std::string& cellName, + ActionTeleport::ActionTeleport (const std::string& cellName, const ESM::Position& position) : mCellName (cellName), mPosition (position) {} - void ActionTeleportPlayer::execute() + void ActionTeleport::executeImp (const Ptr& actor) { if (mCellName.empty()) MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index 00efdc876..a13cb61b2 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -9,17 +9,17 @@ namespace MWWorld { - class ActionTeleportPlayer : public Action + class ActionTeleport : public Action { std::string mCellName; ESM::Position mPosition; + virtual void executeImp (const Ptr& actor); + public: - ActionTeleportPlayer (const std::string& cellName, const ESM::Position& position); + ActionTeleport (const std::string& cellName, const ESM::Position& position); ///< If cellName is empty, an exterior cell is asumed. - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/nullaction.hpp b/apps/openmw/mwworld/nullaction.hpp index c8e3368e4..7ef8b4a06 100644 --- a/apps/openmw/mwworld/nullaction.hpp +++ b/apps/openmw/mwworld/nullaction.hpp @@ -8,9 +8,7 @@ namespace MWWorld /// \brief Action: do nothing class NullAction : public Action { - public: - - virtual void execute() {} + virtual void executeImp (const Ptr& actor) {} }; } From 8b08928dae542758bdf59eb423cedf8b31e1082f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jul 2012 12:19:25 +0200 Subject: [PATCH 294/688] Issue #351: Added sound playing to Action --- apps/openmw/mwworld/action.cpp | 13 +++++++++++++ apps/openmw/mwworld/action.hpp | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 0fcbf4a6f..0a57d5f67 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -1,11 +1,24 @@ #include "action.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwsound/soundmanager.hpp" + MWWorld::Action::Action() {} MWWorld::Action::~Action() {} void MWWorld::Action::execute (const Ptr& actor) { + if (!mSoundId.empty()) + MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, + MWSound::Play_NoTrack); + executeImp (actor); } + +void MWWorld::Action::setSound (const std::string& id) +{ + mSoundId = id; +} diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index c95648135..a00f67951 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWWORLD_ACTION_H #define GAME_MWWORLD_ACTION_H +#include + namespace MWWorld { class Ptr; @@ -8,6 +10,8 @@ namespace MWWorld /// \brief Abstract base for actions class Action { + std::string mSoundId; + // not implemented Action (const Action& action); Action& operator= (const Action& action); @@ -21,6 +25,8 @@ namespace MWWorld virtual ~Action(); void execute (const Ptr& actor); + + void setSound (const std::string& id); }; } From 0da3f2cb594abf9bd41698427b1c831d8a6a4dd8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Jul 2012 12:27:22 +0200 Subject: [PATCH 295/688] Issue #314: Playing drink sound when using a potion --- apps/openmw/mwclass/potion.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 3d370ac32..d3f2912ea 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -157,8 +157,12 @@ namespace MWClass MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - return boost::shared_ptr ( + boost::shared_ptr action ( new MWWorld::ActionApply (actor, ref->base->mId)); + + action->setSound ("Drink"); + + return action; } MWWorld::Ptr From fbe9a94568d60b9742ab9216d803de18f2912625 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 01:53:50 +0400 Subject: [PATCH 296/688] bug #348: fixed OS X deployment just enable CMake option "OPENMW_OSX_DEPLOYMENT" and it will search plugins inside application bundle instead of Ogre prefix --- CMakeLists.txt | 17 +++++++------ apps/launcher/graphicspage.cpp | 17 ++++++------- components/CMakeLists.txt | 4 +++ components/ogreplugin/ogreplugin.cpp | 37 ++++++++++++++++++++++++++++ components/ogreplugin/ogreplugin.h | 12 +++++++++ libs/openengine/ogre/renderer.cpp | 23 ++++++----------- 6 files changed, 78 insertions(+), 32 deletions(-) create mode 100644 components/ogreplugin/ogreplugin.cpp create mode 100644 components/ogreplugin/ogreplugin.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 05c53e6d6..0bc6344c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,9 @@ option(USE_FFMPEG "use ffmpeg for sound" OFF) option(USE_AUDIERE "use audiere for sound" OFF) option(USE_MPG123 "use mpg123 + libsndfile for sound" ON) +# OS X deployment +option(OPENMW_OSX_DEPLOYMENT OFF) + find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems") # Location of morrowind data files @@ -241,12 +244,8 @@ if (APPLE) else () set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) endif () - - set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") - -# set(OGRE_PLUGIN_DIR_2 ${OGRE_PLUGIN_DIR}) -# set(OGRE_PLUGIN_DIR "") -# set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_2}) + + #set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist "${APP_BUNDLE_DIR}/Contents/Info.plist") @@ -270,7 +269,11 @@ if (DEFINED CMAKE_BUILD_TYPE) endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") -add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") +if (APPLE AND OPENMW_OSX_DEPLOYMENT) + add_definitions(-DOGRE_PLUGIN_DIR="${APP_BUNDLE_NAME}/Contents/Plugins") +else() + add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") +endif() add_subdirectory(files/) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 296ef2c3e..f9f5c6dda 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "graphicspage.hpp" #include "naturalsort.hpp" @@ -115,17 +116,13 @@ bool GraphicsPage::setupOgre() #endif } - std::string glPlugin = std::string(pluginDir) + "/RenderSystem_GL" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(glPlugin + ".so") || - boost::filesystem::exists(glPlugin + ".dll") || - boost::filesystem::exists(glPlugin + ".dylib")) - mOgre->loadPlugin (glPlugin); + boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); - std::string dxPlugin = std::string(pluginDir) + "/RenderSystem_Direct3D9" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(dxPlugin + ".so") || - boost::filesystem::exists(dxPlugin + ".dll") || - boost::filesystem::exists(dxPlugin + ".dylib")) - mOgre->loadPlugin (dxPlugin); + pluginDir = absPluginPath.string(); + + loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); + loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre); + loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); #ifdef ENABLE_PLUGIN_GL mGLPlugin = new Ogre::GLPlugin(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index efeb69cae..e16a860c4 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -66,6 +66,10 @@ add_component_dir (interpreter miscopcodes opcodes runtime scriptopcodes spatialopcodes types ) +add_component_dir (ogreplugin + ogreplugin +) + include_directories(${BULLET_INCLUDE_DIRS}) add_library(components STATIC ${COMPONENT_FILES}) diff --git a/components/ogreplugin/ogreplugin.cpp b/components/ogreplugin/ogreplugin.cpp new file mode 100644 index 000000000..5516482aa --- /dev/null +++ b/components/ogreplugin/ogreplugin.cpp @@ -0,0 +1,37 @@ +#include "ogreplugin.h" + +#include +#include + +#include + +bool loadOgrePlugin(std::string pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + std::ostringstream verStream; + verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; + pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX + verStream.str(); +#endif + + std::string pluginExt; +#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + pluginExt = ".dll"; +#endif +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + pluginExt = ".dylib"; +#endif +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + pluginExt = ".so"; +#endif + + std::string pluginPath = pluginDir + "/" + pluginName + pluginExt; + + std::cout << "loading plugin: " << pluginPath << std::endl; + + if (boost::filesystem::exists(pluginPath)) { + ogreRoot.loadPlugin(pluginPath); + return true; + } + else { + return false; + } +} \ No newline at end of file diff --git a/components/ogreplugin/ogreplugin.h b/components/ogreplugin/ogreplugin.h new file mode 100644 index 000000000..bb1ea04e1 --- /dev/null +++ b/components/ogreplugin/ogreplugin.h @@ -0,0 +1,12 @@ +#ifndef OGREPLUGIN_H +#define OGREPLUGIN_H + +#include + +namespace Ogre { + class Root; +} + +extern bool loadOgrePlugin(std::string pluginDir, std::string pluginName, Ogre::Root &ogreRoot); + +#endif \ No newline at end of file diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 58b363f7f..20bb1f40f 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include #include @@ -94,6 +96,7 @@ void OgreRenderer::configure(const std::string &logPath, loadPlugins(); #endif + std::cout << "current path is: " << boost::filesystem::current_path() << std::endl; std::string pluginDir; const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); if (pluginEnv) @@ -111,23 +114,13 @@ void OgreRenderer::configure(const std::string &logPath, #endif } - std::string glPlugin = std::string(pluginDir) + "/RenderSystem_GL" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(glPlugin + ".so") || - boost::filesystem::exists(glPlugin + ".dll") || - boost::filesystem::exists(glPlugin + ".dylib")) - mRoot->loadPlugin (glPlugin); + boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); - std::string dxPlugin = std::string(pluginDir) + "/RenderSystem_Direct3D9" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(dxPlugin + ".so") || - boost::filesystem::exists(dxPlugin + ".dll") || - boost::filesystem::exists(dxPlugin + ".dylib")) - mRoot->loadPlugin (dxPlugin); + pluginDir = absPluginPath.string(); - std::string cgPlugin = std::string(pluginDir) + "/Plugin_CgProgramManager" + OGRE_PLUGIN_DEBUG_SUFFIX; - if (boost::filesystem::exists(cgPlugin + ".so") || - boost::filesystem::exists(cgPlugin + ".dll") || - boost::filesystem::exists(cgPlugin + ".dylib")) - mRoot->loadPlugin (cgPlugin); + loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); + loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); + loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) From e9d4195500287266ee9a6e78006dae4141e3d7ea Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 02:03:34 +0400 Subject: [PATCH 297/688] removed cout spam --- libs/openengine/ogre/renderer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 20bb1f40f..f8fed3899 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -96,7 +96,6 @@ void OgreRenderer::configure(const std::string &logPath, loadPlugins(); #endif - std::cout << "current path is: " << boost::filesystem::current_path() << std::endl; std::string pluginDir; const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); if (pluginEnv) From 0cdb651c5ece1bf5aebf55b9cd8d14ec1fbaf0b4 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 02:05:06 +0400 Subject: [PATCH 298/688] removed excess invocation --- apps/launcher/graphicspage.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index f9f5c6dda..7d25f2df1 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -122,7 +122,6 @@ bool GraphicsPage::setupOgre() loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre); - loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); #ifdef ENABLE_PLUGIN_GL mGLPlugin = new Ogre::GLPlugin(); From a0a086f69d7ea7feb35452b3475d96435126a8e7 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 02:11:14 +0400 Subject: [PATCH 299/688] fixed redefining macro --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bc6344c4..cbb895760 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,8 +263,6 @@ if (DEFINED CMAKE_BUILD_TYPE) if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) set(DEBUG_SUFFIX "_d") add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") - else() - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") endif() endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") From 23e44a86c6c7be2b6373df3a0f0afefef8ec7ca7 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 02:13:57 +0400 Subject: [PATCH 300/688] another attempt to fix --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbb895760..b51ea68ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,12 +257,13 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix set(DEBUG_SUFFIX "") -add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") if (DEFINED CMAKE_BUILD_TYPE) # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) set(DEBUG_SUFFIX "_d") add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") + else() + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") endif() endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") From b0b206423285180f0e84a0a2214ccb84d339de43 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 02:31:30 +0400 Subject: [PATCH 301/688] always add debug suffix in plugin loader also removed cout spam --- components/ogreplugin/ogreplugin.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/ogreplugin/ogreplugin.cpp b/components/ogreplugin/ogreplugin.cpp index 5516482aa..ce61f344d 100644 --- a/components/ogreplugin/ogreplugin.cpp +++ b/components/ogreplugin/ogreplugin.cpp @@ -6,10 +6,11 @@ #include bool loadOgrePlugin(std::string pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { + pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; - pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX + verStream.str(); + pluginName = pluginName + verStream.str(); #endif std::string pluginExt; @@ -24,9 +25,6 @@ bool loadOgrePlugin(std::string pluginDir, std::string pluginName, Ogre::Root &o #endif std::string pluginPath = pluginDir + "/" + pluginName + pluginExt; - - std::cout << "loading plugin: " << pluginPath << std::endl; - if (boost::filesystem::exists(pluginPath)) { ogreRoot.loadPlugin(pluginPath); return true; From 7161361b520fdb895ba42270613e00c3c4457e68 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 02:38:37 +0400 Subject: [PATCH 302/688] cleanup --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b51ea68ea..f2decb8f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,11 +256,9 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix -set(DEBUG_SUFFIX "") if (DEFINED CMAKE_BUILD_TYPE) # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) - set(DEBUG_SUFFIX "_d") add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") else() add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") From a84d8e83cd04e7bfaa25ec7b0e566ef9b0026bd2 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 28 Jul 2012 02:39:49 +0400 Subject: [PATCH 303/688] add const specifier to first argument, also made it reference --- components/ogreplugin/ogreplugin.cpp | 2 +- components/ogreplugin/ogreplugin.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/ogreplugin/ogreplugin.cpp b/components/ogreplugin/ogreplugin.cpp index ce61f344d..329cd1ea9 100644 --- a/components/ogreplugin/ogreplugin.cpp +++ b/components/ogreplugin/ogreplugin.cpp @@ -5,7 +5,7 @@ #include -bool loadOgrePlugin(std::string pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { +bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; diff --git a/components/ogreplugin/ogreplugin.h b/components/ogreplugin/ogreplugin.h index bb1ea04e1..d9b00e576 100644 --- a/components/ogreplugin/ogreplugin.h +++ b/components/ogreplugin/ogreplugin.h @@ -7,6 +7,6 @@ namespace Ogre { class Root; } -extern bool loadOgrePlugin(std::string pluginDir, std::string pluginName, Ogre::Root &ogreRoot); +extern bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot); #endif \ No newline at end of file From 90de02b901bde3e62bdf6438998f2365a3704e1f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 30 Jul 2012 11:43:28 +0200 Subject: [PATCH 304/688] Issue #350: console only script instructions --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 9 ++- apps/openmw/engine.hpp | 4 + apps/openmw/main.cpp | 10 ++- apps/openmw/mwgui/console.cpp | 10 ++- apps/openmw/mwgui/console.hpp | 5 +- apps/openmw/mwgui/window_manager.cpp | 4 +- apps/openmw/mwgui/window_manager.hpp | 2 +- apps/openmw/mwscript/consoleextensions.cpp | 24 ++++++ apps/openmw/mwscript/consoleextensions.hpp | 25 ++++++ apps/openmw/mwscript/docs/vmformat.txt | 13 +++- apps/openmw/mwscript/extensions.cpp | 18 ++++- apps/openmw/mwscript/extensions.hpp | 8 +- apps/openmw/mwscript/userextensions.cpp | 91 ++++++++++++++++++++++ apps/openmw/mwscript/userextensions.hpp | 25 ++++++ 15 files changed, 230 insertions(+), 20 deletions(-) create mode 100644 apps/openmw/mwscript/consoleextensions.cpp create mode 100644 apps/openmw/mwscript/consoleextensions.hpp create mode 100644 apps/openmw/mwscript/userextensions.cpp create mode 100644 apps/openmw/mwscript/userextensions.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8e424ad56..556b9a1ba 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -39,7 +39,7 @@ add_openmw_dir (mwscript locals scriptmanager compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions - animationextensions transformationextensions + animationextensions transformationextensions consoleextensions userextensions ) add_openmw_dir (mwsound diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index ab5b63071..d224ab71b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -129,6 +129,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mCompileAll (false) , mScriptContext (0) , mFSStrict (false) + , mScriptConsoleMode (false) , mCfgMgr(configurationManager) { std::srand ( std::time(NULL) ); @@ -326,7 +327,8 @@ void OMW::Engine::go() MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"))); + mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mScriptConsoleMode)); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -490,3 +492,8 @@ void OMW::Engine::setFallbackValues(std::map fallbackMa { mFallbackMap = fallbackMap; } + +void OMW::Engine::setScriptConsoleMode (bool enabled) +{ + mScriptConsoleMode = enabled; +} diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 031cae551..01b2dd8d6 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -73,6 +73,7 @@ namespace OMW bool mCompileAll; std::string mFocusName; std::map mFallbackMap; + bool mScriptConsoleMode; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; @@ -158,6 +159,9 @@ namespace OMW void setFallbackValues(std::map map); + /// Enable console-only script functionality + void setScriptConsoleMode (bool enabled); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 993ec6623..c54c5969b 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -124,12 +124,15 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-verbose", bpo::value()->implicit_value(true) ->default_value(false), "verbose script output") - ("new-game", bpo::value()->implicit_value(true) - ->default_value(false), "activate char gen/new game mechanics") - ("script-all", bpo::value()->implicit_value(true) ->default_value(false), "compile all scripts (excluding dialogue scripts) at startup") + ("script-console", bpo::value()->implicit_value(true) + ->default_value(false), "enable console-only script functionality") + + ("new-game", bpo::value()->implicit_value(true) + ->default_value(false), "activate char gen/new game mechanics") + ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -249,6 +252,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setCompileAll(variables["script-all"].as()); engine.setAnimationVerbose(variables["anim-verbose"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); + engine.setScriptConsoleMode (variables["script-console"].as()); return true; } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 4101165c1..39f36d28a 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -105,9 +105,10 @@ namespace MWGui } } - Console::Console(int w, int h, const Compiler::Extensions& extensions) + Console::Console(int w, int h, bool consoleOnlyScripts) : Layout("openmw_console.layout"), - mCompilerContext (MWScript::CompilerContext::Type_Console) + mCompilerContext (MWScript::CompilerContext::Type_Console), + mConsoleOnlyScripts (consoleOnlyScripts) { setCoord(10,10, w-10, h/2); @@ -126,7 +127,8 @@ namespace MWGui history->setVisibleVScroll(true); // compiler - mCompilerContext.setExtensions (&extensions); + MWScript::registerExtensions (mExtensions, mConsoleOnlyScripts); + mCompilerContext.setExtensions (&mExtensions); } void Console::enable() @@ -246,7 +248,7 @@ namespace MWGui { ConsoleInterpreterContext interpreterContext (*this, mPtr); Interpreter::Interpreter interpreter; - MWScript::installOpcodes (interpreter); + MWScript::installOpcodes (interpreter, mConsoleOnlyScripts); std::vector code; output.getCode (code); interpreter.run (&code[0], code.size(), interpreterContext); diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index eadf4aa4e..b01ebf7fa 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "../mwscript/compilercontext.hpp" @@ -24,8 +25,10 @@ namespace MWGui { private: + Compiler::Extensions mExtensions; MWScript::CompilerContext mCompilerContext; std::vector mNames; + bool mConsoleOnlyScripts; bool compile (const std::string& cmd, Compiler::Output& output); @@ -62,7 +65,7 @@ namespace MWGui StringList::iterator current; std::string editString; - Console(int w, int h, const Compiler::Extensions& extensions); + Console(int w, int h, bool consoleOnlyScripts); void enable(); diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index aa0595b85..25f0fc097 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -41,7 +41,7 @@ using namespace MWGui; WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath) + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts) : mGuiManager(NULL) , mHud(NULL) , mMap(NULL) @@ -113,7 +113,7 @@ WindowManager::WindowManager( mMenu = new MainMenu(w,h); mMap = new MapWindow(*this); mStatsWindow = new StatsWindow(*this); - mConsole = new Console(w,h, extensions); + mConsole = new Console(w,h, consoleOnlyScripts); mJournal = new JournalWindow(*this); mMessageBoxManager = new MessageBoxManager(this); mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index adab79942..51fde071d 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -94,7 +94,7 @@ namespace MWGui typedef std::vector FactionList; typedef std::vector SkillList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath); + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); virtual ~WindowManager(); /** diff --git a/apps/openmw/mwscript/consoleextensions.cpp b/apps/openmw/mwscript/consoleextensions.cpp new file mode 100644 index 000000000..00b4f74e5 --- /dev/null +++ b/apps/openmw/mwscript/consoleextensions.cpp @@ -0,0 +1,24 @@ + +#include "consoleextensions.hpp" + +#include + +#include +#include +#include + +namespace MWScript +{ + namespace Console + { + void registerExtensions (Compiler::Extensions& extensions) + { + + } + + void installOpcodes (Interpreter::Interpreter& interpreter) + { + + } + } +} diff --git a/apps/openmw/mwscript/consoleextensions.hpp b/apps/openmw/mwscript/consoleextensions.hpp new file mode 100644 index 000000000..b10bf06a8 --- /dev/null +++ b/apps/openmw/mwscript/consoleextensions.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_SCRIPT_CONSOLEEXTENSIONS_H +#define GAME_SCRIPT_CONSOLEEXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWScript +{ + /// \brief Script functionality limited to the console + namespace Console + { + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); + } +} + +#endif diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b979420dd..3ca93d4b2 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -169,5 +169,14 @@ op 0x2000164: SetScale op 0x2000165: SetScale, explicit reference op 0x2000166: SetAngle op 0x2000167: SetAngle, explicit reference -opcodes 0x2000168-0x3ffffff unused - +op 0x2000168: GetScale +op 0x2000169: GetScale, explicit reference +op 0x200016a: GetAngle +op 0x200016b: GetAngle, explicit reference +op 0x200016c: user1 (console only, requires --script-console switch) +op 0x200016d: user2 (console only, requires --script-console switch) +op 0x200016e: user3, explicit reference (console only, requires --script-console switch) +op 0x200016f: user3 (implicit reference, console only, requires --script-console switch) +op 0x2000170: user4, explicit reference (console only, requires --script-console switch) +op 0x2000171: user4 (implicit reference, console only, requires --script-console switch) +opcodes 0x2000172-0x3ffffff unused diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp index b7425aca0..1b1560820 100644 --- a/apps/openmw/mwscript/extensions.cpp +++ b/apps/openmw/mwscript/extensions.cpp @@ -16,10 +16,12 @@ #include "dialogueextensions.hpp" #include "animationextensions.hpp" #include "transformationextensions.hpp" +#include "consoleextensions.hpp" +#include "userextensions.hpp" namespace MWScript { - void registerExtensions (Compiler::Extensions& extensions) + void registerExtensions (Compiler::Extensions& extensions, bool consoleOnly) { Cell::registerExtensions (extensions); Misc::registerExtensions (extensions); @@ -33,9 +35,15 @@ namespace MWScript Dialogue::registerExtensions (extensions); Animation::registerExtensions (extensions); Transformation::registerExtensions (extensions); + + if (consoleOnly) + { + Console::registerExtensions (extensions); + User::registerExtensions (extensions); + } } - void installOpcodes (Interpreter::Interpreter& interpreter) + void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly) { Interpreter::installOpcodes (interpreter); Cell::installOpcodes (interpreter); @@ -50,5 +58,11 @@ namespace MWScript Dialogue::installOpcodes (interpreter); Animation::installOpcodes (interpreter); Transformation::installOpcodes (interpreter); + + if (consoleOnly) + { + Console::installOpcodes (interpreter); + User::installOpcodes (interpreter); + } } } diff --git a/apps/openmw/mwscript/extensions.hpp b/apps/openmw/mwscript/extensions.hpp index 9738367a0..cb1aaf9db 100644 --- a/apps/openmw/mwscript/extensions.hpp +++ b/apps/openmw/mwscript/extensions.hpp @@ -13,9 +13,11 @@ namespace Interpreter namespace MWScript { - void registerExtensions (Compiler::Extensions& extensions); - - void installOpcodes (Interpreter::Interpreter& interpreter); + void registerExtensions (Compiler::Extensions& extensions, bool consoleOnly = false); + ///< \param consoleOnly include console only extensions + + void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly = false); + ///< \param consoleOnly include console only opcodes } #endif diff --git a/apps/openmw/mwscript/userextensions.cpp b/apps/openmw/mwscript/userextensions.cpp new file mode 100644 index 000000000..9c9738815 --- /dev/null +++ b/apps/openmw/mwscript/userextensions.cpp @@ -0,0 +1,91 @@ + +#include "userextensions.hpp" + +#include + +#include +#include +#include +#include + +#include "ref.hpp" + +namespace MWScript +{ + /// Temporary script extensions. + /// + /// \attention Do not commit changes to this file to a git repository! + namespace User + { + class OpUser1 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.getContext().report ("user1: not in use"); + } + }; + + class OpUser2 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.getContext().report ("user2: not in use"); + } + }; + + template + class OpUser3 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { +// MWWorld::Ptr ptr = R()(runtime); + + runtime.getContext().report ("user3: not in use"); + } + }; + + template + class OpUser4 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { +// MWWorld::Ptr ptr = R()(runtime); + + runtime.getContext().report ("user4: not in use"); + } + }; + + const int opcodeUser1 = 0x200016c; + const int opcodeUser2 = 0x200016d; + const int opcodeUser3 = 0x200016e; + const int opcodeUser3Explicit = 0x200016f; + const int opcodeUser4 = 0x2000170; + const int opcodeUser4Explicit = 0x2000171; + + void registerExtensions (Compiler::Extensions& extensions) + { + extensions.registerInstruction ("user1", "", opcodeUser1); + extensions.registerInstruction ("user2", "", opcodeUser2); + extensions.registerInstruction ("user3", "", opcodeUser3, opcodeUser3); + extensions.registerInstruction ("user4", "", opcodeUser4, opcodeUser4); + } + + void installOpcodes (Interpreter::Interpreter& interpreter) + { + interpreter.installSegment5 (opcodeUser1, new OpUser1); + interpreter.installSegment5 (opcodeUser2, new OpUser2); + interpreter.installSegment5 (opcodeUser3, new OpUser3); + interpreter.installSegment5 (opcodeUser3Explicit, new OpUser3); + interpreter.installSegment5 (opcodeUser4, new OpUser4); + interpreter.installSegment5 (opcodeUser4Explicit, new OpUser4); + } + } +} diff --git a/apps/openmw/mwscript/userextensions.hpp b/apps/openmw/mwscript/userextensions.hpp new file mode 100644 index 000000000..3642eb5f4 --- /dev/null +++ b/apps/openmw/mwscript/userextensions.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_SCRIPT_USEREXTENSIONS_H +#define GAME_SCRIPT_USEREXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWScript +{ + /// \brief Temporaty script functionality limited to the console + namespace User + { + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); + } +} + +#endif From 23f8595b8767f97492e9aeb67d82a8a8cbd81543 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 30 Jul 2012 11:57:19 +0200 Subject: [PATCH 305/688] Issue #352: refactored script execution in console --- apps/openmw/mwgui/console.cpp | 49 +++++++++++++++++++---------------- apps/openmw/mwgui/console.hpp | 2 ++ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 39f36d28a..fdcc67604 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -175,6 +175,32 @@ namespace MWGui print("#FF2222" + msg + "\n"); } + void Console::execute (const std::string& command) + { + // Log the command + print("#FFFFFF> " + command + "\n"); + + Compiler::Locals locals; + Compiler::Output output (locals); + + if (compile (command + "\n", output)) + { + try + { + ConsoleInterpreterContext interpreterContext (*this, mPtr); + Interpreter::Interpreter interpreter; + MWScript::installOpcodes (interpreter, mConsoleOnlyScripts); + std::vector code; + output.getCode (code); + interpreter.run (&code[0], code.size(), interpreterContext); + } + catch (const std::exception& error) + { + printError (std::string ("An exception has been thrown: ") + error.what()); + } + } + } + void Console::keyPress(MyGUI::WidgetPtr _sender, MyGUI::KeyCode key, MyGUI::Char _char) @@ -236,28 +262,7 @@ namespace MWGui current = command_history.end(); editString.clear(); - // Log the command - print("#FFFFFF> " + cm + "\n"); - - Compiler::Locals locals; - Compiler::Output output (locals); - - if (compile (cm + "\n", output)) - { - try - { - ConsoleInterpreterContext interpreterContext (*this, mPtr); - Interpreter::Interpreter interpreter; - MWScript::installOpcodes (interpreter, mConsoleOnlyScripts); - std::vector code; - output.getCode (code); - interpreter.run (&code[0], code.size(), interpreterContext); - } - catch (const std::exception& error) - { - printError (std::string ("An exception has been thrown: ") + error.what()); - } - } + execute (cm); command->setCaption(""); } diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index b01ebf7fa..f587fefed 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -89,6 +89,8 @@ namespace MWGui /// Error message void printError(const std::string &msg); + void execute (const std::string& command); + private: void keyPress(MyGUI::WidgetPtr _sender, From fd6c155118fbe469fb78fc584eb3fbf98df7a80f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 30 Jul 2012 12:37:46 +0200 Subject: [PATCH 306/688] Issue #352: added --script-run switch --- apps/openmw/engine.cpp | 8 ++++++++ apps/openmw/engine.hpp | 4 ++++ apps/openmw/main.cpp | 6 ++++++ apps/openmw/mwgui/console.cpp | 16 ++++++++++++++++ apps/openmw/mwgui/console.hpp | 2 ++ apps/openmw/mwgui/window_manager.cpp | 5 +++++ apps/openmw/mwgui/window_manager.hpp | 2 ++ 7 files changed, 43 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d224ab71b..5e9aedf66 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -390,6 +390,9 @@ void OMW::Engine::go() << std::endl; } + if (!mStartupScript.empty()) + MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); + // Start the main rendering loop mOgre->start(); @@ -497,3 +500,8 @@ void OMW::Engine::setScriptConsoleMode (bool enabled) { mScriptConsoleMode = enabled; } + +void OMW::Engine::setStartupScript (const std::string& path) +{ + mStartupScript = path; +} diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 01b2dd8d6..57402c91e 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -74,6 +74,7 @@ namespace OMW std::string mFocusName; std::map mFallbackMap; bool mScriptConsoleMode; + std::string mStartupScript; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; @@ -162,6 +163,9 @@ namespace OMW /// Enable console-only script functionality void setScriptConsoleMode (bool enabled); + /// Set path for a script that is run on startup in the console. + void setStartupScript (const std::string& path); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index c54c5969b..fb1657ad4 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -130,6 +130,11 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-console", bpo::value()->implicit_value(true) ->default_value(false), "enable console-only script functionality") + ("script-run", bpo::value()->default_value(""), + "set a file that is execute in the console on startup\n\n" + "Note: The file contains a list of script lines, but not a complete scripts. " + "That means no begin/end and no variable declarations.") + ("new-game", bpo::value()->implicit_value(true) ->default_value(false), "activate char gen/new game mechanics") @@ -253,6 +258,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setAnimationVerbose(variables["anim-verbose"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); engine.setScriptConsoleMode (variables["script-console"].as()); + engine.setStartupScript (variables["script-run"].as()); return true; } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index fdcc67604..86c8940a1 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -2,6 +2,7 @@ #include "console.hpp" #include +#include #include #include @@ -201,6 +202,21 @@ namespace MWGui } } + void Console::executeFile (const std::string& path) + { + std::ifstream stream (path.c_str()); + + if (!stream.is_open()) + printError ("failed to open file: " + path); + else + { + std::string line; + + while (std::getline (stream, line)) + execute (line); + } + } + void Console::keyPress(MyGUI::WidgetPtr _sender, MyGUI::KeyCode key, MyGUI::Char _char) diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index f587fefed..1893b0148 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -91,6 +91,8 @@ namespace MWGui void execute (const std::string& command); + void executeFile (const std::string& command); + private: void keyPress(MyGUI::WidgetPtr _sender, diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 25f0fc097..659af0447 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -740,3 +740,8 @@ bool WindowManager::getWorldMouseOver() { return mHud->getWorldMouseOver(); } + +void WindowManager::executeInConsole (const std::string& path) +{ + mConsole->executeFile (path); +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 51fde071d..3653615a6 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -237,6 +237,8 @@ namespace MWGui void processChangedSettings(const Settings::CategorySettingVector& changed); + void executeInConsole (const std::string& path); + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; From 2ccecd839bdac85672cd3aa861cfa95f25c27f46 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 30 Jul 2012 12:43:23 +0200 Subject: [PATCH 307/688] improved the help text for --script-run; updated readme.txt --- apps/openmw/main.cpp | 2 +- readme.txt | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index fb1657ad4..0ca9dd6d9 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -131,7 +131,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ->default_value(false), "enable console-only script functionality") ("script-run", bpo::value()->default_value(""), - "set a file that is execute in the console on startup\n\n" + "select a file that is executed in the console on startup\n\n" "Note: The file contains a list of script lines, but not a complete scripts. " "That means no begin/end and no variable declarations.") diff --git a/readme.txt b/readme.txt index ded9bcd7b..ce3d115e3 100644 --- a/readme.txt +++ b/readme.txt @@ -66,9 +66,16 @@ Allowed options: --debug [=arg(=1)] (=0) debug mode --nosound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output - --new-game [=arg(=1)] (=0) activate char gen/new game mechanics --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scri pts) at startup + --script-console [=arg(=1)] (=0) enable console-only script functionality + --script-run arg select a file that is executed in the consol + e on startup + + Note: The file contains a list of script + lines, but not a complete scripts. That mean + s no begin/end and no variable declarations. + --new-game [=arg(=1)] (=0) activate char gen/new game mechanics --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding ) --encoding arg (=win1252) Character encoding used in OpenMW game messa From d37d0b19474a31535ddd5bd3f1debf4ba5d58600 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 30 Jul 2012 19:45:40 +0200 Subject: [PATCH 308/688] build fix for windows --- apps/openmw/mwrender/terrainmaterial.cpp | 12 ++++++------ libs/openengine/ogre/renderer.cpp | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 1a2d96a14..9ef4652f6 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -86,15 +86,15 @@ namespace MWRender normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - uint maxLayers = getMaxLayers(terrain); - uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + Ogre::uint maxLayers = getMaxLayers(terrain); + Ogre::uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); + Ogre::uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); p->mShaderProperties.setProperty ("num_layers", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numLayers)))); p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); // blend maps - for (uint i = 0; i < numBlendTextures; ++i) + for (Ogre::uint i = 0; i < numBlendTextures; ++i) { sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(i)))); @@ -102,7 +102,7 @@ namespace MWRender } // layer maps - for (uint i = 0; i < numLayers; ++i) + for (Ogre::uint i = 0; i < numLayers; ++i) { sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(i, 0)))); @@ -111,7 +111,7 @@ namespace MWRender } // shadow - for (uint i = 0; i < 3; ++i) + for (Ogre::uint i = 0; i < 3; ++i) { sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index e40bdf708..b1893568e 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -110,7 +110,9 @@ void OgreRenderer::configure(const std::string &logPath, pluginDir = OGRE_PLUGIN_DIR_REL; #endif } - +#ifndef OGRE_PLUGIN_DEBUG_SUFFIX +#define OGRE_PLUGIN_DEBUG_SUFFIX "" +#endif std::string glPlugin = std::string(pluginDir) + "/RenderSystem_GL" + OGRE_PLUGIN_DEBUG_SUFFIX; if (boost::filesystem::exists(glPlugin + ".so") || boost::filesystem::exists(glPlugin + ".dll")) mRoot->loadPlugin (glPlugin); From e7a1ab9fa627e5509d0611d86b07e0ed9b65d5ce Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Mon, 30 Jul 2012 15:04:14 -0400 Subject: [PATCH 309/688] Define OGRE_PLUGIN_DEBUG_SUFFIX in all cases. --- CMakeLists.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea0c41fe2..9ac6419ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,14 +235,13 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix set(DEBUG_SUFFIX "") -if (DEFINED CMAKE_BUILD_TYPE) - if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(DEBUG_SUFFIX "_d") - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") - else() - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") - endif() +if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DEBUG_SUFFIX "_d") + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") +else() + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") endif() + add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") From d6150b7482ab9eed2f3337cc30d18a55de64d4aa Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 30 Jul 2012 23:28:14 +0400 Subject: [PATCH 310/688] high-level implementation, unstable --- apps/openmw/mwclass/creature.hpp | 5 ++ apps/openmw/mwclass/npc.hpp | 5 ++ apps/openmw/mwrender/actors.cpp | 21 ++++++ apps/openmw/mwrender/actors.hpp | 1 + apps/openmw/mwrender/objects.cpp | 21 ++++++ apps/openmw/mwrender/objects.hpp | 2 + apps/openmw/mwrender/renderingmanager.cpp | 19 ++++- apps/openmw/mwworld/class.hpp | 5 ++ apps/openmw/mwworld/worldimp.cpp | 85 +++++++++++++++-------- 9 files changed, 135 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index f7a5e5874..4de877b31 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -59,6 +59,11 @@ namespace MWClass static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool + isActor() const { + return true; + } }; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index e494fbaa7..edb6ca40f 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -93,6 +93,11 @@ namespace MWClass static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual bool + isActor() const { + return true; + } }; } diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index a64397d49..a73244305 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -132,3 +132,24 @@ void Actors::update (float duration){ for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) iter->second->runAnimation(duration); } + +void +Actors::updateObjectCell(const MWWorld::Ptr &ptr) +{ + Ogre::SceneNode *node; + MWWorld::CellStore *newCell = ptr.getCell(); + + if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { + node = mMwRoot->createChildSceneNode(); + mCellSceneNodes[newCell] = node; + } else { + node = mCellSceneNodes[newCell]; + } + node->addChild(ptr.getRefData().getBaseNode()); + if (Animation *anim = mAllActors[ptr]) { + /// \note Update key (Ptr's are compared only with refdata so mCell + /// on key is outdated), maybe redundant + mAllActors.erase(ptr); + mAllActors[ptr] = anim; + } +} diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 4b0b2e572..336465b16 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -45,6 +45,7 @@ namespace MWRender{ void update (float duration); + void updateObjectCell(const MWWorld::Ptr &ptr); }; } #endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index ecd1328c4..a76ef09d1 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -11,6 +11,7 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" #include "renderconst.hpp" @@ -474,3 +475,23 @@ void Objects::rebuildStaticGeometry() it->second->build(); } } + +void +Objects::updateObjectCell(const MWWorld::Ptr &ptr) +{ + Ogre::SceneNode *node; + MWWorld::CellStore *newCell = ptr.getCell(); + + if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { + node = mMwRoot->createChildSceneNode(); + mCellSceneNodes[newCell] = node; + } else { + node = mCellSceneNodes[newCell]; + } + node->addChild(ptr.getRefData().getBaseNode()); + + /// \note Still unaware how to move aabb and static w/o full rebuild, + /// moving static objects may cause problems + insertMesh(ptr, MWWorld::Class::get(ptr).getModel(ptr)); +} + diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 443f25ecf..630e5932c 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -95,6 +95,8 @@ public: void setMwRoot(Ogre::SceneNode* root); void rebuildStaticGeometry(); + + void updateObjectCell(const MWWorld::Ptr &ptr); }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ae0e57219..cec5569c8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -256,8 +256,25 @@ void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3 void RenderingManager::rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation){ } -void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store){ +void +RenderingManager::moveObjectToCell( + const MWWorld::Ptr& ptr, + const Ogre::Vector3& pos, + MWWorld::CellStore *store) +{ + Ogre::SceneNode *child = + mRendering.getScene()->getSceneNode(ptr.getRefData().getHandle()); + + Ogre::SceneNode *parent = child->getParentSceneNode(); + parent->removeChild(child); + + if (MWWorld::Class::get(ptr).isActor()) { + mActors.updateObjectCell(ptr); + } else { + mObjects.updateObjectCell(ptr); + } + child->setPosition(pos); } void RenderingManager::update (float duration){ diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 1bc592798..6ab92319d 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -220,6 +220,11 @@ namespace MWWorld virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; + + virtual bool + isActor() const { + return false; + } }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 67d8a7cec..abed25079 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -545,47 +545,76 @@ namespace MWWorld bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) { - bool ret = false; - ptr.getRefData().getPosition().pos[0] = x; - ptr.getRefData().getPosition().pos[1] = y; - ptr.getRefData().getPosition().pos[2] = z; - if (ptr==mPlayer->getPlayer()) - { - //std::cout << "X:" << ptr.getRefData().getPosition().pos[0] << " Z: " << ptr.getRefData().getPosition().pos[1] << "\n"; + bool cellChanged = false, haveToMove = false; - Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); - if (currentCell) - { - if (!(currentCell->cell->data.flags & ESM::Cell::Interior)) + ESM::Position &pos = ptr.getRefData().getPosition(); + pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; + Ogre::Vector3 vec(x, y, z); + + CellStore *currCell; + // a bit ugly + if (ptr == mPlayer->getPlayer()) { + currCell = mWorldScene->getCurrentCell(); + } else { + currCell = ptr.getCell(); + } + + if (currCell) { + if (!(currCell->cell->data.flags & ESM::Cell::Interior)) { + // exterior -> adjust loaded cells + int cellX = 0, cellY = 0; + positionToIndex (x, y, cellX, cellY); + + if (currCell->cell->data.gridX != cellX || + currCell->cell->data.gridY != cellY) { - // exterior -> adjust loaded cells - int cellX = 0; - int cellY = 0; + if (ptr == mPlayer->getPlayer()) { + mWorldScene->changeCell(cellX, cellY, pos, false); + haveToMove = true; + } else { + CellStore *newCell = + MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); - positionToIndex (x, y, cellX, cellY); + if (!mWorldScene->isCellActive(*currCell)) { + placeObject(ptr, *newCell, pos); + } else if (!mWorldScene->isCellActive(*newCell)) { + MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); + mWorldScene->removeObjectFromScene(ptr); + mLocalScripts.remove(ptr); + } else { + MWWorld::Ptr copy = + MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); - if (currentCell->cell->data.gridX!=cellX || currentCell->cell->data.gridY!=cellY) - { - mWorldScene->changeCell (cellX, cellY, mPlayer->getPlayer().getRefData().getPosition(), false); - ret = true; + mRendering->moveObjectToCell(copy, vec, currCell); + + /// \note Maybe mechanics actors change is redundant + /// because of Ptr comparing operators + if (MWWorld::Class::get(ptr).isActor()) { + MWMechanics::MechanicsManager *mechMgr = + MWBase::Environment::get().getMechanicsManager(); + + mechMgr->removeActor(ptr); + mechMgr->addActor(copy); + + haveToMove = true; + } + } + ptr.getRefData().setCount(0); } - + cellChanged = true; } } } - - /// \todo cell change for non-player ref - - mRendering->moveObject (ptr, Ogre::Vector3 (x, y, z)); - - return ret; + if (haveToMove) { + mRendering->moveObject(ptr, vec); + mPhysics->moveObject(ptr.getRefData().getHandle(), vec); + } + return cellChanged; } void World::moveObject (const Ptr& ptr, float x, float y, float z) { moveObjectImp(ptr, x, y, z); - - mPhysics->moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z)); } void World::scaleObject (const Ptr& ptr, float scale) From 37990b5133c699e461ef9fb5d354e14a13149039 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 30 Jul 2012 23:54:26 +0400 Subject: [PATCH 311/688] fix player update --- 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 abed25079..79b246ad2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -555,6 +555,7 @@ namespace MWWorld // a bit ugly if (ptr == mPlayer->getPlayer()) { currCell = mWorldScene->getCurrentCell(); + haveToMove = true; } else { currCell = ptr.getCell(); } @@ -570,7 +571,6 @@ namespace MWWorld { if (ptr == mPlayer->getPlayer()) { mWorldScene->changeCell(cellX, cellY, pos, false); - haveToMove = true; } else { CellStore *newCell = MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); From b05dfeae70302bd4b44d30a16c8beecd81aebc26 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Tue, 31 Jul 2012 01:00:53 +0400 Subject: [PATCH 312/688] bug #348: cleanup moved files to appropriate component, moved function to namespace, added docs --- apps/launcher/graphicspage.cpp | 6 +-- components/CMakeLists.txt | 6 +-- .../{ogreplugin => files}/ogreplugin.cpp | 6 ++- components/files/ogreplugin.hpp | 50 +++++++++++++++++++ components/ogreplugin/ogreplugin.h | 12 ----- libs/openengine/ogre/renderer.cpp | 8 +-- 6 files changed, 63 insertions(+), 25 deletions(-) rename components/{ogreplugin => files}/ogreplugin.cpp (95%) create mode 100644 components/files/ogreplugin.hpp delete mode 100644 components/ogreplugin/ogreplugin.h diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 7d25f2df1..c3c39cffc 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -6,8 +6,8 @@ #include #include +#include #include -#include #include "graphicspage.hpp" #include "naturalsort.hpp" @@ -120,8 +120,8 @@ bool GraphicsPage::setupOgre() pluginDir = absPluginPath.string(); - loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); - loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); + Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre); #ifdef ENABLE_PLUGIN_GL mGLPlugin = new Ogre::GLPlugin(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e16a860c4..c0585d5ee 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -52,7 +52,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary + filelibrary ogreplugin ) add_component_dir (compiler @@ -66,10 +66,6 @@ add_component_dir (interpreter miscopcodes opcodes runtime scriptopcodes spatialopcodes types ) -add_component_dir (ogreplugin - ogreplugin -) - include_directories(${BULLET_INCLUDE_DIRS}) add_library(components STATIC ${COMPONENT_FILES}) diff --git a/components/ogreplugin/ogreplugin.cpp b/components/files/ogreplugin.cpp similarity index 95% rename from components/ogreplugin/ogreplugin.cpp rename to components/files/ogreplugin.cpp index 329cd1ea9..d0e80f68d 100644 --- a/components/ogreplugin/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -1,10 +1,12 @@ -#include "ogreplugin.h" +#include "ogreplugin.hpp" #include #include #include +namespace Files { + bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE @@ -32,4 +34,6 @@ bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre:: else { return false; } +} + } \ No newline at end of file diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp new file mode 100644 index 000000000..a6d2a35c9 --- /dev/null +++ b/components/files/ogreplugin.hpp @@ -0,0 +1,50 @@ +/** + * Open Morrowind - an opensource Elder Scrolls III: Morrowind + * engine implementation. + * + * Copyright (C) 2011 Open Morrowind Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** \file components/files/ogreplugin.hpp */ + +#ifndef COMPONENTS_FILES_OGREPLUGIN_H +#define COMPONENTS_FILES_OGREPLUGIN_H + +#include + +namespace Ogre { + class Root; +} + +/** + * \namespace Files + */ +namespace Files { + +/** + * \brief Loads Ogre plugin with given name. + * + * \param pluginDir absolute path to plugins + * \param pluginName plugin name, for example "RenderSystem_GL" + * \param ogreRoot Ogre::Root instance + * + * \return whether plugin was located or not + */ +bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot); + +} + +#endif /* COMPONENTS_FILES_OGREPLUGIN_H */ \ No newline at end of file diff --git a/components/ogreplugin/ogreplugin.h b/components/ogreplugin/ogreplugin.h deleted file mode 100644 index d9b00e576..000000000 --- a/components/ogreplugin/ogreplugin.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef OGREPLUGIN_H -#define OGREPLUGIN_H - -#include - -namespace Ogre { - class Root; -} - -extern bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot); - -#endif \ No newline at end of file diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index f8fed3899..7fff2a8b4 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -11,7 +11,7 @@ #include -#include +#include #include #include @@ -117,9 +117,9 @@ void OgreRenderer::configure(const std::string &logPath, pluginDir = absPluginPath.string(); - loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); - loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); - loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); + Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) From 03cccee0e4eb7f57a039559308cfe8f25ce5b0f1 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Tue, 31 Jul 2012 10:51:34 +0400 Subject: [PATCH 313/688] bug #348: workaround for boost older than 1.44 --- components/files/ogreplugin.cpp | 2 -- components/files/ogreplugin.hpp | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index d0e80f68d..c434114b3 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -3,8 +3,6 @@ #include #include -#include - namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index a6d2a35c9..d8469aa4b 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -25,10 +25,25 @@ #include +#include +#include + namespace Ogre { class Root; } +#if (BOOST_VERSION <= 104300) +#error BOOST_VERSION +namespace boost { +namespace filesystem { +path absolute(const path& p, const path& base=current_path()) { + // call obsolete version of this function on older boost + return complete(p, base); +} +} +} +#endif /* (BOOST_VERSION <= 104300) */ + /** * \namespace Files */ From 288b63350c874e05b9f4a6058d2532457e2f7207 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 31 Jul 2012 11:46:57 +0400 Subject: [PATCH 314/688] fixed objects update, local script handling --- apps/openmw/mwworld/worldimp.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 79b246ad2..f3fb8b47f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -545,7 +545,7 @@ namespace MWWorld bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) { - bool cellChanged = false, haveToMove = false; + bool cellChanged = false, haveToMove = true; ESM::Position &pos = ptr.getRefData().getPosition(); pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; @@ -555,7 +555,6 @@ namespace MWWorld // a bit ugly if (ptr == mPlayer->getPlayer()) { currCell = mWorldScene->getCurrentCell(); - haveToMove = true; } else { currCell = ptr.getCell(); } @@ -577,10 +576,13 @@ namespace MWWorld if (!mWorldScene->isCellActive(*currCell)) { placeObject(ptr, *newCell, pos); + haveToMove = false; } else if (!mWorldScene->isCellActive(*newCell)) { MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); + + haveToMove = false; } else { MWWorld::Ptr copy = MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); @@ -595,8 +597,13 @@ namespace MWWorld mechMgr->removeActor(ptr); mechMgr->addActor(copy); - - haveToMove = true; + } else { + std::string script = + MWWorld::Class::get(ptr).getScript(ptr); + if (!script.empty()) { + mLocalScripts.remove(ptr); + mLocalScripts.add(script, copy); + } } } ptr.getRefData().setCount(0); From 0e3f70413edebd2525660c171e215763422e8fd7 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 31 Jul 2012 16:52:21 +0400 Subject: [PATCH 315/688] a very little comments --- apps/openmw/mwrender/actors.hpp | 3 ++- apps/openmw/mwrender/objects.hpp | 1 + apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 6 +++--- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 336465b16..073c5d51f 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -15,7 +15,7 @@ namespace MWRender{ OEngine::Render::OgreRenderer &mRend; std::map mCellSceneNodes; Ogre::SceneNode* mMwRoot; - std::map mAllActors; + std::map mAllActors; @@ -45,6 +45,7 @@ namespace MWRender{ void update (float duration); + /// Updates containing cell for object rendering data void updateObjectCell(const MWWorld::Ptr &ptr); }; } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 630e5932c..8594e4fe4 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -96,6 +96,7 @@ public: void rebuildStaticGeometry(); + /// Updates containing cell for object rendering data void updateObjectCell(const MWWorld::Ptr &ptr); }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d6a372d10..e618908c1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -95,6 +95,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setWaterHeight(const float height); void toggleWater(); + /// Moves object rendering part to proper container /// \param store Cell the object was in previously (\a ptr has already been updated to the new cell). void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f3fb8b47f..215fbb30e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -552,7 +552,7 @@ namespace MWWorld Ogre::Vector3 vec(x, y, z); CellStore *currCell; - // a bit ugly + /// \todo fix assertion fail on player ptr.getCell() on start if (ptr == mPlayer->getPlayer()) { currCell = mWorldScene->getCurrentCell(); } else { @@ -574,6 +574,8 @@ namespace MWWorld CellStore *newCell = MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); + // placeObject() handles both target cell states + // with active current cell if (!mWorldScene->isCellActive(*currCell)) { placeObject(ptr, *newCell, pos); haveToMove = false; @@ -589,8 +591,6 @@ namespace MWWorld mRendering->moveObjectToCell(copy, vec, currCell); - /// \note Maybe mechanics actors change is redundant - /// because of Ptr comparing operators if (MWWorld::Class::get(ptr).isActor()) { MWMechanics::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); From 32d2326b4dddc0420cc5df23a89de17cf76677fa Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Tue, 31 Jul 2012 18:15:09 +0400 Subject: [PATCH 316/688] Update components/files/ogreplugin.hpp removed #error directive --- components/files/ogreplugin.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index d8469aa4b..7a0b5841e 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -33,7 +33,6 @@ namespace Ogre { } #if (BOOST_VERSION <= 104300) -#error BOOST_VERSION namespace boost { namespace filesystem { path absolute(const path& p, const path& base=current_path()) { From f2a2e5f57d566a7a6c97f91bc63968dae2a67dcb Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 31 Jul 2012 19:30:24 +0400 Subject: [PATCH 317/688] remove MWWorld::Player::setPos() --- apps/openmw/mwworld/player.cpp | 6 ------ apps/openmw/mwworld/player.hpp | 3 --- apps/openmw/mwworld/scene.cpp | 26 +++++++++++++++++--------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 4d508c3e9..54e5a625f 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -38,12 +38,6 @@ namespace MWWorld delete mClass; } - void Player::setPos(float x, float y, float z) - { - /// \todo This fcuntion should be removed during the mwrender-refactoring. - MWBase::Environment::get().getWorld()->moveObject (getPlayer(), x, y, z); - } - void Player::setRot(float x, float y, float z) { mRenderer->setRot(x, y, z); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index ee7c030a5..d2058dea6 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -42,9 +42,6 @@ namespace MWWorld ~Player(); - /// Set the player position. Uses Morrowind coordinates. - void setPos(float x, float y, float z); - /// Set where the player is looking at. Uses Morrowind (euler) angles void setRot(float x, float y, float z); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 13e5ecb85..c768fce26 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -135,23 +135,31 @@ namespace MWWorld } - void Scene::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position, + void + Scene::playerCellChange( + MWWorld::CellStore *cell, + const ESM::Position& pos, bool adjustPlayerPos) { bool hasWater = cell->cell->data.flags & cell->cell->HasWater; mPhysics->setCurrentWater(hasWater, cell->cell->water); - if (adjustPlayerPos) - { - MWBase::Environment::get().getWorld()->getPlayer().setPos (position.pos[0], position.pos[1], position.pos[2]); - MWBase::Environment::get().getWorld()->getPlayer().setRot (position.rot[0], position.rot[1], position.rot[2]); + + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + + if (adjustPlayerPos) { + world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); + MWBase::Environment::get().getWorld()->getPlayer().setRot (pos.rot[0], pos.rot[1], pos.rot[2]); } + world->getPlayer().setCell(cell); - MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); + MWMechanics::MechanicsManager *mechMgr = + MWBase::Environment::get().getMechanicsManager(); - MWBase::Environment::get().getMechanicsManager()->addActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - MWBase::Environment::get().getMechanicsManager()->watchActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mechMgr->addActor(player); + mechMgr->watchActor(player); - MWBase::Environment::get().getWindowManager()->changeCell( mCurrentCell ); + MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); } void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) From 3b776cb3ca5ef6417cb965daf60a95be96c09555 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Wed, 1 Aug 2012 00:01:32 +0400 Subject: [PATCH 318/688] fixed submodule version --- extern/shiny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny b/extern/shiny index 5546a5bd8..164bc8d3b 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 5546a5bd8474ef328dfedae2df42126cdaf9515f +Subproject commit 164bc8d3bfe860bd16ad89c0bd1b59f465c9bb24 From 15a16aeba1c2b7fd0ba3e47b14fd673a17295524 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 1 Aug 2012 04:11:16 +0200 Subject: [PATCH 319/688] Fixed DirectX HLSL shaders and re-enabled them in the gui --- apps/openmw/mwgui/settingswindow.cpp | 12 ++++------ apps/openmw/mwrender/renderingmanager.cpp | 16 ++++++------- files/materials/core.h | 29 +++++++++++++---------- files/materials/objects.shaderset | 4 ++-- files/materials/terrain.shaderset | 4 ++-- files/materials/underwater.h | 10 ++++---- files/materials/water.shader | 11 +++++---- files/materials/water.shaderset | 4 ++-- 8 files changed, 46 insertions(+), 44 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index d8b0c96d7..4488096fe 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -75,9 +75,9 @@ namespace return boost::lexical_cast(xaspect) + " : " + boost::lexical_cast(yaspect); } - bool hasGLSL () + std::string hlslGlsl () { - return (Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") != std::string::npos); + return (Ogre::Root::getSingleton ().getRenderSystem ()->getName ().find("OpenGL") != std::string::npos) ? "glsl" : "hlsl"; } } @@ -393,12 +393,9 @@ namespace MWGui std::string val = static_cast(_sender)->getCaption(); if (val == "off") { - if (hasGLSL ()) - val = "glsl"; - else - val = "cg"; + val = hlslGlsl(); } - else if (val == "glsl") + else if (val == hlslGlsl()) val = "cg"; else val = "off"; @@ -411,6 +408,7 @@ namespace MWGui // water shader not supported with object shaders off mWaterShaderButton->setCaptionWithReplacing("#{sOff}"); + mUnderwaterButton->setCaptionWithReplacing("#{sOff}"); mWaterShaderButton->setEnabled(false); mReflectObjectsButton->setEnabled(false); mReflectActorsButton->setEnabled(false); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ae0e57219..a4b2e8489 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -43,12 +43,14 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0) { // select best shader mode - if (Settings::Manager::getString("shader mode", "General") == "") + bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); + + // glsl is only supported in opengl mode and hlsl only in direct3d mode. + if (Settings::Manager::getString("shader mode", "General") == "" + || (openGL && Settings::Manager::getString("shader mode", "General") == "hlsl") + || (!openGL && Settings::Manager::getString("shader mode", "General") == "glsl")) { - if (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") == std::string::npos) - Settings::Manager::setString("shader mode", "General", "cg"); - else - Settings::Manager::setString("shader mode", "General", "glsl"); + Settings::Manager::setString("shader mode", "General", openGL ? "glsl" : "hlsl"); } mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5); @@ -74,10 +76,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mFactory->setCurrentLanguage (lang); mFactory->loadAllFiles(); - //The fog type must be set before any terrain objects are created as if the - //fog type is set to FOG_NONE then the initially created terrain won't have any fog - configureFog(1, ColourValue(1,1,1)); - // Set default mipmap level (NB some APIs ignore this) TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); diff --git a/files/materials/core.h b/files/materials/core.h index 6e27d349c..cba716777 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -1,20 +1,20 @@ -#if SH_HLSL == 1 - #error "HLSL is unsupported" -#endif - -#if SH_CG == 1 +#if SH_HLSL == 1 || SH_CG == 1 #define shTexture2D sampler2D #define shSample(tex, coord) tex2D(tex, coord) + #define shCubicSample(tex, coord) texCUBE(tex, coord) #define shLerp(a, b, t) lerp(a, b, t) #define shSaturate(a) saturate(a) #define shSampler2D(name) , uniform sampler2D name : register(s@shCounter(0)) @shUseSampler(name) + + #define shSamplerCube(name) , uniform samplerCUBE name : register(s@shCounter(0)) @shUseSampler(name) #define shMatrixMult(m, v) mul(m, v) #define shUniform(type, name) , uniform type name + #define shTangentInput(type) , in type tangent : TANGENT #define shVertexInput(type, name) , in type name : TEXCOORD@shCounter(1) #define shInput(type, name) , in type name : TEXCOORD@shCounter(1) #define shOutput(type, name) , out type name : TEXCOORD@shCounter(2) @@ -65,9 +65,10 @@ #define float4 vec4 #define int2 ivec2 #define int3 ivec3 - #define int4 ivec4/ + #define int4 ivec4 #define shTexture2D sampler2D #define shSample(tex, coord) texture2D(tex, coord) + #define shCubicSample(tex, coord) textureCube(tex, coord) #define shLerp(a, b, t) mix(a, b, t) #define shSaturate(a) clamp(a, 0.0, 1.0) @@ -75,11 +76,14 @@ #define shSampler2D(name) uniform sampler2D name; @shUseSampler(name) + #define shSamplerCube(name) uniform samplerCube name; @shUseSampler(name) + #define shMatrixMult(m, v) (m * v) #define shOutputPosition gl_Position #define float4x4 mat4 + #define float3x3 mat3 // GLSL 1.3 #if 0 @@ -89,8 +93,8 @@ #define shOutputColour(num) oColor##num - - + #define shTangentInput(type) in type tangent; + #define shVertexInput(type, name) in type name; #define shInput(type, name) in type name; #define shOutput(type, name) out type name; @@ -101,7 +105,7 @@ #ifdef SH_VERTEX_SHADER #define SH_BEGIN_PROGRAM \ - in float4 shInputPosition; + in float4 vertex; #define SH_START_PROGRAM \ void main(void) @@ -126,10 +130,11 @@ #if 1 // automatically recognized by ogre when the input name equals this - #define shInputPosition gl_Vertex + #define shInputPosition vertex #define shOutputColour(num) gl_FragData[num] + #define shTangentInput(type) attribute type tangent; #define shVertexInput(type, name) attribute type name; #define shInput(type, name) varying type name; #define shOutput(type, name) varying type name; @@ -140,8 +145,8 @@ #ifdef SH_VERTEX_SHADER - #define SH_BEGIN_PROGRAM - + #define SH_BEGIN_PROGRAM \ + attribute vec4 vertex; #define SH_START_PROGRAM \ void main(void) diff --git a/files/materials/objects.shaderset b/files/materials/objects.shaderset index ccb975fe9..028c15ce8 100644 --- a/files/materials/objects.shaderset +++ b/files/materials/objects.shaderset @@ -3,7 +3,7 @@ shader_set openmw_objects_vertex source objects.shader type vertex profiles_cg vs_2_0 vp40 arbvp1 - profiles_hlsl vs_2_0 + profiles_hlsl vs_3_0 vs_2_0 } shader_set openmw_objects_fragment @@ -11,5 +11,5 @@ shader_set openmw_objects_fragment source objects.shader type fragment profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 - profiles_hlsl ps_2_0 + profiles_hlsl ps_3_0 ps_2_0 } diff --git a/files/materials/terrain.shaderset b/files/materials/terrain.shaderset index be8ecd7d8..a72f2358f 100644 --- a/files/materials/terrain.shaderset +++ b/files/materials/terrain.shaderset @@ -3,7 +3,7 @@ shader_set terrain_vertex source terrain.shader type vertex profiles_cg vs_2_0 vp40 arbvp1 - profiles_hlsl vs_2_0 + profiles_hlsl vs_3_0 vs_2_0 } shader_set terrain_fragment @@ -11,5 +11,5 @@ shader_set terrain_fragment source terrain.shader type fragment profiles_cg ps_3_0 ps_2_x ps_2_0 fp40 arbfp1 - profiles_hlsl ps_2_0 + profiles_hlsl ps_3_0 ps_2_0 } diff --git a/files/materials/underwater.h b/files/materials/underwater.h index fe19ff93b..18052a98d 100644 --- a/files/materials/underwater.h +++ b/files/materials/underwater.h @@ -28,7 +28,7 @@ float3 intercept(float3 lineP, float3 perturb1(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) { - float2 nCoord = float2(0.0); + float2 nCoord = float2(0,0); bend *= WAVE_CHOPPYNESS; nCoord = coords * (WAVE_SCALE * 0.05) + windDir * timer * (windSpeed*0.04); float3 normal0 = 2.0 * shSample(tex, nCoord + float2(-timer*0.015,-timer*0.05)).rgb - 1.0; @@ -55,8 +55,8 @@ float3 perturb1(shTexture2D tex, float2 coords, float bend, float2 windDir, floa float3 perturb(shTexture2D tex, float2 coords, float bend, float2 windDir, float windSpeed, float timer) { bend *= WAVE_CHOPPYNESS; - float3 col = float3(0.0); - float2 nCoord = float2(0.0); //normal coords + float3 col = float3(0,0,0); + float2 nCoord = float2(0,0); //normal coords nCoord = coords * (WAVE_SCALE * 0.025) + windDir * timer * (windSpeed*0.03); col += shSample(tex,nCoord + float2(-timer*0.005,-timer*0.01)).rgb*0.20; @@ -102,11 +102,11 @@ float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 waterEyePos, /// \todo sunFade // float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*sunFade*causticdepth; - float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth; + float3 caustics = clamp(pow(float3(causticR,causticR,causticR)*5.5,float3(5.5*causticdepth,5.5*causticdepth,5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth; float causticG = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; float causticB = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; //caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*sunFade*causticdepth; - caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*causticdepth; + caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth,5.5*causticdepth,5.5*causticdepth)))*NdotL*causticdepth; caustics *= 3; diff --git a/files/materials/water.shader b/files/materials/water.shader index 2145919b0..08a19ace9 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -198,7 +198,7 @@ float depth = shSample(depthMap, screenCoords).x * far - depthPassthrough; float shoreFade = shSaturate(depth / 50.0); - float2 nCoord = float2(0.0); + float2 nCoord = float2(0,0); nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04); float3 normal0 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0; @@ -238,12 +238,12 @@ // sunlight scattering float3 pNormal = float3(0,1,0); - vec3 lR = reflect(lVec, lNormal); - vec3 llR = reflect(lVec, pNormal); + float3 lR = reflect(lVec, lNormal); + float3 llR = reflect(lVec, pNormal); float s = shSaturate(dot(lR, vVec)*2.0-1.2); float lightScatter = shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); - float3 scatterColour = shLerp(vec3(SCATTER_COLOUR)*vec3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); // fresnel float ior = (cameraPos.y>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air @@ -284,7 +284,7 @@ waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; float3 waterext = float3(0.6, 0.9, 1.0);//water extinction - watercolour = mix(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); float darkness = VISIBILITY*2.0; darkness = clamp((cameraPos.y+darkness)/darkness,0.2,1.0); @@ -299,6 +299,7 @@ shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); } + shOutputColour(0).w = 1; } #endif diff --git a/files/materials/water.shaderset b/files/materials/water.shaderset index 754ac8e49..5e070a45a 100644 --- a/files/materials/water.shaderset +++ b/files/materials/water.shaderset @@ -3,7 +3,7 @@ shader_set water_vertex source water.shader type vertex profiles_cg vs_2_0 vp40 arbvp1 - profiles_hlsl vs_2_0 + profiles_hlsl vs_3_0 vs_2_0 } shader_set water_fragment @@ -11,5 +11,5 @@ shader_set water_fragment source water.shader type fragment profiles_cg ps_2_x ps_2_0 ps fp40 arbfp1 - profiles_hlsl ps_2_0 + profiles_hlsl ps_3_0 ps_2_0 } From f8e54b401bc0b65a8bda04f0e332775c1f9a81f9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 1 Aug 2012 09:09:00 +0200 Subject: [PATCH 320/688] fixed linkage problem --- components/files/ogreplugin.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index 7a0b5841e..c5292b3a2 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -35,7 +35,7 @@ namespace Ogre { #if (BOOST_VERSION <= 104300) namespace boost { namespace filesystem { -path absolute(const path& p, const path& base=current_path()) { +inline path absolute(const path& p, const path& base=current_path()) { // call obsolete version of this function on older boost return complete(p, base); } @@ -50,7 +50,7 @@ namespace Files { /** * \brief Loads Ogre plugin with given name. - * + * * \param pluginDir absolute path to plugins * \param pluginName plugin name, for example "RenderSystem_GL" * \param ogreRoot Ogre::Root instance @@ -61,4 +61,4 @@ bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre:: } -#endif /* COMPONENTS_FILES_OGREPLUGIN_H */ \ No newline at end of file +#endif /* COMPONENTS_FILES_OGREPLUGIN_H */ From ebd8b064f64fa7d1f5c0e346135c0c0d5c06173a Mon Sep 17 00:00:00 2001 From: gugus Date: Wed, 1 Aug 2012 12:21:42 +0200 Subject: [PATCH 321/688] getStartingAngle script instruction --- apps/openmw/mwscript/docs/vmformat.txt | 8 ++++- .../mwscript/transformationextensions.cpp | 34 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b979420dd..2f08353db 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -169,5 +169,11 @@ op 0x2000164: SetScale op 0x2000165: SetScale, explicit reference op 0x2000166: SetAngle op 0x2000167: SetAngle, explicit reference -opcodes 0x2000168-0x3ffffff unused +op 0x2000168: GetScale +op 0x2000169: GetScale, explicit reference +op 0x200016a: GetAngle +op 0x200016b: GetAngle, explicit reference +op 0x200016c: GetStartingAngle +op 0x200016d: GetStartingAngle, explicit reference +opcodes 0x200016e-0x3ffffff unused diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 2ea80c0d8..1c6940710 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -82,7 +82,7 @@ namespace MWScript }; template - class OpGetAngle : public Interpreter::Opcode0 + class OpGetStartingAngle : public Interpreter::Opcode0 { public: @@ -108,6 +108,33 @@ namespace MWScript } }; + template + class OpGetAngle : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + if(axis == "X") + { + runtime.push(Ogre::Radian(ptr.getCellRef().pos.rot[0]).valueDegrees()); + } + if(axis == "Y") + { + runtime.push(Ogre::Radian(ptr.getCellRef().pos.rot[1]).valueDegrees()); + } + if(axis == "Z") + { + runtime.push(Ogre::Radian(ptr.getCellRef().pos.rot[2]).valueDegrees()); + } + } + }; + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -116,6 +143,8 @@ namespace MWScript const int opcodeGetScaleExplicit = 0x2000169; const int opcodeGetAngle = 0x200016a; const int opcodeGetAngleExplicit = 0x200016b; + const int opcodeGetStartingAngle = 0x200016c; + const int opcodeGetStartingAngleExplicit = 0x200016d; void registerExtensions (Compiler::Extensions& extensions) { @@ -123,6 +152,7 @@ namespace MWScript extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); extensions.registerInstruction("setangle","Sf",opcodeSetAngle,opcodeSetAngleExplicit); extensions.registerFunction("getangle",'f',"S",opcodeGetAngle,opcodeGetAngleExplicit); + extensions.registerFunction("getstartingangle",'f',"S",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -135,6 +165,8 @@ namespace MWScript interpreter.installSegment5(opcodeGetScaleExplicit,new OpGetScale); interpreter.installSegment5(opcodeGetAngle,new OpGetAngle); interpreter.installSegment5(opcodeGetAngleExplicit,new OpGetAngle); + interpreter.installSegment5(opcodeGetStartingAngle,new OpGetStartingAngle); + interpreter.installSegment5(opcodeGetStartingAngleExplicit,new OpGetStartingAngle); } } } From 18fd14ff75441a8f2464dfedf1676bb14dd69efb Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Mon, 30 Jul 2012 14:18:02 -0400 Subject: [PATCH 322/688] Workaround for FindMyGUI.cmake path space bug on Windows. See bug 353 http://bugs.openmw.org/issues/353 --- cmake/FindMyGUI.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmake/FindMyGUI.cmake b/cmake/FindMyGUI.cmake index 7278fe200..e2fefac7d 100644 --- a/cmake/FindMyGUI.cmake +++ b/cmake/FindMyGUI.cmake @@ -128,9 +128,11 @@ ELSE (WIN32) #Unix ENDIF (WIN32) #Do some preparation -SEPARATE_ARGUMENTS(MYGUI_INCLUDE_DIRS) -SEPARATE_ARGUMENTS(MYGUI_LIBRARIES) -SEPARATE_ARGUMENTS(MYGUI_PLATFORM_LIBRARIES) +IF (NOT WIN32) # This does not work on Windows for paths with spaces in them + SEPARATE_ARGUMENTS(MYGUI_INCLUDE_DIRS) + SEPARATE_ARGUMENTS(MYGUI_LIBRARIES) + SEPARATE_ARGUMENTS(MYGUI_PLATFORM_LIBRARIES) +ENDIF (NOT WIN32) SET(MYGUI_LIBRARIES ${MYGUI_LIBRARIES} ${FREETYPE_LIBRARIES}) From e9aac4512b8563d5b5e65e9c05ecfd2502a8207f Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 30 Jul 2012 19:45:40 +0200 Subject: [PATCH 323/688] Build fix for Windows Fixes a number of places where uint was used and not defined. This commit was originally authored by gus. It rebased by Michael Mc Donnell to take recent commit fixes into account. --- apps/openmw/mwrender/terrainmaterial.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 1a2d96a14..9ef4652f6 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -86,15 +86,15 @@ namespace MWRender normalMap->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getTerrainNormalMap ()->getName()))); normalMap->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - uint maxLayers = getMaxLayers(terrain); - uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); - uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + Ogre::uint maxLayers = getMaxLayers(terrain); + Ogre::uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); + Ogre::uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); p->mShaderProperties.setProperty ("num_layers", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numLayers)))); p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty(new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); // blend maps - for (uint i = 0; i < numBlendTextures; ++i) + for (Ogre::uint i = 0; i < numBlendTextures; ++i) { sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(i)))); @@ -102,7 +102,7 @@ namespace MWRender } // layer maps - for (uint i = 0; i < numLayers; ++i) + for (Ogre::uint i = 0; i < numLayers; ++i) { sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(i, 0)))); @@ -111,7 +111,7 @@ namespace MWRender } // shadow - for (uint i = 0; i < 3; ++i) + for (Ogre::uint i = 0; i < 3; ++i) { sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); From 7f802a22b5a68437709b473310c81d6055ff9784 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Wed, 1 Aug 2012 17:50:42 -0400 Subject: [PATCH 324/688] Compare with stream position instead of int. Fixes compilation of Debug build on Windows. --- components/bsa/bsa_file.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 053191c9b..1700e1aab 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -200,7 +200,7 @@ void BSAFile::readHeader() input.read(&stringBuf[0], stringBuf.size()); // Check our position - assert(input.tellg() == static_cast (12+dirsize)); + assert(input.tellg() == std::streampos(12+dirsize)); // Calculate the offset of the data buffer. All file offsets are // relative to this. 12 header bytes + directory + hash table From ff627706573cc0c624cadd79c692316de80e5686 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 3 Aug 2012 14:42:09 +0400 Subject: [PATCH 325/688] World::isUnderwater(), World::isSwimming() --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwrender/renderingmanager.cpp | 20 +++++++++-------- apps/openmw/mwrender/renderingmanager.hpp | 1 - apps/openmw/mwrender/water.cpp | 20 ++++++----------- apps/openmw/mwrender/water.hpp | 3 ++- apps/openmw/mwworld/worldimp.cpp | 26 +++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 +++ 7 files changed, 52 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f257b723e..d9e19bead 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -249,6 +249,9 @@ namespace MWBase ///< @return true if it is possible to place on object at specified cursor location virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; + + virtual bool isSwimming(const MWWorld::Ptr &object) = 0; + virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ae0e57219..cbf799d7a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -275,11 +275,20 @@ void RenderingManager::update (float duration){ mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealOrientation() ); - checkUnderwater(); + if (mWater) { + Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); - if (mWater) + MWBase::World *world = MWBase::Environment::get().getWorld(); + + mWater->updateUnderwater( + world->isUnderwater( + *world->getPlayer().getPlayer().getCell()->cell, + Ogre::Vector3(cam.x, -cam.z, cam.y)) + ); mWater->update(duration); + } } + void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ if(store->cell->data.flags & store->cell->HasWater || ((!(store->cell->data.flags & ESM::Cell::Interior)) @@ -459,13 +468,6 @@ void RenderingManager::toggleLight() setAmbientMode(); } -void RenderingManager::checkUnderwater() -{ - if(mWater) - { - mWater->checkUnderwater( mRendering.getCamera()->getRealPosition().y ); - } -} void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d6a372d10..12cbee9b8 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -91,7 +91,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation); - void checkUnderwater(); void setWaterHeight(const float height); void toggleWater(); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 92fc97b3b..d5b93b7cb 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -184,22 +184,16 @@ void Water::toggle() updateVisible(); } -void Water::checkUnderwater(float y) +void +Water::updateUnderwater(bool underwater) { - if (!mActive) - { + if (!mActive) { return; } - - if ((mIsUnderwater && y > mTop) || !mWater->isVisible() || mCamera->getPolygonMode() != Ogre::PM_SOLID) - { - mIsUnderwater = false; - } - - if (!mIsUnderwater && y < mTop && mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID) - { - mIsUnderwater = true; - } + mIsUnderwater = + underwater && + mWater->isVisible() && + mCamera->getPolygonMode() == Ogre::PM_SOLID; updateVisible(); } diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index d0a5a4352..f56ba7410 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -101,7 +101,8 @@ namespace MWRender { void processChangedSettings(const Settings::CategorySettingVector& settings); - void checkUnderwater(float y); + /// Updates underwater state accordingly + void updateUnderwater(bool underwater); void changeCell(const ESM::Cell* cell); void setHeight(const float height); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 67d8a7cec..4bdf3f948 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -18,6 +18,8 @@ #include "manualref.hpp" #include "cellfunctors.hpp" +#include + using namespace Ogre; namespace @@ -1095,4 +1097,28 @@ namespace MWWorld { mRendering->getTriangleBatchCount(triangles, batches); } + + bool + World::isSwimming(const MWWorld::Ptr &object) + { + /// \todo add check ifActor() - only actors can swim + float *fpos = object.getRefData().getPosition().pos; + Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + + /// \fixme should rely on object height + pos.z += 30; + + return isUnderwater(*object.getCell()->cell, pos); + } + + bool + World::isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) + { + if (cell.data.flags & ESM::Cell::HasWater == 0) { + return false; + } + bool res = pos.z < cell.water; + std::cout << "World::isUnderwater(" << pos.z << "):" << res << std::endl; + return res; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d39871c21..f80b88be2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -270,6 +270,9 @@ namespace MWWorld ///< @return true if it is possible to place on object at specified cursor location virtual void processChangedSettings(const Settings::CategorySettingVector& settings); + + virtual bool isSwimming(const MWWorld::Ptr &object); + virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); }; } From 7d6ad6ad9477e0644cc7f1e671f08ccfb79d9df1 Mon Sep 17 00:00:00 2001 From: gugus Date: Fri, 3 Aug 2012 18:14:05 +0200 Subject: [PATCH 326/688] setPos and getPos Script instructions. Cell-crossing is not tested yet. --- .../mwscript/transformationextensions.cpp | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 2ea80c0d8..42ef16ed9 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -108,6 +108,67 @@ namespace MWScript } }; + template + class OpGetPos : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + if(axis == "X") + { + runtime.push(ptr.getRefData().getPosition().pos[0]); + } + if(axis == "Y") + { + runtime.push(ptr.getRefData().getPosition().pos[1]); + } + if(axis == "Z") + { + runtime.push(ptr.getRefData().getPosition().pos[2]); + } + } + }; + + template + class OpSetPos : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + Interpreter::Type_Float pos = runtime[0].mFloat; + runtime.pop(); + + float ax = ptr.getRefData().getPosition().pos[0]; + float ay = ptr.getRefData().getPosition().pos[1]; + float az = ptr.getRefData().getPosition().pos[2]; + std::cout << "setPos"; + + if(axis == "X") + { + MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az); + } + if(axis == "Y") + { + MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az); + } + if(axis == "Z") + { + MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); + } + } + }; + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -116,6 +177,10 @@ namespace MWScript const int opcodeGetScaleExplicit = 0x2000169; const int opcodeGetAngle = 0x200016a; const int opcodeGetAngleExplicit = 0x200016b; + const int opcodeGetPos = 0x200016c; + const int opcodeGetPosExplicit = 0x200016d; + const int opcodeSetPos = 0x200016e; + const int opcodeSetPosExplicit = 0x200016f; void registerExtensions (Compiler::Extensions& extensions) { @@ -123,6 +188,8 @@ namespace MWScript extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); extensions.registerInstruction("setangle","Sf",opcodeSetAngle,opcodeSetAngleExplicit); extensions.registerFunction("getangle",'f',"S",opcodeGetAngle,opcodeGetAngleExplicit); + extensions.registerInstruction("setpos","Sf",opcodeSetPos,opcodeSetPosExplicit); + extensions.registerFunction("getpos",'f',"S",opcodeGetPos,opcodeGetPosExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -135,6 +202,10 @@ namespace MWScript interpreter.installSegment5(opcodeGetScaleExplicit,new OpGetScale); interpreter.installSegment5(opcodeGetAngle,new OpGetAngle); interpreter.installSegment5(opcodeGetAngleExplicit,new OpGetAngle); + interpreter.installSegment5(opcodeGetPos,new OpGetPos); + interpreter.installSegment5(opcodeGetPosExplicit,new OpGetPos); + interpreter.installSegment5(opcodeSetPos,new OpSetPos); + interpreter.installSegment5(opcodeSetPosExplicit,new OpSetPos); } } } From b2ce1dfd1ab10f1a12f42576d9611cca8a570b29 Mon Sep 17 00:00:00 2001 From: gugus Date: Fri, 3 Aug 2012 18:20:51 +0200 Subject: [PATCH 327/688] getStartingPos --- .../mwscript/transformationextensions.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 42ef16ed9..1eb53e8f3 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -169,6 +169,33 @@ namespace MWScript } }; + template + class OpGetStartingPos : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string axis = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + if(axis == "X") + { + runtime.push(ptr.getCellRef().pos.pos[0]); + } + if(axis == "Y") + { + runtime.push(ptr.getCellRef().pos.pos[1]); + } + if(axis == "Z") + { + runtime.push(ptr.getCellRef().pos.pos[2]); + } + } + }; + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -181,6 +208,8 @@ namespace MWScript const int opcodeGetPosExplicit = 0x200016d; const int opcodeSetPos = 0x200016e; const int opcodeSetPosExplicit = 0x200016f; + const int opcodeGetStartingPos = 0x2000170; + const int opcodeGetStartingPosExplicit = 0x2000171; void registerExtensions (Compiler::Extensions& extensions) { @@ -190,6 +219,7 @@ namespace MWScript extensions.registerFunction("getangle",'f',"S",opcodeGetAngle,opcodeGetAngleExplicit); extensions.registerInstruction("setpos","Sf",opcodeSetPos,opcodeSetPosExplicit); extensions.registerFunction("getpos",'f',"S",opcodeGetPos,opcodeGetPosExplicit); + extensions.registerFunction("getstartingpos",'f',"S",opcodeGetStartingPos,opcodeGetStartingPosExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -206,6 +236,8 @@ namespace MWScript interpreter.installSegment5(opcodeGetPosExplicit,new OpGetPos); interpreter.installSegment5(opcodeSetPos,new OpSetPos); interpreter.installSegment5(opcodeSetPosExplicit,new OpSetPos); + interpreter.installSegment5(opcodeGetStartingPos,new OpGetStartingPos); + interpreter.installSegment5(opcodeGetStartingPosExplicit,new OpGetStartingPos); } } } From 0f3cb5667fa85c5e36a3dc783e16e8a5e4cb32e6 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Fri, 3 Aug 2012 22:04:02 +0200 Subject: [PATCH 328/688] esm_reader.hpp: fix std::runtime_error compile error --- components/esm/esm_reader.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 13f1f4a01..c369f01f5 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include From 11d0761528d8f5ee3cbea99e6206600147b92e28 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Fri, 3 Aug 2012 22:10:51 +0200 Subject: [PATCH 329/688] terrainmaterial.cpp: fix std::runtime_error compile error --- apps/openmw/mwrender/terrainmaterial.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 9ef4652f6..5ef9fe58f 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -2,6 +2,8 @@ #include +#include + #include namespace From b0b3ebe123bb710838408e5ec09d2336805fedd3 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Fri, 3 Aug 2012 21:53:38 +0200 Subject: [PATCH 330/688] nif_file.hpp: include type definitions to fix build --- components/nif/nif_file.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 624066317..143c06993 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -39,6 +39,7 @@ #include "record.hpp" #include "nif_types.hpp" +#include "components/esm/loadland.hpp" namespace Nif { From e4dc01832bd8fc5cd0ddbd262d3c12cf8fb3d9d0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 4 Aug 2012 09:18:20 +0200 Subject: [PATCH 331/688] fixing object rotation script instructions (wrong argument type and missing error handling) --- .../mwscript/transformationextensions.cpp | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 532bc8cee..6f906343c 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -66,18 +66,20 @@ namespace MWScript float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); - if(axis == "X") + if (axis == "x") { MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); } - if(axis == "Y") + else if (axis == "y") { MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); } - if(axis == "Z") + else if (axis == "z") { MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); } + else + throw std::runtime_error ("invalid ration axis: " + axis); } }; @@ -93,18 +95,20 @@ namespace MWScript std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - if(axis == "X") + if (axis == "x") { runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); } - if(axis == "Y") + else if (axis == "y") { runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); } - if(axis == "Z") + else if (axis == "z") { runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); } + else + throw std::runtime_error ("invalid ration axis: " + axis); } }; @@ -120,18 +124,20 @@ namespace MWScript std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - if(axis == "X") + if (axis=="x") { runtime.push(Ogre::Radian(ptr.getCellRef().pos.rot[0]).valueDegrees()); } - if(axis == "Y") + else if (axis=="y") { runtime.push(Ogre::Radian(ptr.getCellRef().pos.rot[1]).valueDegrees()); } - if(axis == "Z") + else if (axis=="z") { runtime.push(Ogre::Radian(ptr.getCellRef().pos.rot[2]).valueDegrees()); } + else + throw std::runtime_error ("invalid ration axis: " + axis); } }; @@ -150,9 +156,9 @@ namespace MWScript { extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); - extensions.registerInstruction("setangle","Sf",opcodeSetAngle,opcodeSetAngleExplicit); - extensions.registerFunction("getangle",'f',"S",opcodeGetAngle,opcodeGetAngleExplicit); - extensions.registerFunction("getstartingangle",'f',"S",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); + extensions.registerInstruction("setangle","cf",opcodeSetAngle,opcodeSetAngleExplicit); + extensions.registerFunction("getangle",'f',"c",opcodeGetAngle,opcodeGetAngleExplicit); + extensions.registerFunction("getstartingangle",'f',"c",opcodeGetStartingAngle,opcodeGetStartingAngleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) From 994e33e3d27e04047f97ae3504f6838cd662abef Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 4 Aug 2012 09:34:49 +0200 Subject: [PATCH 332/688] fixed object rotation code --- apps/openmw/mwworld/worldimp.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 67d8a7cec..c1d7763ce 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -606,10 +606,10 @@ namespace MWWorld ptr.getRefData().getPosition().rot[1] = Ogre::Degree(y).valueRadians(); ptr.getRefData().getPosition().rot[2] = Ogre::Degree(z).valueRadians(); - Ogre::Quaternion rotx(Ogre::Degree(x),Ogre::Vector3::UNIT_X); - Ogre::Quaternion roty(Ogre::Degree(y),Ogre::Vector3::UNIT_Y); - Ogre::Quaternion rotz(Ogre::Degree(z),Ogre::Vector3::UNIT_Z); - ptr.getRefData().getBaseNode()->setOrientation(rotz*roty*rotx); + Ogre::Quaternion rotx(Ogre::Degree(-x),Ogre::Vector3::UNIT_X); + Ogre::Quaternion roty(Ogre::Degree(-y),Ogre::Vector3::UNIT_Y); + Ogre::Quaternion rotz(Ogre::Degree(-z),Ogre::Vector3::UNIT_Z); + ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); } @@ -1074,7 +1074,7 @@ namespace MWWorld Ogre::Vector3 orig = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); - + float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; len += 100.0; From 16ad97610d2e21ce2608913a928027e47b9befa1 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 4 Aug 2012 11:54:42 +0400 Subject: [PATCH 333/688] add support for some scripting player control switches --- apps/openmw/mwinput/inputmanager.cpp | 82 +++++++++++++++------- apps/openmw/mwinput/inputmanager.hpp | 2 + apps/openmw/mwscript/controlextensions.cpp | 6 ++ apps/openmw/mwworld/worldimp.cpp | 6 +- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 3a8315b71..6bda77ea8 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -90,6 +90,7 @@ namespace MWInput bool mDragDrop; + std::map mControlSwitch; /* InputImpl Methods */ public: @@ -339,6 +340,14 @@ private: poller.bind(A_Jump, KC_E); poller.bind(A_Crouch, KC_LCONTROL); + + mControlSwitch["playercontrols"] = true; + mControlSwitch["playerfighting"] = true; + mControlSwitch["playerjumping"] = true; + mControlSwitch["playerlooking"] = true; + mControlSwitch["playermagic"] = true; + mControlSwitch["playerviewswitch"] = true; + mControlSwitch["vanitymode"] = true; } void setDragDrop(bool dragDrop) @@ -366,33 +375,35 @@ private: // Configure player movement according to keyboard input. Actual movement will // be done in the physics system. - if (poller.isDown(A_MoveLeft)) - { - player.setAutoMove (false); - player.setLeftRight (1); - } - else if (poller.isDown(A_MoveRight)) - { - player.setAutoMove (false); - player.setLeftRight (-1); - } - else - player.setLeftRight (0); + if (mControlSwitch["playercontrols"]) { + if (poller.isDown(A_MoveLeft)) + { + player.setAutoMove (false); + player.setLeftRight (1); + } + else if (poller.isDown(A_MoveRight)) + { + player.setAutoMove (false); + player.setLeftRight (-1); + } + else + player.setLeftRight (0); - if (poller.isDown(A_MoveForward)) - { - player.setAutoMove (false); - player.setForwardBackward (1); + if (poller.isDown(A_MoveForward)) + { + player.setAutoMove (false); + player.setForwardBackward (1); + } + else if (poller.isDown(A_MoveBackward)) + { + player.setAutoMove (false); + player.setForwardBackward (-1); + } + else + player.setForwardBackward (0); } - else if (poller.isDown(A_MoveBackward)) - { - player.setAutoMove (false); - player.setForwardBackward (-1); - } - else - player.setForwardBackward (0); - if (poller.isDown(A_Jump)) + if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) player.setUpDown (1); else if (poller.isDown(A_Crouch)) player.setUpDown (-1); @@ -423,6 +434,24 @@ private: guiEvents->enabled = false; } } + + void toggleControlSwitch(std::string sw, bool value) + { + if (mControlSwitch[sw] == value) { + return; + } + /// \note 7 switches at all, if-else is relevant + if (sw == "playercontrols") { + player.setLeftRight(0); + player.setForwardBackward(0); + player.setAutoMove(false); + } else if (sw == "playerjumping") { + /// \fixme maybe crouching at this time + player.setUpDown(0); + } + mControlSwitch[sw] = value; + } + }; /***CONSTRUCTOR***/ @@ -471,4 +500,9 @@ private: if (changeRes) impl->adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); } + + void MWInputManager::toggleControlSwitch(std::string sw, bool value) + { + impl->toggleControlSwitch(sw, value); + } } diff --git a/apps/openmw/mwinput/inputmanager.hpp b/apps/openmw/mwinput/inputmanager.hpp index 4ef3df137..2486f82d6 100644 --- a/apps/openmw/mwinput/inputmanager.hpp +++ b/apps/openmw/mwinput/inputmanager.hpp @@ -57,6 +57,8 @@ namespace MWInput void processChangedSettings(const Settings::CategorySettingVector& changed); void setDragDrop(bool dragDrop); + + void toggleControlSwitch(std::string sw, bool value); }; } #endif diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 24d8bcf1e..084698c5b 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -14,6 +14,8 @@ #include "../mwmechanics/npcstats.hpp" +#include "../mwinput/inputmanager.hpp" + #include "interpretercontext.hpp" #include "ref.hpp" @@ -36,6 +38,10 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { + MWBase::Environment::get() + .getInputManager() + ->toggleControlSwitch(mControl, mEnable); + if (mEnable) std::cout << "enable: " << mControl << std::endl; else diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4bdf3f948..8eb9a1c84 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -18,8 +18,6 @@ #include "manualref.hpp" #include "cellfunctors.hpp" -#include - using namespace Ogre; namespace @@ -1117,8 +1115,6 @@ namespace MWWorld if (cell.data.flags & ESM::Cell::HasWater == 0) { return false; } - bool res = pos.z < cell.water; - std::cout << "World::isUnderwater(" << pos.z << "):" << res << std::endl; - return res; + return pos.z < cell.water; } } From cacf0bd10dca7efd216f823976404b0be273fd95 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 4 Aug 2012 15:43:33 -0400 Subject: [PATCH 334/688] Basic collision with npcs --- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwworld/physicssystem.cpp | 18 +++++++++++++----- components/nifbullet/bullet_nif_loader.cpp | 21 +++++++++++++++++---- components/nifbullet/bullet_nif_loader.hpp | 1 + libs/openengine/bullet/physic.cpp | 4 +++- libs/openengine/bullet/pmove.cpp | 2 +- libs/openengine/bullet/pmove.h | 2 +- libs/openengine/bullet/trace.cpp | 13 ++++++++----- libs/openengine/bullet/trace.h | 1 - 10 files changed, 46 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ab4e2d5e6..72565abda 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -140,7 +140,7 @@ namespace MWClass std::string smodel = "meshes\\base_anim.nif"; if(beast) smodel = "meshes\\base_animkna.nif"; - physics.insertActorPhysics(ptr, smodel); + physics.insertObjectPhysics(ptr, smodel); MWBase::Environment::get().getMechanicsManager()->addActor (ptr); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index fa88b7277..3cc0589f0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -161,7 +161,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere textmappings = NIFLoader::getSingletonPtr()->getTextIndices(smodel); insert->attachObject(base); - + if(isFemale) insert->scale(race->data.height.female, race->data.height.female, race->data.height.female); else diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 9848efe6e..86268567a 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -156,10 +156,11 @@ namespace MWWorld act->setWalkDirection(btVector3(0,0,0)); } playerMove::playercmd& pm_ref = playerphysics->cmd; - + //playerphysics->ps.snappingImplemented = false; pm_ref.rightmove = 0; pm_ref.forwardmove = 0; pm_ref.upmove = 0; + //playerphysics->ps.move_type = PM_NOCLIP; for (std::vector >::const_iterator iter (actors.begin()); @@ -175,12 +176,14 @@ namespace MWWorld Ogre::Quaternion yawQuat = yawNode->getOrientation(); Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); + playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); - playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() *-1 + 90; - + playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() * -1 + 90; + if(playerphysics->ps.viewangles.y < 0) + playerphysics->ps.viewangles.y += 360; Ogre::Quaternion quat = yawNode->getOrientation(); Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); @@ -213,7 +216,7 @@ namespace MWWorld Ogre::Vector3 coord(newPos.x(), newPos.y(), newPos.z()); if(it->first == "player"){ - coord = playerphysics->ps.origin; + coord = playerphysics->ps.origin ; } @@ -242,7 +245,12 @@ namespace MWWorld OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle,scale); mEngine->addRigidBody(body); btTransform tr; - tr.setOrigin(btVector3(position.x,position.y,position.z)); + btBoxShape* box = dynamic_cast(body->getCollisionShape()); + if(box != NULL){ + tr.setOrigin(btVector3(position.x,position.y,position.z + box->getHalfExtentsWithMargin().getZ())); + } + else + tr.setOrigin(btVector3(position.x,position.y,position.z)); tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); body->setWorldTransform(tr); } diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 17c5f18ac..e0215fe19 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -136,6 +136,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) cShape = static_cast(resource); resourceName = cShape->getName(); cShape->collide = false; + mBoundingBox = NULL; mTriMesh = new btTriangleMesh(); @@ -199,9 +200,13 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) delete m_meshInterface; } }; - - currentShape = new TriangleMeshShape(mTriMesh,true); - cShape->Shape = currentShape; + if(mBoundingBox != NULL) + cShape->Shape = mBoundingBox; + else + { + currentShape = new TriangleMeshShape(mTriMesh,true); + cShape->Shape = currentShape; + } } bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) @@ -289,7 +294,15 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, final.scale *= trafo->scale; } - + if(node->hasBounds) + { + btVector3 getHalf = getbtVector(*(node->boundXYZ)); + //getHalf.setX(getHalf.getX() / 2); + //getHalf.setY(getHalf.getY() / 2); + //getHalf.setZ(getHalf.getZ() / 2); + const btVector3 boxsize = getHalf; + mBoundingBox = new btBoxShape(boxsize); + } // For NiNodes, loop through children if (node->recType == Nif::RC_NiNode) diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index 488a7a42b..93809ff7d 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -126,6 +126,7 @@ private: BulletShape* cShape;//current shape btTriangleMesh *mTriMesh; + btBoxShape *mBoundingBox; btBvhTriangleMeshShape* currentShape;//the shape curently under construction }; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 904747afa..8c07d6e2c 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -344,7 +344,9 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); shape->Shape->setLocalScaling( btVector3(scale,scale,scale)); - //btScaledBvhTriangleMeshShape* scaled = new btScaledBvhTriangleMeshShape(dynamic_cast (shape->Shape), btVector3(scale,scale,scale)); + + + // //create the motionState CMotionState* newMotionState = new CMotionState(this,name); diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 924812c29..4c70971a3 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -449,7 +449,7 @@ int PM_StepSlideMove( bool gravity ) //pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); //tracefunc(&trace, start_o, down, , 0, pml.scene); //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, start_o, down, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(&trace, down, start_o, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); // up = vec3(0, 0, 1) //VectorSet(up, 0, 0, 1); diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index aea63d1db..a0fec3d1c 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -55,7 +55,7 @@ static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); #define CONTENTS_FOG 64 static const float pm_accelerate = 10.0f; static const float pm_stopspeed = 100.0f; -static const float pm_friction = 12.0f; +static const float pm_friction = 6.0f; static const float pm_flightfriction = 3.0f; static const float pm_waterfriction = 1.0f; static const float pm_airaccelerate = 1.0f; diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 2d18aaa4d..6d2166baa 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -13,7 +13,8 @@ void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) //Traceobj was a Aedra Object { - + static float lastyaw = 0.0f; + static float lastpitch = 0.0f; //if (!traceobj) // return; @@ -21,10 +22,12 @@ void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogr // return; const Ogre::Vector3 rayDir = end - start; + + + - // Nudge starting point backwards - //const Position3D nudgestart = start + (rayDir * -0.1f); // by 10% (isn't that too much?) - //const Position3D nudgestart = start; + + NewPhysTraceResults out; //std::cout << "Starting trace\n"; @@ -32,7 +35,7 @@ void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogr //Ogre::Vector3 endReplace = startReplace; //endReplace.z -= .25; - const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0, rotation), isInterior, enginePass); + const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0f,0.0f), isInterior, enginePass); if (out.fraction < 0.001f) results->startsolid = true; diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index 076baf56e..bd554031a 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -9,7 +9,6 @@ #include - enum traceWorldType { collisionWorldTrace = 1, From 1db797836192232dc04cccc46129e9b61aebb1e6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 4 Aug 2012 22:03:14 +0200 Subject: [PATCH 335/688] silenced a warning --- 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 203f5f915..8fb7b1ba7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1112,7 +1112,7 @@ namespace MWWorld bool World::isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) { - if (cell.data.flags & ESM::Cell::HasWater == 0) { + if (!(cell.data.flags & ESM::Cell::HasWater)) { return false; } return pos.z < cell.water; From a63fd77ccca398c76b6cfd69580ad01277c01e3e Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Sat, 4 Aug 2012 22:58:26 +0200 Subject: [PATCH 336/688] Revert "esm_reader.hpp: fix std::runtime_error compile error" This reverts commit 0f3cb5667fa85c5e36a3dc783e16e8a5e4cb32e6. --- components/esm/esm_reader.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index c369f01f5..13f1f4a01 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include From 32b167ce2bd11068cffe119e6c44b6c8ae9de33e Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Sat, 4 Aug 2012 23:02:27 +0200 Subject: [PATCH 337/688] Revert "nif_file.hpp: include type definitions to fix build" This reverts commit b0b3ebe123bb710838408e5ec09d2336805fedd3. --- components/nif/nif_file.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 143c06993..624066317 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -39,7 +39,6 @@ #include "record.hpp" #include "nif_types.hpp" -#include "components/esm/loadland.hpp" namespace Nif { From c08e098d7f9218089e38610f1fc5b9faaee7c1c0 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Sat, 4 Aug 2012 22:51:43 +0200 Subject: [PATCH 338/688] esm_reader.cpp: fix std::runtime_error compile error --- components/esm/esm_reader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esm/esm_reader.cpp b/components/esm/esm_reader.cpp index a2cf69ddc..95ef46e81 100644 --- a/components/esm/esm_reader.cpp +++ b/components/esm/esm_reader.cpp @@ -1,4 +1,5 @@ #include "esm_reader.hpp" +#include namespace ESM { From 20deb97a09bf85b1456421609cc85848a04ee594 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Sat, 4 Aug 2012 23:14:53 +0200 Subject: [PATCH 339/688] nif_file.hpp: add stdint.h include to fix build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It fixes such compile errors as: * error: ‘uint8_t’ does not name a type * error: ‘uint16_t’ does not name a type * error: ‘uint32_t’ does not name a type --- components/nif/nif_file.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 624066317..d236bc460 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "record.hpp" #include "nif_types.hpp" From dbcd4a8b5bef088bc10bd6e4c92b72848b788007 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 5 Aug 2012 00:06:19 +0200 Subject: [PATCH 340/688] compatibility fix --- components/nif/nif_file.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index d236bc460..a4eaa8990 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -36,7 +36,8 @@ #include #include #include -#include + +#include #include "record.hpp" #include "nif_types.hpp" From d850a3ee49bf31b4b301695a6023d56c9bd29566 Mon Sep 17 00:00:00 2001 From: gugus Date: Sun, 5 Aug 2012 16:21:53 +0200 Subject: [PATCH 341/688] some correction --- .../mwscript/transformationextensions.cpp | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 1eb53e8f3..432482e48 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -66,15 +66,15 @@ namespace MWScript float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); - if(axis == "X") + if(axis == "x") { MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); } - if(axis == "Y") + if(axis == "y") { MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); } - if(axis == "Z") + if(axis == "z") { MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); } @@ -93,15 +93,15 @@ namespace MWScript std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - if(axis == "X") + if(axis == "x") { runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); } - if(axis == "Y") + if(axis == "y") { runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); } - if(axis == "Z") + if(axis == "z") { runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); } @@ -120,15 +120,15 @@ namespace MWScript std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - if(axis == "X") + if(axis == "x") { runtime.push(ptr.getRefData().getPosition().pos[0]); } - if(axis == "Y") + if(axis == "y") { runtime.push(ptr.getRefData().getPosition().pos[1]); } - if(axis == "Z") + if(axis == "z") { runtime.push(ptr.getRefData().getPosition().pos[2]); } @@ -154,15 +154,15 @@ namespace MWScript float az = ptr.getRefData().getPosition().pos[2]; std::cout << "setPos"; - if(axis == "X") + if(axis == "x") { MWBase::Environment::get().getWorld()->moveObject(ptr,pos,ay,az); } - if(axis == "Y") + if(axis == "y") { MWBase::Environment::get().getWorld()->moveObject(ptr,ax,pos,az); } - if(axis == "Z") + if(axis == "z") { MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); } @@ -181,15 +181,15 @@ namespace MWScript std::string axis = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - if(axis == "X") + if(axis == "x") { runtime.push(ptr.getCellRef().pos.pos[0]); } - if(axis == "Y") + if(axis == "y") { runtime.push(ptr.getCellRef().pos.pos[1]); } - if(axis == "Z") + if(axis == "z") { runtime.push(ptr.getCellRef().pos.pos[2]); } @@ -217,9 +217,9 @@ namespace MWScript extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); extensions.registerInstruction("setangle","Sf",opcodeSetAngle,opcodeSetAngleExplicit); extensions.registerFunction("getangle",'f',"S",opcodeGetAngle,opcodeGetAngleExplicit); - extensions.registerInstruction("setpos","Sf",opcodeSetPos,opcodeSetPosExplicit); - extensions.registerFunction("getpos",'f',"S",opcodeGetPos,opcodeGetPosExplicit); - extensions.registerFunction("getstartingpos",'f',"S",opcodeGetStartingPos,opcodeGetStartingPosExplicit); + extensions.registerInstruction("setpos","cf",opcodeSetPos,opcodeSetPosExplicit); + extensions.registerFunction("getpos",'f',"c",opcodeGetPos,opcodeGetPosExplicit); + extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) From 8a485e213b78ef280807d1b91845cf50dee168bb Mon Sep 17 00:00:00 2001 From: gugus Date: Sun, 5 Aug 2012 16:44:56 +0200 Subject: [PATCH 342/688] first try of the Position script instruction. Still missing: check if overlapp with an object. --- .../mwscript/transformationextensions.cpp | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 432482e48..46cbf781f 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -196,6 +196,37 @@ namespace MWScript } }; + template + class OpPosition : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Float x = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float y = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float z = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float zRot = runtime[0].mFloat; + runtime.pop(); + + MWBase::Environment::get().getWorld()->moveObject(ptr,x,y,z); + float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); + if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity + { + ax = ax/60.; + ay = ay/60.; + zRot = zRot/60.; + } + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + } + }; + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -210,6 +241,8 @@ namespace MWScript const int opcodeSetPosExplicit = 0x200016f; const int opcodeGetStartingPos = 0x2000170; const int opcodeGetStartingPosExplicit = 0x2000171; + const int opcodePosition = 0x2000172; + const int opcodePositionExplicit = 0x2000173; void registerExtensions (Compiler::Extensions& extensions) { @@ -220,6 +253,7 @@ namespace MWScript extensions.registerInstruction("setpos","cf",opcodeSetPos,opcodeSetPosExplicit); extensions.registerFunction("getpos",'f',"c",opcodeGetPos,opcodeGetPosExplicit); extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit); + extensions.registerInstruction("position","ffff",opcodePosition,opcodePositionExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -238,6 +272,8 @@ namespace MWScript interpreter.installSegment5(opcodeSetPosExplicit,new OpSetPos); interpreter.installSegment5(opcodeGetStartingPos,new OpGetStartingPos); interpreter.installSegment5(opcodeGetStartingPosExplicit,new OpGetStartingPos); + interpreter.installSegment5(opcodePosition,new OpPosition); + interpreter.installSegment5(opcodePositionExplicit,new OpPosition); } } } From ee97a204f2db3970358cd77667208dfcc72379f1 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 6 Aug 2012 15:26:37 +0400 Subject: [PATCH 343/688] fix npc animation key update --- apps/openmw/mwrender/actors.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index a73244305..415d17241 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -146,9 +146,10 @@ Actors::updateObjectCell(const MWWorld::Ptr &ptr) node = mCellSceneNodes[newCell]; } node->addChild(ptr.getRefData().getBaseNode()); - if (Animation *anim = mAllActors[ptr]) { + if (mAllActors.find(ptr) != mAllActors.end()) { /// \note Update key (Ptr's are compared only with refdata so mCell /// on key is outdated), maybe redundant + Animation *anim = mAllActors[ptr]; mAllActors.erase(ptr); mAllActors[ptr] = anim; } From d32a61b928e6df7df164fcdb16ce7248d8057f88 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 6 Aug 2012 18:20:48 +0400 Subject: [PATCH 344/688] fix object moving --- apps/openmw/mwworld/cells.cpp | 44 +++++++++++++++++--------------- apps/openmw/mwworld/worldimp.cpp | 5 ++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index cffaf70ea..822aa78d6 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -167,67 +167,71 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce else return Ptr(); } + MWWorld::Ptr ptr; if (MWWorld::LiveCellRef *ref = cell.activators.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.potions.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.appas.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.armors.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.books.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.clothes.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.containers.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.creatures.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.doors.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.ingreds.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.creatureLists.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.itemLists.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.lights.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.lockpicks.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.miscItems.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.npcs.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.probes.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.repairs.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.statics.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); if (MWWorld::LiveCellRef *ref = cell.weapons.find (name)) - return Ptr (ref, &cell); + ptr = Ptr (ref, &cell); + if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0) { + return ptr; + } return Ptr(); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 215fbb30e..dc7f25751 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -545,7 +545,7 @@ namespace MWWorld bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) { - bool cellChanged = false, haveToMove = true; + bool cellChanged = false; ESM::Position &pos = ptr.getRefData().getPosition(); pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; @@ -558,6 +558,7 @@ namespace MWWorld } else { currCell = ptr.getCell(); } + bool haveToMove = mWorldScene->isCellActive(*currCell); if (currCell) { if (!(currCell->cell->data.flags & ESM::Cell::Interior)) { @@ -575,7 +576,7 @@ namespace MWWorld MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); // placeObject() handles both target cell states - // with active current cell + // with inactive current cell if (!mWorldScene->isCellActive(*currCell)) { placeObject(ptr, *newCell, pos); haveToMove = false; From 79b7487c8757b3d2c2a587f8129ad17cc99893e5 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 7 Aug 2012 22:21:46 +0400 Subject: [PATCH 345/688] fix using deleted objects --- apps/openmw/mwworld/cellstore.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 0be0ef651..8eafb6b10 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -69,7 +69,7 @@ namespace MWWorld { for (typename std::list::iterator iter (list.begin()); iter!=list.end(); ++iter) { - if (iter->ref.refID==name) + if (iter->mData.getCount() > 0 && iter->ref.refID == name) return &*iter; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dc7f25751..748e6dc04 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -51,7 +51,7 @@ namespace for (iterator iter (refList.list.begin()); iter!=refList.list.end(); ++iter) { - if(iter->mData.getBaseNode()){ + if(iter->mData.getCount() > 0 && iter->mData.getBaseNode()){ if (iter->mData.getHandle()==handle) { return &*iter; From 993c7150dcdc00979f70fe23c421443fe721b057 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Aug 2012 22:43:11 +0200 Subject: [PATCH 346/688] tweaked depth bias --- files/materials/shadows.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/materials/shadows.h b/files/materials/shadows.h index b1dc92d21..65dffe492 100644 --- a/files/materials/shadows.h +++ b/files/materials/shadows.h @@ -1,5 +1,5 @@ -#define FIXED_BIAS 0.005 +#define FIXED_BIAS 0.0003 float depthShadowPCF (shTexture2D shadowMap, float4 shadowMapPos, float2 offset) { From 7cc2de3e21adfad2605518f96a961181ad585273 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 08:52:08 +0200 Subject: [PATCH 347/688] boost filesystem compatibility fix --- components/files/ogreplugin.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index c5292b3a2..2d56bfb47 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -32,7 +32,7 @@ namespace Ogre { class Root; } -#if (BOOST_VERSION <= 104300) +#if (BOOST_VERSION <= 104500) namespace boost { namespace filesystem { inline path absolute(const path& p, const path& base=current_path()) { From b6f7f21bcfe3aee9b367681ca18d1cdb914e2e3a Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 8 Aug 2012 14:51:33 +0400 Subject: [PATCH 348/688] fix player cell assertion fail, moveObject(Ptr&, CellStore&, f, f, f) added --- apps/openmw/mwbase/world.hpp | 5 +- apps/openmw/mwworld/cellstore.hpp | 14 ++++ apps/openmw/mwworld/scene.cpp | 10 +-- apps/openmw/mwworld/worldimp.cpp | 122 ++++++++++++++---------------- apps/openmw/mwworld/worldimp.hpp | 3 +- 5 files changed, 81 insertions(+), 73 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f257b723e..d6037f107 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -57,7 +57,7 @@ namespace MWBase protected: virtual void - placeObject( + copyObjectToCell( const MWWorld::Ptr &ptr, MWWorld::CellStore &cell, const ESM::Position &pos) = 0; @@ -184,6 +184,9 @@ namespace MWBase virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; + virtual void + moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore &newCell, float x, float y, float z) = 0; + virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z) = 0; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 8eafb6b10..3ec4a2e78 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -155,6 +155,20 @@ namespace MWWorld forEachImp (functor, weapons); } + bool operator==(const CellStore &cell) { + return this->cell->name == cell.cell->name && + this->cell->data.gridX == cell.cell->data.gridX && + this->cell->data.gridY == cell.cell->data.gridY; + } + + bool operator!=(const CellStore &cell) { + return !(*this == cell); + } + + bool isExterior() const { + return cell->isExterior(); + } + private: template diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 13e5ecb85..ef5da7b91 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -138,6 +138,8 @@ namespace MWWorld void Scene::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position, bool adjustPlayerPos) { + MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); + bool hasWater = cell->cell->data.flags & cell->cell->HasWater; mPhysics->setCurrentWater(hasWater, cell->cell->water); if (adjustPlayerPos) @@ -146,7 +148,6 @@ namespace MWWorld MWBase::Environment::get().getWorld()->getPlayer().setRot (position.rot[0], position.rot[1], position.rot[2]); } - MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); MWBase::Environment::get().getMechanicsManager()->addActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); MWBase::Environment::get().getMechanicsManager()->watchActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); @@ -224,7 +225,7 @@ namespace MWWorld // adjust player - playerCellChange (MWBase::Environment::get().getWorld()->getExterior(X, Y), position, adjustPlayerPos); + playerCellChange (mCurrentCell, position, adjustPlayerPos); // Sky system MWBase::Environment::get().getWorld()->adjustSky(); @@ -350,10 +351,7 @@ namespace MWWorld { CellStoreCollection::iterator active = mActiveCells.begin(); while (active != mActiveCells.end()) { - if ((*active)->cell->name == cell.cell->name && - (*active)->cell->data.gridX == cell.cell->data.gridX && - (*active)->cell->data.gridY == cell.cell->data.gridY) - { + if (**active == cell) { return true; } ++active; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 748e6dc04..4dd06ad05 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -543,81 +543,73 @@ namespace MWWorld } } - bool World::moveObjectImp (const Ptr& ptr, float x, float y, float z) + void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) { - bool cellChanged = false; - ESM::Position &pos = ptr.getRefData().getPosition(); pos.pos[0] = x, pos.pos[1] = y, pos.pos[2] = z; Ogre::Vector3 vec(x, y, z); - CellStore *currCell; - /// \todo fix assertion fail on player ptr.getCell() on start - if (ptr == mPlayer->getPlayer()) { - currCell = mWorldScene->getCurrentCell(); - } else { - currCell = ptr.getCell(); - } - bool haveToMove = mWorldScene->isCellActive(*currCell); + CellStore *currCell = ptr.getCell(); + bool isPlayer = ptr == mPlayer->getPlayer(); + bool haveToMove = mWorldScene->isCellActive(*currCell) || isPlayer; - if (currCell) { - if (!(currCell->cell->data.flags & ESM::Cell::Interior)) { - // exterior -> adjust loaded cells - int cellX = 0, cellY = 0; - positionToIndex (x, y, cellX, cellY); - - if (currCell->cell->data.gridX != cellX || - currCell->cell->data.gridY != cellY) - { - if (ptr == mPlayer->getPlayer()) { - mWorldScene->changeCell(cellX, cellY, pos, false); - } else { - CellStore *newCell = - MWBase::Environment::get().getWorld()->getExterior(cellX, cellY); - - // placeObject() handles both target cell states - // with inactive current cell - if (!mWorldScene->isCellActive(*currCell)) { - placeObject(ptr, *newCell, pos); - haveToMove = false; - } else if (!mWorldScene->isCellActive(*newCell)) { - MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); - mWorldScene->removeObjectFromScene(ptr); - mLocalScripts.remove(ptr); - - haveToMove = false; - } else { - MWWorld::Ptr copy = - MWWorld::Class::get(ptr).copyToCell(ptr, *newCell); - - mRendering->moveObjectToCell(copy, vec, currCell); - - if (MWWorld::Class::get(ptr).isActor()) { - MWMechanics::MechanicsManager *mechMgr = - MWBase::Environment::get().getMechanicsManager(); - - mechMgr->removeActor(ptr); - mechMgr->addActor(copy); - } else { - std::string script = - MWWorld::Class::get(ptr).getScript(ptr); - if (!script.empty()) { - mLocalScripts.remove(ptr); - mLocalScripts.add(script, copy); - } - } - } - ptr.getRefData().setCount(0); - } - cellChanged = true; + if (*currCell != newCell) { + if (isPlayer) { + if (!newCell.isExterior()) { + changeToInteriorCell(newCell.cell->name, pos); + } else { + changeToExteriorCell(pos); } + } else { + if (!mWorldScene->isCellActive(newCell)) { + copyObjectToCell(ptr, newCell, pos); + } else if (!mWorldScene->isCellActive(*currCell)) { + MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + mWorldScene->removeObjectFromScene(ptr); + mLocalScripts.remove(ptr); + haveToMove = false; + } else { + MWWorld::Ptr copy = + MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + + mRendering->moveObjectToCell(copy, vec, currCell); + + if (MWWorld::Class::get(ptr).isActor()) { + MWMechanics::MechanicsManager *mechMgr = + MWBase::Environment::get().getMechanicsManager(); + + mechMgr->removeActor(ptr); + mechMgr->addActor(copy); + } else { + std::string script = + MWWorld::Class::get(ptr).getScript(ptr); + if (!script.empty()) { + mLocalScripts.remove(ptr); + mLocalScripts.add(script, copy); + } + } + } + ptr.getRefData().setCount(0); } } if (haveToMove) { mRendering->moveObject(ptr, vec); mPhysics->moveObject(ptr.getRefData().getHandle(), vec); } - return cellChanged; + } + + bool World::moveObjectImp(const Ptr& ptr, float x, float y, float z) + { + CellStore *cell = ptr.getCell(); + if (cell->isExterior()) { + int cellX, cellY; + positionToIndex(x, y, cellX, cellY); + + cell = getExterior(cellX, cellY); + } + moveObject(ptr, *cell, x, y, z); + + return cell != ptr.getCell(); } void World::moveObject (const Ptr& ptr, float x, float y, float z) @@ -1058,7 +1050,7 @@ namespace MWWorld pos.pos[1] = -result.second[2]; pos.pos[2] = result.second[1]; - placeObject(object, *cell, pos); + copyObjectToCell(object, *cell, pos); object.getRefData().setCount(0); return true; @@ -1076,7 +1068,7 @@ namespace MWWorld } void - World::placeObject(const Ptr &object, CellStore &cell, const ESM::Position &pos) + World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos) { /// \todo add searching correct cell for position specified MWWorld::Ptr dropped = @@ -1119,7 +1111,7 @@ namespace MWWorld mPhysics->castRay(orig, dir, len); pos.pos[2] = hit.second.z; - placeObject(object, *cell, pos); + copyObjectToCell(object, *cell, pos); object.getRefData().setCount(0); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d39871c21..e33e90eaa 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -90,7 +90,7 @@ namespace MWWorld ///< @return true if the active cell (cell player is in) changed virtual void - placeObject(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); public: @@ -206,6 +206,7 @@ namespace MWWorld virtual void deleteObject (const Ptr& ptr); virtual void moveObject (const Ptr& ptr, float x, float y, float z); + virtual void moveObject (const Ptr& ptr, CellStore &newCell, float x, float y, float z); virtual void scaleObject (const Ptr& ptr, float scale); From 4977fafb9c252c00fb148288fcd4bae267d5bc6e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 14:27:14 +0200 Subject: [PATCH 349/688] updated credits.txt --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 3a942bfd8..5b8ede117 100644 --- a/credits.txt +++ b/credits.txt @@ -14,6 +14,7 @@ Jason “jhooks†Hooks Karl-Felix “k1ll†Glatzer Lukasz “lgro†Gromanowski Marc “Zini†Zinnschlag +Michael Mc Donnell Michael “werdanith†Papageorgiou Nikolay “corristo†Kasyanov Pieter “pvdk†van der Kloet From 9b567e4ba69060286ea9a80e4b596d03b9f6c007 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 14:54:35 +0200 Subject: [PATCH 350/688] Issue #107: Removed a redundant function in world interface; added a comment --- apps/openmw/mwbase/world.hpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d9e19bead..80669d586 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -46,6 +46,7 @@ namespace MWWorld namespace MWBase { + /// \brief Interface for the World (implemented in MWWorld) class World { World (const World&); @@ -54,14 +55,6 @@ namespace MWBase World& operator= (const World&); ///< not implemented - protected: - - virtual void - placeObject( - const MWWorld::Ptr &ptr, - MWWorld::CellStore &cell, - const ESM::Position &pos) = 0; - public: enum RenderMode From 28ecfb4290e4e867d11379ec882d2e9439d7cf09 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 8 Aug 2012 15:18:55 +0200 Subject: [PATCH 351/688] Issue #107: ScriptManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +-- apps/openmw/mwbase/environment.hpp | 12 ++-- apps/openmw/mwbase/scriptmanager.hpp | 62 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanager.cpp | 2 +- apps/openmw/mwscript/compilercontext.cpp | 5 +- apps/openmw/mwscript/globalscripts.cpp | 11 ++-- apps/openmw/mwscript/globalscripts.hpp | 5 +- apps/openmw/mwscript/interpretercontext.cpp | 2 +- ...scriptmanager.cpp => scriptmanagerimp.cpp} | 4 +- ...scriptmanager.hpp => scriptmanagerimp.hpp} | 17 ++--- 12 files changed, 96 insertions(+), 37 deletions(-) create mode 100644 apps/openmw/mwbase/scriptmanager.hpp rename apps/openmw/mwscript/{scriptmanager.cpp => scriptmanagerimp.cpp} (98%) rename apps/openmw/mwscript/{scriptmanager.hpp => scriptmanagerimp.hpp} (75%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 556b9a1ba..768054ed7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -36,7 +36,7 @@ add_openmw_dir (mwdialogue ) add_openmw_dir (mwscript - locals scriptmanager compilercontext interpretercontext cellextensions miscextensions + locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions animationextensions transformationextensions consoleextensions userextensions @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world + environment world scriptmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5e9aedf66..d4fa078ae 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -18,7 +18,7 @@ #include "mwgui/window_manager.hpp" #include "mwgui/cursorreplace.hpp" -#include "mwscript/scriptmanager.hpp" +#include "mwscript/scriptmanagerimp.hpp" #include "mwscript/extensions.hpp" #include "mwsound/soundmanager.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 7218f22eb..e01500003 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -5,8 +5,6 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwscript/scriptmanager.hpp" - #include "../mwsound/soundmanager.hpp" #include "../mwdialogue/dialoguemanager.hpp" @@ -15,6 +13,7 @@ #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" +#include "scriptmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -42,7 +41,7 @@ void MWBase::Environment::setSoundManager (MWSound::SoundManager *soundManager) mSoundManager = soundManager; } -void MWBase::Environment::setScriptManager (MWScript::ScriptManager *scriptManager) +void MWBase::Environment::setScriptManager (ScriptManager *scriptManager) { mScriptManager = scriptManager; } @@ -89,7 +88,7 @@ MWSound::SoundManager *MWBase::Environment::getSoundManager() const return mSoundManager; } -MWScript::ScriptManager *MWBase::Environment::getScriptManager() const +MWBase::ScriptManager *MWBase::Environment::getScriptManager() const { assert (mScriptManager); return mScriptManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index ad5a6f655..50d9e3940 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWSound class SoundManager; } -namespace MWScript -{ - class ScriptManager; -} - namespace MWGui { class WindowManager; @@ -35,6 +30,7 @@ namespace MWInput namespace MWBase { class World; + class ScriptManager; /// \brief Central hub for mw-subsystems /// @@ -48,7 +44,7 @@ namespace MWBase World *mWorld; MWSound::SoundManager *mSoundManager; - MWScript::ScriptManager *mScriptManager; + ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; MWDialogue::DialogueManager *mDialogueManager; @@ -72,7 +68,7 @@ namespace MWBase void setSoundManager (MWSound::SoundManager *soundManager); - void setScriptManager (MWScript::ScriptManager *scriptManager); + void setScriptManager (MWBase::ScriptManager *scriptManager); void setWindowManager (MWGui::WindowManager *windowManager); @@ -91,7 +87,7 @@ namespace MWBase MWSound::SoundManager *getSoundManager() const; - MWScript::ScriptManager *getScriptManager() const; + MWBase::ScriptManager *getScriptManager() const; MWGui::WindowManager *getWindowManager() const; diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp new file mode 100644 index 000000000..a8db868c3 --- /dev/null +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -0,0 +1,62 @@ +#ifndef GAME_MWBASE_SCRIPTMANAGERIMP_H +#define GAME_MWBASE_SCRIPTMANAGERIMP_H + +#include + +namespace Interpreter +{ + class Context; +} + +namespace Compiler +{ + class Locals; +} + +namespace MWScript +{ + class GlobalScripts; +} + +namespace MWBase +{ + /// \brief Interface for script manager (implemented in MWScript) + class ScriptManager + { + ScriptManager (const ScriptManager&); + ///< not implemented + + ScriptManager& operator= (const ScriptManager&); + ///< not implemented + + public: + + ScriptManager() {} + + virtual ~ScriptManager() {} + + virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; + ///< Run the script with the given name (compile first, if not compiled yet) + + virtual bool compile (const std::string& name) = 0; + ///< Compile script with the given namen + /// \return Success? + + virtual std::pair compileAll() = 0; + ///< Compile all scripts + /// \return count, success + + virtual Compiler::Locals& getLocals (const std::string& name) = 0; + ///< Return locals for script \a name. + + virtual MWScript::GlobalScripts& getGlobalScripts() = 0; + + virtual int getLocalIndex (const std::string& scriptId, const std::string& variable, + char type) = 0; + ///< Return index of the variable of the given name and type in the given script. Will + /// throw an exception, if there is no such script or variable or the type does not match. + + }; +} + +#endif diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 6f3f0c901..3fde7ad6c 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -26,7 +27,6 @@ #include #include "../mwscript/extensions.hpp" -#include "../mwscript/scriptmanager.hpp" #include #include diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index 57e786b11..139a5cc6c 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -3,14 +3,15 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" -#include "scriptmanager.hpp" - namespace MWScript { CompilerContext::CompilerContext (Type type) diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 3f4cec723..6407bf0cb 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -6,13 +6,15 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/scriptmanager.hpp" + #include "interpretercontext.hpp" -#include "scriptmanager.hpp" namespace MWScript { - GlobalScripts::GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager) - : mStore (store), mScriptManager (scriptManager) + GlobalScripts::GlobalScripts (const ESMS::ESMStore& store) + : mStore (store) { addScript ("Main"); @@ -63,9 +65,8 @@ namespace MWScript { MWScript::InterpreterContext interpreterContext ( &iter->second.second, MWWorld::Ptr()); - mScriptManager.run (iter->first, interpreterContext); + MWBase::Environment::get().getScriptManager()->run (iter->first, interpreterContext); } } - } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index 3d62e4d7a..cc2f7ffdd 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -13,17 +13,14 @@ namespace ESMS namespace MWScript { - class ScriptManager; - class GlobalScripts { const ESMS::ESMStore& mStore; - ScriptManager& mScriptManager; std::map > mScripts; // running, local variables public: - GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager); + GlobalScripts (const ESMS::ESMStore& store); void addScript (const std::string& name); diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 3a824473e..327eed913 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -18,7 +19,6 @@ #include "locals.hpp" #include "globalscripts.hpp" -#include "scriptmanager.hpp" namespace MWScript { diff --git a/apps/openmw/mwscript/scriptmanager.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp similarity index 98% rename from apps/openmw/mwscript/scriptmanager.cpp rename to apps/openmw/mwscript/scriptmanagerimp.cpp index d8bd269c6..0b33d43fa 100644 --- a/apps/openmw/mwscript/scriptmanager.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -1,5 +1,5 @@ -#include "scriptmanager.hpp" +#include "scriptmanagerimp.hpp" #include #include @@ -21,7 +21,7 @@ namespace MWScript Compiler::Context& compilerContext) : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), - mOpcodesInstalled (false), mGlobalScripts (store, *this) + mOpcodesInstalled (false), mGlobalScripts (store) {} bool ScriptManager::compile (const std::string& name) diff --git a/apps/openmw/mwscript/scriptmanager.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp similarity index 75% rename from apps/openmw/mwscript/scriptmanager.hpp rename to apps/openmw/mwscript/scriptmanagerimp.hpp index a466f903d..bacc232b2 100644 --- a/apps/openmw/mwscript/scriptmanager.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -10,6 +10,8 @@ #include #include +#include "../mwbase/scriptmanager.hpp" + #include "globalscripts.hpp" namespace ESMS @@ -30,7 +32,7 @@ namespace Interpreter namespace MWScript { - class ScriptManager + class ScriptManager : public MWBase::ScriptManager { Compiler::StreamErrorHandler mErrorHandler; const ESMS::ESMStore& mStore; @@ -51,23 +53,24 @@ namespace MWScript ScriptManager (const ESMS::ESMStore& store, bool verbose, Compiler::Context& compilerContext); - void run (const std::string& name, Interpreter::Context& interpreterContext); + virtual void run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) - bool compile (const std::string& name); + virtual bool compile (const std::string& name); ///< Compile script with the given namen /// \return Success? - std::pair compileAll(); + virtual std::pair compileAll(); ///< Compile all scripts /// \return count, success - Compiler::Locals& getLocals (const std::string& name); + virtual Compiler::Locals& getLocals (const std::string& name); ///< Return locals for script \a name. - GlobalScripts& getGlobalScripts(); + virtual GlobalScripts& getGlobalScripts(); - int getLocalIndex (const std::string& scriptId, const std::string& variable, char type); + virtual int getLocalIndex (const std::string& scriptId, const std::string& variable, + char type); ///< Return index of the variable of the given name and type in the given script. Will /// throw an exception, if there is no such script or variable or the type does not match. }; From ec9cf4d3c60c39edd89f0c734bc84c726640a8d1 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 00:15:52 +0400 Subject: [PATCH 352/688] rotateObject() added, input system rewritten --- apps/openmw/CMakeLists.txt | 1 + apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwinput/inputmanager.cpp | 14 +++-- apps/openmw/mwinput/mouselookevent.cpp | 28 ++++++++++ apps/openmw/mwinput/mouselookevent.hpp | 57 +++++++++++++++++++++ apps/openmw/mwrender/player.cpp | 50 +++++++++++++----- apps/openmw/mwrender/player.hpp | 34 +++++++++--- apps/openmw/mwrender/renderinginterface.hpp | 15 +++--- apps/openmw/mwrender/renderingmanager.cpp | 33 ++++++++++-- apps/openmw/mwrender/renderingmanager.hpp | 8 +-- apps/openmw/mwsound/soundmanager.cpp | 4 +- apps/openmw/mwworld/player.cpp | 12 +---- apps/openmw/mwworld/player.hpp | 13 +---- apps/openmw/mwworld/scene.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 28 +++++----- apps/openmw/mwworld/worldimp.hpp | 2 +- 16 files changed, 217 insertions(+), 86 deletions(-) create mode 100644 apps/openmw/mwinput/mouselookevent.cpp create mode 100644 apps/openmw/mwinput/mouselookevent.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 556b9a1ba..82f8680fb 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,6 +21,7 @@ add_openmw_dir (mwrender add_openmw_dir (mwinput inputmanager + mouselookevent ) add_openmw_dir (mwgui diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index d9e19bead..298a2ac79 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -186,7 +186,7 @@ namespace MWBase virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; - virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z) = 0; + virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const = 0; diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 6bda77ea8..bbd80e8c0 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include "../mwgui/window_manager.hpp" @@ -16,12 +15,12 @@ #include +#include "mouselookevent.hpp" + #include "../engine.hpp" #include "../mwworld/player.hpp" -#include "../mwrender/player.hpp" - #include #include #include @@ -82,7 +81,7 @@ namespace MWInput OEngine::Render::ExitListener exit; Mangle::Input::OISDriver input; OEngine::Input::Poller poller; - OEngine::Render::MouseLookEventPtr mouse; + MouseLookEventPtr mouse; OEngine::GUI::EventInjectorPtr guiEvents; MWWorld::Player &player; MWGui::WindowManager &windows; @@ -279,8 +278,7 @@ private: // Add the exit listener ogre.getRoot()->addFrameListener(&exit); - // Set up the mouse handler and tell it about the player camera - mouse = MouseLookEventPtr(new MouseLookEvent(player.getRenderer()->getCamera())); + mouse = MouseLookEventPtr(new MouseLookEvent()); // This event handler pumps events into MyGUI guiEvents = EventInjectorPtr(new EventInjector(windows.getGui())); @@ -419,7 +417,7 @@ private: if(guiMode) { // Disable mouse look - mouse->setCamera(NULL); + mouse->disable(); // Enable GUI events guiEvents->enabled = true; @@ -428,7 +426,7 @@ private: { // Start mouse-looking again. TODO: This should also allow // for other ways to disable mouselook, like paralyzation. - mouse->setCamera(player.getRenderer()->getCamera()); + mouse->enable(); // Disable GUI events guiEvents->enabled = false; diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp new file mode 100644 index 000000000..4138c481c --- /dev/null +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -0,0 +1,28 @@ +#include "mouselookevent.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" + +#include +#include +#include + +using namespace OIS; +using namespace MWInput; + +void MouseLookEvent::event(Type type, int index, const void *p) +{ + if (type != EV_MouseMove || mDisabled) { + return; + } + + MouseEvent *arg = (MouseEvent*)(p); + + float x = arg->state.X.rel * sensX; + float y = arg->state.Y.rel * sensY; + + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); +} diff --git a/apps/openmw/mwinput/mouselookevent.hpp b/apps/openmw/mwinput/mouselookevent.hpp new file mode 100644 index 000000000..af996643f --- /dev/null +++ b/apps/openmw/mwinput/mouselookevent.hpp @@ -0,0 +1,57 @@ +#ifndef _MWINPUT_MOUSELOOKEVENT_H +#define _MWINPUT_MOUSELOOKEVENT_H + +/* + A mouse-look class for Ogre. Accepts input events from Mangle::Input + and translates them. + + You can adjust the mouse sensibility and switch to a different + camera. The mouselook class also has an optional wrap protection + that keeps the camera from flipping upside down. + + You can disable the mouse looker at any time by calling + setCamera(NULL), and reenable it by setting the camera back. + + NOTE: The current implementation will ONLY work for native OIS + events. + */ + +#include + +namespace MWInput +{ + class MouseLookEvent : public Mangle::Input::Event + { + float sensX, sensY; // Mouse sensibility + bool flipProt; // Flip protection + bool mDisabled; + + public: + MouseLookEvent(float sX = 0.2, float sY = 0.2, bool prot=true) + : sensX(sX), sensY(sY), flipProt(prot) + {} + + void setSens(float sX, float sY) { + sensX = sX; + sensY = sY; + } + + void setProt(bool p) { + flipProt = p; + } + + void disable() { + mDisabled = true; + } + + void enable() { + mDisabled = false; + } + + void event(Type type, int index, const void *p); + }; + + typedef boost::shared_ptr MouseLookEventPtr; +} + +#endif diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 27ae28e14..a71d9da68 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -1,34 +1,58 @@ - #include "player.hpp" #include +#include + +#include "../mwworld/ptr.hpp" +#include "../mwworld/refdata.hpp" namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) - : mCamera (camera), mNode (node) + : mCamera (camera), + mNode (node), + mFirstPersonView(true), + mVanityModeEnabled(false) {} - void Player::setRot(float x, float y, float z) + bool Player::setRotation(const Ogre::Vector3 &rot) { - Ogre::SceneNode *sceneNode = mNode; - Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); - Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); + Ogre::SceneNode *sceneNode = mNode; + Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); + Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); - // we are only interested in X and Y rotation + // we are only interested in X and Y rotation - // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(x), Ogre::Vector3::UNIT_X); + // Rotate around X axis + Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); - // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-z), Ogre::Vector3::UNIT_Y); + // Rotate around Y axis + Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); - pitchNode->setOrientation(xr); - yawNode->setOrientation(yr); + pitchNode->setOrientation(xr); + yawNode->setOrientation(yr); + + return !mVanityModeEnabled; } std::string Player::getHandle() const { return mNode->getName(); } + + void Player::attachTo(const MWWorld::Ptr &ptr) + { + ptr.getRefData().setBaseNode(mNode); + } + + bool Player::adjustRotation(const Ogre::Vector3 &rot) + { + Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); + Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); + + pitchNode->pitch(Ogre::Degree(rot.x)); + yawNode->yaw(Ogre::Degree(rot.z)); + + return !mVanityModeEnabled; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 0ba936f6f..a145997a9 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -3,12 +3,19 @@ #include + namespace Ogre -{ +{ + class Vector3; class Camera; class SceneNode; } +namespace MWWorld +{ + class Ptr; +} + namespace MWRender { /// \brief Player character rendering and camera control @@ -17,17 +24,28 @@ namespace MWRender Ogre::Camera *mCamera; Ogre::SceneNode* mNode; - public: + bool mFirstPersonView; + bool mVanityModeEnabled; - Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); + public: - Ogre::Camera *getCamera() { return mCamera; } + Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); - /// Set where the player is looking at. Uses Morrowind (euler) angles - void setRot(float x, float y, float z); + /// Set where the player is looking at. Uses Morrowind (euler) angles + bool setRotation(const Ogre::Vector3 &rot); + bool adjustRotation(const Ogre::Vector3 &rot); - std::string getHandle() const; - Ogre::SceneNode* getNode() {return mNode;} + std::string getHandle() const; + + void attachTo(const MWWorld::Ptr &); + + void toggleViewMode() { + mFirstPersonView = !mFirstPersonView; + } + + void toggleVanityMode() { + mVanityModeEnabled = !mVanityModeEnabled; + } }; } diff --git a/apps/openmw/mwrender/renderinginterface.hpp b/apps/openmw/mwrender/renderinginterface.hpp index 03935bef6..8ae2c0f8f 100644 --- a/apps/openmw/mwrender/renderinginterface.hpp +++ b/apps/openmw/mwrender/renderinginterface.hpp @@ -1,16 +1,17 @@ #ifndef _GAME_RENDERING_INTERFACE_H #define _GAME_RENDERING_INTERFACE_H -namespace MWRender{ - class Objects; - class Actors; - class Player; + +namespace MWRender +{ + class Objects; + class Actors; -class RenderingInterface{ + class RenderingInterface + { public: virtual MWRender::Objects& getObjects() = 0; - virtual MWRender::Player& getPlayer() = 0; virtual MWRender::Actors& getActors() = 0; virtual ~RenderingInterface(){}; }; } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 9eb08d77d..afab7416f 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -183,10 +183,6 @@ MWRender::Actors& RenderingManager::getActors(){ return mActors; } -MWRender::Player& RenderingManager::getPlayer(){ - return (*mPlayer); -} - OEngine::Render::Fader* RenderingManager::getFader() { return mRendering.getFader(); @@ -251,9 +247,31 @@ void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale){ } -void RenderingManager::rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation){ +bool +RenderingManager::rotateObject( + const MWWorld::Ptr &ptr, + Ogre::Vector3 &rot, + bool adjust) +{ + if (ptr.getRefData().getHandle() == "player") { + if (adjust) { + return mPlayer->adjustRotation(rot); + } else { + return mPlayer->setRotation(rot); + } + } + MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); + + Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Z); + + ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); + + return true; } + void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store){ } @@ -770,4 +788,9 @@ void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned i } } +void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) +{ + mPlayer->attachTo(ptr); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 12cbee9b8..bedd66c46 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -56,11 +56,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine); virtual ~RenderingManager(); - - - virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as - /// MWWorld::Player has been rewritten to not need access - /// to internal details of the rendering system anymore + void attachCameraTo(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); Compositors* getCompositors(); @@ -89,7 +85,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); - void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation); + bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); void setWaterHeight(const float height); void toggleWater(); diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index c6332d9f3..e4af847d5 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -492,11 +492,13 @@ namespace MWSound startRandomTitle(); const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->cell; - Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); +// Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); Ogre::Vector3 nPos, nDir, nUp; + /* nPos = cam->getRealPosition(); nDir = cam->getRealDirection(); nUp = cam->getRealUp(); + */ Environment env = Env_Normal; if((cell->data.flags&cell->HasWater) && nPos.y < cell->water) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 54e5a625f..0f05ae24d 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -6,8 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwrender/player.hpp" - #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" @@ -15,8 +13,8 @@ namespace MWWorld { - Player::Player (MWRender::Player *renderer, const ESM::NPC *player, const MWBase::World& world) : - mCellStore (0), mRenderer (renderer), mClass (0), + Player::Player (const ESM::NPC *player, const MWBase::World& world) : + mCellStore (0), mClass (0), mAutoMove (false), mForwardBackward (0) { mPlayer.base = player; @@ -28,7 +26,6 @@ namespace MWWorld float* playerPos = mPlayer.mData.getPosition().pos; playerPos[0] = playerPos[1] = playerPos[2] = 0; - mPlayer.mData.setBaseNode(renderer->getNode()); /// \todo Do not make a copy of classes defined in esm/p records. mClass = new ESM::Class (*world.getStore().classes.find (player->cls)); } @@ -38,11 +35,6 @@ namespace MWWorld delete mClass; } - void Player::setRot(float x, float y, float z) - { - mRenderer->setRot(x, y, z); - } - void Player::setClass (const ESM::Class& class_) { ESM::Class *new_class = new ESM::Class (class_); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index d2058dea6..68df2ec6d 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -14,11 +14,6 @@ namespace MWBase class World; } -namespace MWRender -{ - class Player; -} - namespace MWWorld { class CellStore; @@ -28,7 +23,6 @@ namespace MWWorld { LiveCellRef mPlayer; MWWorld::CellStore *mCellStore; - MWRender::Player *mRenderer; std::string mName; bool mMale; std::string mRace; @@ -38,13 +32,10 @@ namespace MWWorld int mForwardBackward; public: - Player(MWRender::Player *renderer, const ESM::NPC *player, const MWBase::World& world); + Player(const ESM::NPC *player, const MWBase::World& world); ~Player(); - /// Set where the player is looking at. Uses Morrowind (euler) angles - void setRot(float x, float y, float z); - void setCell (MWWorld::CellStore *cellStore) { mCellStore = cellStore; @@ -56,8 +47,6 @@ namespace MWWorld return ptr; } - MWRender::Player *getRenderer() { return mRenderer; } - void setName (const std::string& name) { mName = name; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c768fce26..1fa77f6f6 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -149,7 +149,7 @@ namespace MWWorld if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); - MWBase::Environment::get().getWorld()->getPlayer().setRot (pos.rot[0], pos.rot[1], pos.rot[2]); + world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); } world->getPlayer().setCell(cell); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8fb7b1ba7..3cb1e6e32 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -189,8 +189,9 @@ namespace MWWorld mEsm.open (masterPath.string()); mStore.load (mEsm); - MWRender::Player* play = &(mRendering->getPlayer()); - mPlayer = new MWWorld::Player (play, mStore.npcs.find ("player"), *this); + mPlayer = new MWWorld::Player (mStore.npcs.find ("player"), *this); + mRendering->attachCameraTo(mPlayer->getPlayer()); + mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); // global variables @@ -598,19 +599,20 @@ namespace MWWorld mPhysics->scaleObject( ptr.getRefData().getHandle(), scale ); } - void World::rotateObject (const Ptr& ptr,float x,float y,float z) + void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { - MWWorld::Class::get(ptr).adjustRotation(ptr,x,y,z); + Ogre::Vector3 rot(x, y, z); + if (mRendering->rotateObject(ptr, rot, adjust)) { + float *objRot = ptr.getRefData().getPosition().rot; + objRot[0] = Ogre::Degree(rot.x).valueRadians(); + objRot[1] = Ogre::Degree(rot.y).valueRadians(); + objRot[2] = Ogre::Degree(rot.z).valueRadians(); - ptr.getRefData().getPosition().rot[0] = Ogre::Degree(x).valueRadians(); - ptr.getRefData().getPosition().rot[1] = Ogre::Degree(y).valueRadians(); - ptr.getRefData().getPosition().rot[2] = Ogre::Degree(z).valueRadians(); - - Ogre::Quaternion rotx(Ogre::Degree(-x),Ogre::Vector3::UNIT_X); - Ogre::Quaternion roty(Ogre::Degree(-y),Ogre::Vector3::UNIT_Y); - Ogre::Quaternion rotz(Ogre::Degree(-z),Ogre::Vector3::UNIT_Z); - ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); - mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); + mPhysics->rotateObject( + ptr.getRefData().getHandle(), + ptr.getRefData().getBaseNode()->getOrientation() + ); + } } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f80b88be2..e2570b97c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -209,7 +209,7 @@ namespace MWWorld virtual void scaleObject (const Ptr& ptr, float scale); - virtual void rotateObject (const Ptr& ptr,float x,float y,float z); + virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; From a2d87d5f5b55939735a11e21bf2b0831f251a183 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 10:24:18 +0400 Subject: [PATCH 353/688] added camera flip control, fixed input rotation axis --- apps/openmw/mwinput/mouselookevent.cpp | 2 +- apps/openmw/mwrender/player.cpp | 32 +++++++++++++++++++++++--- apps/openmw/mwrender/player.hpp | 2 ++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index 4138c481c..f318ce666 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -24,5 +24,5 @@ void MouseLookEvent::event(Type type, int index, const void *p) float y = arg->state.Y.rel * sensY; MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index a71d9da68..0d101a839 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -24,14 +24,16 @@ namespace MWRender // we are only interested in X and Y rotation // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); pitchNode->setOrientation(xr); yawNode->setOrientation(yr); + controlFlip(); + return !mVanityModeEnabled; } @@ -51,8 +53,32 @@ namespace MWRender Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); pitchNode->pitch(Ogre::Degree(rot.x)); - yawNode->yaw(Ogre::Degree(rot.z)); + yawNode->yaw(Ogre::Degree(-rot.z)); + + controlFlip(); return !mVanityModeEnabled; } + + void Player::controlFlip() + { + Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); + Ogre::Quaternion orient = pitchNode->getOrientation(); + + float pitchAngle = + (2 * Ogre::Degree(Ogre::Math::ACos(orient.w)).valueDegrees()); + + // Limit the pitch between -90 degress and +90 degrees, Quake3-style. + if (pitchAngle > 90.0f) + { + Ogre::Real sqrt = Ogre::Math::Sqrt(0.5f); + if (orient.x > 0) { + // Set orientation to 90 degrees on X-axis. + pitchNode->setOrientation(Ogre::Quaternion(sqrt, sqrt, 0, 0)); + } else if (orient.x < 0) { + // Sets orientation to -90 degrees on X-axis. + pitchNode->setOrientation(Ogre::Quaternion(sqrt, -sqrt, 0, 0)); + } + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index a145997a9..35333c9f1 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -27,6 +27,8 @@ namespace MWRender bool mFirstPersonView; bool mVanityModeEnabled; + void controlFlip(); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); From 1511eb3549264c4573baccca19a5f5446046018b Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 10:55:49 +0400 Subject: [PATCH 354/688] implemented enable/disable player looking switch --- apps/openmw/mwinput/inputmanager.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index bbd80e8c0..ea279cfdf 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -424,9 +424,10 @@ private: } else { - // Start mouse-looking again. TODO: This should also allow - // for other ways to disable mouselook, like paralyzation. - mouse->enable(); + // Start mouse-looking again if allowed. + if (mControlSwitch["playerlooking"]) { + mouse->enable(); + } // Disable GUI events guiEvents->enabled = false; @@ -439,13 +440,19 @@ private: return; } /// \note 7 switches at all, if-else is relevant - if (sw == "playercontrols") { + if (sw == "playercontrols" && !value) { player.setLeftRight(0); player.setForwardBackward(0); player.setAutoMove(false); - } else if (sw == "playerjumping") { + } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time player.setUpDown(0); + } else if (sw == "playerlooking") { + if (value) { + mouse->enable(); + } else { + mouse->disable(); + } } mControlSwitch[sw] = value; } From 378fcc246078a3273180b91e5574c3ec8e821e43 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 11:10:18 +0400 Subject: [PATCH 355/688] fix DisablePlayerControl logic --- apps/openmw/mwinput/inputmanager.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index ea279cfdf..4281612f0 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -399,14 +399,14 @@ private: } else player.setForwardBackward (0); - } - if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) - player.setUpDown (1); - else if (poller.isDown(A_Crouch)) - player.setUpDown (-1); - else - player.setUpDown (0); + if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) + player.setUpDown (1); + else if (poller.isDown(A_Crouch)) + player.setUpDown (-1); + else + player.setUpDown (0); + } } // Switch between gui modes. Besides controlling the Gui windows @@ -444,6 +444,7 @@ private: player.setLeftRight(0); player.setForwardBackward(0); player.setAutoMove(false); + player.setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time player.setUpDown(0); From e6ede480c74a1f4a0d34c92b2a634a610d33fd1a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 09:41:17 +0200 Subject: [PATCH 356/688] Issue #107: fixed up some interfaces --- apps/openmw/mwdialogue/dialoguemanager.cpp | 18 +++++++++--------- apps/openmw/mwdialogue/dialoguemanager.hpp | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 3fde7ad6c..7c23ecebb 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -599,12 +599,12 @@ namespace MWDialogue } } - void DialogueManager::addTopic(std::string topic) + void DialogueManager::addTopic (const std::string& topic) { mKnownTopics[toLower(topic)] = true; } - void DialogueManager::parseText(std::string text) + void DialogueManager::parseText (std::string text) { std::list::iterator it; for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it) @@ -804,7 +804,7 @@ namespace MWDialogue mChoice = choice; } - void DialogueManager::keywordSelected(std::string keyword) + void DialogueManager::keywordSelected (const std::string& keyword) { if(!mIsInChoice) { @@ -846,11 +846,11 @@ namespace MWDialogue MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); } - void DialogueManager::questionAnswered(std::string answere) + void DialogueManager::questionAnswered (const std::string& answer) { - if(mChoiceMap.find(answere) != mChoiceMap.end()) + if(mChoiceMap.find(answer) != mChoiceMap.end()) { - mChoice = mChoiceMap[answere]; + mChoice = mChoiceMap[answer]; std::vector::const_iterator iter; if(mDialogueMap.find(mLastTopic) != mDialogueMap.end()) @@ -882,13 +882,13 @@ namespace MWDialogue } } - void DialogueManager::printError(std::string error) + void DialogueManager::printError (std::string error) { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->addText(error); } - void DialogueManager::askQuestion(std::string question, int choice) + void DialogueManager::askQuestion (const std::string& question, int choice) { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->askQuestion(question); @@ -896,7 +896,7 @@ namespace MWDialogue mIsInChoice = true; } - std::string DialogueManager::getFaction() + std::string DialogueManager::getFaction() const { if (mActor.getTypeName() != typeid(ESM::NPC).name()) return ""; diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanager.hpp index d139ddc01..f28b5647c 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.hpp +++ b/apps/openmw/mwdialogue/dialoguemanager.hpp @@ -52,19 +52,19 @@ namespace MWDialogue void startDialogue (const MWWorld::Ptr& actor); - void addTopic(std::string topic); + void addTopic (const std::string& topic); - void askQuestion(std::string question,int choice); + void askQuestion (const std::string& question,int choice); void goodbye(); ///get the faction of the actor you are talking with - std::string getFaction(); + std::string getFaction() const; //calbacks for the GUI - void keywordSelected(std::string keyword); + void keywordSelected (const std::string& keyword); void goodbyeSelected(); - void questionAnswered(std::string answere); + void questionAnswered (const std::string& answer); }; } From 923109b2607be6c203e4caa4a912ad98ef5c04b4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 10:35:53 +0200 Subject: [PATCH 357/688] Issue #107: DialogueManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/dialoguemanager.hpp | 45 +++++++++++++++++++ apps/openmw/mwbase/environment.cpp | 6 +-- apps/openmw/mwbase/environment.hpp | 8 ++-- ...oguemanager.cpp => dialoguemanagerimp.cpp} | 2 +- ...oguemanager.hpp => dialoguemanagerimp.hpp} | 25 ++++++----- apps/openmw/mwgui/dialogue.cpp | 3 +- apps/openmw/mwscript/dialogueextensions.cpp | 6 ++- apps/openmw/mwscript/statsextensions.cpp | 2 +- apps/openmw/mwworld/actiontalk.cpp | 3 +- 11 files changed, 78 insertions(+), 28 deletions(-) create mode 100644 apps/openmw/mwbase/dialoguemanager.hpp rename apps/openmw/mwdialogue/{dialoguemanager.cpp => dialoguemanagerimp.cpp} (99%) rename apps/openmw/mwdialogue/{dialoguemanager.hpp => dialoguemanagerimp.hpp} (71%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 768054ed7..b9da636cf 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -32,7 +32,7 @@ add_openmw_dir (mwgui ) add_openmw_dir (mwdialogue - dialoguemanager journal journalentry quest topic + dialoguemanagerimp journal journalentry quest topic ) add_openmw_dir (mwscript @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world scriptmanager + environment world scriptmanager dialoguemanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d4fa078ae..d97d40580 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -29,7 +29,7 @@ #include "mwclass/classes.hpp" -#include "mwdialogue/dialoguemanager.hpp" +#include "mwdialogue/dialoguemanagerimp.hpp" #include "mwdialogue/journal.hpp" #include "mwmechanics/mechanicsmanager.hpp" diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp new file mode 100644 index 000000000..ca339e81e --- /dev/null +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_MWBASE_DIALOGUEMANAGERIMP_H +#define GAME_MWBASE_DIALOGUEMANAGERIMP_H + +#include + +namespace MWWorld +{ + class Ptr; +} + +namespace MWBase +{ + class DialogueManager + { + DialogueManager (const DialogueManager&); + ///< not implemented + + DialogueManager& operator= (const DialogueManager&); + ///< not implemented + + public: + + DialogueManager() {} + + virtual ~DialogueManager() {} + + virtual void startDialogue (const MWWorld::Ptr& actor) = 0; + + virtual void addTopic (const std::string& topic) = 0; + + virtual void askQuestion (const std::string& question,int choice) = 0; + + virtual void goodbye() = 0; + + ///get the faction of the actor you are talking with + virtual std::string getFaction() const = 0; + + //calbacks for the GUI + virtual void keywordSelected (const std::string& keyword) = 0; + virtual void goodbyeSelected() = 0; + virtual void questionAnswered (const std::string& answer) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index e01500003..c0fc56f97 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -7,13 +7,13 @@ #include "../mwsound/soundmanager.hpp" -#include "../mwdialogue/dialoguemanager.hpp" #include "../mwdialogue/journal.hpp" #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" #include "scriptmanager.hpp" +#include "dialoguemanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -56,7 +56,7 @@ void MWBase::Environment::setMechanicsManager (MWMechanics::MechanicsManager *me mMechanicsManager = mechanicsManager; } -void MWBase::Environment::setDialogueManager (MWDialogue::DialogueManager *dialogueManager) +void MWBase::Environment::setDialogueManager (DialogueManager *dialogueManager) { mDialogueManager = dialogueManager; } @@ -106,7 +106,7 @@ MWMechanics::MechanicsManager *MWBase::Environment::getMechanicsManager() const return mMechanicsManager; } -MWDialogue::DialogueManager *MWBase::Environment::getDialogueManager() const +MWBase::DialogueManager *MWBase::Environment::getDialogueManager() const { assert (mDialogueManager); return mDialogueManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 50d9e3940..72d92f00f 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -18,7 +18,6 @@ namespace MWMechanics namespace MWDialogue { - class DialogueManager; class Journal; } @@ -31,6 +30,7 @@ namespace MWBase { class World; class ScriptManager; + class DialogueManager; /// \brief Central hub for mw-subsystems /// @@ -47,7 +47,7 @@ namespace MWBase ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; - MWDialogue::DialogueManager *mDialogueManager; + DialogueManager *mDialogueManager; MWDialogue::Journal *mJournal; MWInput::MWInputManager *mInputManager; float mFrameDuration; @@ -74,7 +74,7 @@ namespace MWBase void setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager); - void setDialogueManager (MWDialogue::DialogueManager *dialogueManager); + void setDialogueManager (DialogueManager *dialogueManager); void setJournal (MWDialogue::Journal *journal); @@ -93,7 +93,7 @@ namespace MWBase MWMechanics::MechanicsManager *getMechanicsManager() const; - MWDialogue::DialogueManager *getDialogueManager() const; + DialogueManager *getDialogueManager() const; MWDialogue::Journal *getJournal() const; diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp similarity index 99% rename from apps/openmw/mwdialogue/dialoguemanager.cpp rename to apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 7c23ecebb..129e78d0f 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -1,5 +1,5 @@ -#include "dialoguemanager.hpp" +#include "dialoguemanagerimp.hpp" #include #include diff --git a/apps/openmw/mwdialogue/dialoguemanager.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp similarity index 71% rename from apps/openmw/mwdialogue/dialoguemanager.hpp rename to apps/openmw/mwdialogue/dialoguemanagerimp.hpp index f28b5647c..69cc88f25 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MMDIALOG_DIALOGUEMANAGER_H -#define GAME_MWDIALOG_DIALOGUEMANAGER_H +#ifndef GAME_MMDIALOG_DIALOGUEMANAGERIMP_H +#define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #include @@ -8,12 +8,15 @@ #include "../mwscript/interpretercontext.hpp" #include +#include "../mwbase/dialoguemanager.hpp" + #include "../mwworld/ptr.hpp" + #include namespace MWDialogue { - class DialogueManager + class DialogueManager : public MWBase::DialogueManager { bool isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo::SelectStruct& select) const; @@ -50,21 +53,21 @@ namespace MWDialogue DialogueManager (const Compiler::Extensions& extensions); - void startDialogue (const MWWorld::Ptr& actor); + virtual void startDialogue (const MWWorld::Ptr& actor); - void addTopic (const std::string& topic); + virtual void addTopic (const std::string& topic); - void askQuestion (const std::string& question,int choice); + virtual void askQuestion (const std::string& question,int choice); - void goodbye(); + virtual void goodbye(); ///get the faction of the actor you are talking with - std::string getFaction() const; + virtual std::string getFaction() const; //calbacks for the GUI - void keywordSelected (const std::string& keyword); - void goodbyeSelected(); - void questionAnswered (const std::string& answer); + virtual void keywordSelected (const std::string& keyword); + virtual void goodbyeSelected(); + virtual void questionAnswered (const std::string& answer); }; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 760c89f49..30089dd46 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -9,7 +9,8 @@ #include #include "../mwbase/environment.hpp" -#include "../mwdialogue/dialoguemanager.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/world.hpp" #include "dialogue_history.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index ec8ab59b4..4eb129331 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -7,8 +7,10 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/dialoguemanager.hpp" + #include "../mwdialogue/journal.hpp" -#include "../mwdialogue/dialoguemanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -84,7 +86,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { - MWDialogue::DialogueManager* dialogue = MWBase::Environment::get().getDialogueManager(); + MWBase::DialogueManager* dialogue = MWBase::Environment::get().getDialogueManager(); while(arg0>0) { std::string question = runtime.getStringLiteral (runtime[0].mInteger); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 0cbbe8398..5113c9dbf 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -16,6 +16,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -26,7 +27,6 @@ #include "interpretercontext.hpp" #include "ref.hpp" -#include "../mwdialogue/dialoguemanager.hpp" namespace MWScript { diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index 78171bbe5..d94cb67f4 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -2,8 +2,7 @@ #include "actiontalk.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" -#include "../mwdialogue/dialoguemanager.hpp" +#include "../mwbase/dialoguemanager.hpp" namespace MWWorld { From df60f4bf92c80da2fd1265de34de6c4f50de7736 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 13:27:32 +0400 Subject: [PATCH 358/688] stub for soundmanager, adjust rotation mode --- apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwsound/soundmanager.cpp | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index afab7416f..be8b518b9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -263,6 +263,11 @@ RenderingManager::rotateObject( } MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); + if (adjust) { + float *f = ptr.getRefData().getPosition().rot; + rot.x += f[0], rot.y += f[1], rot.z += f[2]; + } + Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); Ogre::Quaternion zr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Z); diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index e4af847d5..cb54a70bd 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -13,8 +13,6 @@ #include "../mwworld/player.hpp" -#include "../mwrender/player.hpp" - #include "sound_output.hpp" #include "sound_decoder.hpp" #include "sound.hpp" @@ -491,9 +489,17 @@ namespace MWSound if(!isMusicPlaying()) startRandomTitle(); - const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->cell; + MWWorld::Ptr player = + MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const ESM::Cell *cell = player.getCell()->cell; // Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); - Ogre::Vector3 nPos, nDir, nUp; + Ogre::Vector3 pos, at, up = Ogre::Vector3::UNIT_Z; + + float *fval = player.getRefData().getPosition().pos; + pos.x = fval[0], pos.y = fval[1], pos.z = fval[2]; + + fval = player.getRefData().getPosition().rot; + at.x = fval[0], at.y = fval[1], at.z = fval[2]; /* nPos = cam->getRealPosition(); nDir = cam->getRealDirection(); @@ -501,15 +507,17 @@ namespace MWSound */ Environment env = Env_Normal; - if((cell->data.flags&cell->HasWater) && nPos.y < cell->water) + if((cell->data.flags&cell->HasWater) && pos.z < cell->water) env = Env_Underwater; // 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. + /* 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, env); From 6b996d8c34f9ea0bcaee8c8fd08a6bca51bf5516 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 13:29:13 +0400 Subject: [PATCH 359/688] fix disabled camera on start --- apps/openmw/mwinput/inputmanager.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 4281612f0..35aafa446 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -295,6 +295,14 @@ private: lst->add(guiEvents,Event::EV_ALL); } + mControlSwitch["playercontrols"] = true; + mControlSwitch["playerfighting"] = true; + mControlSwitch["playerjumping"] = true; + mControlSwitch["playerlooking"] = true; + mControlSwitch["playermagic"] = true; + mControlSwitch["playerviewswitch"] = true; + mControlSwitch["vanitymode"] = true; + changeInputMode(false); /********************************** @@ -338,14 +346,6 @@ private: poller.bind(A_Jump, KC_E); poller.bind(A_Crouch, KC_LCONTROL); - - mControlSwitch["playercontrols"] = true; - mControlSwitch["playerfighting"] = true; - mControlSwitch["playerjumping"] = true; - mControlSwitch["playerlooking"] = true; - mControlSwitch["playermagic"] = true; - mControlSwitch["playerviewswitch"] = true; - mControlSwitch["vanitymode"] = true; } void setDragDrop(bool dragDrop) From 181d45661f904b198ff8d206c9571eb5c3c6b444 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 13:43:52 +0400 Subject: [PATCH 360/688] fix rotation angles --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index be8b518b9..9e48cc0eb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -269,8 +269,8 @@ RenderingManager::rotateObject( } Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion yr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Degree(rot.z), Ogre::Vector3::UNIT_Z); ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); From a84145a0872ec4b15c4ff08c6ba8432525e24f34 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 12:05:47 +0200 Subject: [PATCH 361/688] Issue #107: minor corrections --- apps/openmw/mwbase/dialoguemanager.hpp | 5 +++-- apps/openmw/mwbase/scriptmanager.hpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index ca339e81e..ccffc6b21 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWBASE_DIALOGUEMANAGERIMP_H -#define GAME_MWBASE_DIALOGUEMANAGERIMP_H +#ifndef GAME_MWBASE_DIALOGUEMANAGER_H +#define GAME_MWBASE_DIALOGUEMANAGER_H #include @@ -10,6 +10,7 @@ namespace MWWorld namespace MWBase { + /// \brief Interface for dialogue manager (implemented in MWDialogue) class DialogueManager { DialogueManager (const DialogueManager&); diff --git a/apps/openmw/mwbase/scriptmanager.hpp b/apps/openmw/mwbase/scriptmanager.hpp index a8db868c3..ae146e064 100644 --- a/apps/openmw/mwbase/scriptmanager.hpp +++ b/apps/openmw/mwbase/scriptmanager.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWBASE_SCRIPTMANAGERIMP_H -#define GAME_MWBASE_SCRIPTMANAGERIMP_H +#ifndef GAME_MWBASE_SCRIPTMANAGER_H +#define GAME_MWBASE_SCRIPTMANAGER_H #include From d00d40cc3f567d008f980c83aa447718f08ed8d9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 12:56:03 +0200 Subject: [PATCH 362/688] Issue #107: Journal is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +- apps/openmw/mwbase/environment.hpp | 12 +-- apps/openmw/mwbase/journal.hpp | 76 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 +- .../{journal.cpp => journalimp.cpp} | 2 +- .../{journal.hpp => journalimp.hpp} | 37 +++------ apps/openmw/mwgui/journalwindow.cpp | 5 +- apps/openmw/mwscript/dialogueextensions.cpp | 3 +- 10 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 apps/openmw/mwbase/journal.hpp rename apps/openmw/mwdialogue/{journal.cpp => journalimp.cpp} (98%) rename apps/openmw/mwdialogue/{journal.hpp => journalimp.hpp} (52%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b9da636cf..1dffc426d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -32,7 +32,7 @@ add_openmw_dir (mwgui ) add_openmw_dir (mwdialogue - dialoguemanagerimp journal journalentry quest topic + dialoguemanagerimp journalimp journalentry quest topic ) add_openmw_dir (mwscript @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world scriptmanager dialoguemanager + environment world scriptmanager dialoguemanager journal ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d97d40580..5659b1c37 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -30,7 +30,7 @@ #include "mwclass/classes.hpp" #include "mwdialogue/dialoguemanagerimp.hpp" -#include "mwdialogue/journal.hpp" +#include "mwdialogue/journalimp.hpp" #include "mwmechanics/mechanicsmanager.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index c0fc56f97..25488af6c 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -7,13 +7,12 @@ #include "../mwsound/soundmanager.hpp" -#include "../mwdialogue/journal.hpp" - #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" +#include "journal.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -61,7 +60,7 @@ void MWBase::Environment::setDialogueManager (DialogueManager *dialogueManager) mDialogueManager = dialogueManager; } -void MWBase::Environment::setJournal (MWDialogue::Journal *journal) +void MWBase::Environment::setJournal (Journal *journal) { mJournal = journal; } @@ -112,7 +111,7 @@ MWBase::DialogueManager *MWBase::Environment::getDialogueManager() const return mDialogueManager; } -MWDialogue::Journal *MWBase::Environment::getJournal() const +MWBase::Journal *MWBase::Environment::getJournal() const { assert (mJournal); return mJournal; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 72d92f00f..160a63b0b 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -16,11 +16,6 @@ namespace MWMechanics class MechanicsManager; } -namespace MWDialogue -{ - class Journal; -} - namespace MWInput { struct MWInputManager; @@ -31,6 +26,7 @@ namespace MWBase class World; class ScriptManager; class DialogueManager; + class Journal; /// \brief Central hub for mw-subsystems /// @@ -48,7 +44,7 @@ namespace MWBase MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; - MWDialogue::Journal *mJournal; + Journal *mJournal; MWInput::MWInputManager *mInputManager; float mFrameDuration; @@ -76,7 +72,7 @@ namespace MWBase void setDialogueManager (DialogueManager *dialogueManager); - void setJournal (MWDialogue::Journal *journal); + void setJournal (Journal *journal); void setInputManager (MWInput::MWInputManager *inputManager); @@ -95,7 +91,7 @@ namespace MWBase DialogueManager *getDialogueManager() const; - MWDialogue::Journal *getJournal() const; + Journal *getJournal() const; MWInput::MWInputManager *getInputManager() const; diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp new file mode 100644 index 000000000..99f6996cf --- /dev/null +++ b/apps/openmw/mwbase/journal.hpp @@ -0,0 +1,76 @@ +#ifndef GAME_MWBASE_JOURNAL_H +#define GAME_MWBASE_JOURNAL_H + +#include +#include +#include + +namespace MWDialogue +{ + class Quest; + class Topic; + struct StampedJournalEntry; +} + +namespace MWBase +{ + /// \brief Interface for the player's journal (implemented in MWDialogue) + class Journal + { + Journal (const Journal&); + ///< not implemented + + Journal& operator= (const Journal&); + ///< not implemented + + public: + + typedef std::deque TEntryContainer; + typedef TEntryContainer::const_iterator TEntryIter; + typedef std::map TQuestContainer; // topc, quest + typedef TQuestContainer::const_iterator TQuestIter; + typedef std::map TTopicContainer; // topic-id, topic-content + typedef TTopicContainer::const_iterator TTopicIter; + + public: + + Journal() {} + + virtual ~Journal() {} + + virtual void addEntry (const std::string& id, int index) = 0; + ///< Add a journal entry. + + virtual void setJournalIndex (const std::string& id, int index) = 0; + ///< Set the journal index without adding an entry. + + virtual int getJournalIndex (const std::string& id) const = 0; + ///< Get the journal index. + + virtual void addTopic (const std::string& topicId, const std::string& infoId) = 0; + + virtual TEntryIter begin() const = 0; + ///< Iterator pointing to the begin of the main journal. + /// + /// \note Iterators to main journal entries will never become invalid. + + virtual TEntryIter end() const = 0; + ///< Iterator pointing past the end of the main journal. + + virtual TQuestIter questBegin() const = 0; + ///< Iterator pointing to the first quest (sorted by topic ID) + + virtual TQuestIter questEnd() const = 0; + ///< Iterator pointing past the last quest. + + virtual TTopicIter topicBegin() const = 0; + ///< Iterator pointing to the first topic (sorted by topic ID) + /// + /// \note The topic ID is identical with the user-visible topic string. + + virtual TTopicIter topicEnd() const = 0; + ///< Iterator pointing past the last topic. + }; +} + +#endif diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 129e78d0f..644da49e2 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -12,6 +12,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/journal.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -22,8 +23,6 @@ #include "../mwgui/dialogue.hpp" #include "../mwgui/window_manager.hpp" -#include "journal.hpp" - #include #include "../mwscript/extensions.hpp" diff --git a/apps/openmw/mwdialogue/journal.cpp b/apps/openmw/mwdialogue/journalimp.cpp similarity index 98% rename from apps/openmw/mwdialogue/journal.cpp rename to apps/openmw/mwdialogue/journalimp.cpp index ad10be3aa..052b92194 100644 --- a/apps/openmw/mwdialogue/journal.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -1,5 +1,5 @@ -#include "journal.hpp" +#include "journalimp.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwdialogue/journal.hpp b/apps/openmw/mwdialogue/journalimp.hpp similarity index 52% rename from apps/openmw/mwdialogue/journal.hpp rename to apps/openmw/mwdialogue/journalimp.hpp index 62b9f4bed..5fafc265a 100644 --- a/apps/openmw/mwdialogue/journal.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -1,9 +1,7 @@ #ifndef GAME_MMDIALOG_JOURNAL_H #define GAME_MWDIALOG_JOURNAL_H -#include -#include -#include +#include "../mwbase/journal.hpp" #include "journalentry.hpp" #include "quest.hpp" @@ -11,19 +9,8 @@ namespace MWDialogue { /// \brief The player's journal - class Journal + class Journal : public MWBase::Journal { - public: - - typedef std::deque TEntryContainer; - typedef TEntryContainer::const_iterator TEntryIter; - typedef std::map TQuestContainer; // topc, quest - typedef TQuestContainer::const_iterator TQuestIter; - typedef std::map TTopicContainer; // topic-id, topic-content - typedef TTopicContainer::const_iterator TTopicIter; - - private: - TEntryContainer mJournal; TQuestContainer mQuests; TTopicContainer mTopics; @@ -34,37 +21,37 @@ namespace MWDialogue Journal(); - void addEntry (const std::string& id, int index); + virtual void addEntry (const std::string& id, int index); ///< Add a journal entry. - void setJournalIndex (const std::string& id, int index); + virtual void setJournalIndex (const std::string& id, int index); ///< Set the journal index without adding an entry. - int getJournalIndex (const std::string& id) const; + virtual int getJournalIndex (const std::string& id) const; ///< Get the journal index. - void addTopic (const std::string& topicId, const std::string& infoId); + virtual void addTopic (const std::string& topicId, const std::string& infoId); - TEntryIter begin() const; + virtual TEntryIter begin() const; ///< Iterator pointing to the begin of the main journal. /// /// \note Iterators to main journal entries will never become invalid. - TEntryIter end() const; + virtual TEntryIter end() const; ///< Iterator pointing past the end of the main journal. - TQuestIter questBegin() const; + virtual TQuestIter questBegin() const; ///< Iterator pointing to the first quest (sorted by topic ID) - TQuestIter questEnd() const; + virtual TQuestIter questEnd() const; ///< Iterator pointing past the last quest. - TTopicIter topicBegin() const; + virtual TTopicIter topicBegin() const; ///< Iterator pointing to the first topic (sorted by topic ID) /// /// \note The topic ID is identical with the user-visible topic string. - TTopicIter topicEnd() const; + virtual TTopicIter topicEnd() const; ///< Iterator pointing past the last topic. }; } diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 8b50c9ef1..220cd96e7 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -2,11 +2,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwdialogue/journal.hpp" +#include "../mwbase/journal.hpp" #include "../mwsound/soundmanager.hpp" +#include "../mwdialogue/journalentry.hpp" + #include "window_manager.hpp" namespace diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 4eb129331..21c21acf0 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -9,8 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" - -#include "../mwdialogue/journal.hpp" +#include "../mwbase/journal.hpp" #include "interpretercontext.hpp" #include "ref.hpp" From 6bd48d12af439c621bbbfb38d5f8eef8aa9d5bb5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 9 Aug 2012 14:33:21 +0200 Subject: [PATCH 363/688] Issue #107: SoundManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +- apps/openmw/mwbase/environment.hpp | 12 +- apps/openmw/mwbase/soundmanager.hpp | 122 ++++++++++++++++++ apps/openmw/mwclass/apparatus.cpp | 7 +- apps/openmw/mwclass/armor.cpp | 7 +- apps/openmw/mwclass/book.cpp | 5 +- apps/openmw/mwclass/clothing.cpp | 7 +- apps/openmw/mwclass/container.cpp | 5 +- apps/openmw/mwclass/door.cpp | 3 +- apps/openmw/mwclass/ingredient.cpp | 7 +- apps/openmw/mwclass/light.cpp | 7 +- apps/openmw/mwclass/lockpick.cpp | 7 +- apps/openmw/mwclass/misc.cpp | 9 +- apps/openmw/mwclass/potion.cpp | 5 +- apps/openmw/mwclass/probe.cpp | 8 +- apps/openmw/mwclass/repair.cpp | 7 +- apps/openmw/mwclass/weapon.cpp | 7 +- apps/openmw/mwgui/alchemywindow.cpp | 3 +- apps/openmw/mwgui/bookwindow.cpp | 6 +- apps/openmw/mwgui/charactercreation.cpp | 2 +- apps/openmw/mwgui/container.cpp | 3 +- apps/openmw/mwgui/hud.cpp | 3 +- apps/openmw/mwgui/inventorywindow.cpp | 5 +- apps/openmw/mwgui/journalwindow.cpp | 3 +- apps/openmw/mwgui/scrollwindow.cpp | 6 +- apps/openmw/mwgui/settingswindow.cpp | 3 +- apps/openmw/mwgui/spellwindow.cpp | 3 +- apps/openmw/mwgui/tradewindow.cpp | 2 +- apps/openmw/mwscript/soundextensions.cpp | 7 +- apps/openmw/mwsound/openal_output.cpp | 26 ++-- apps/openmw/mwsound/openal_output.hpp | 6 +- apps/openmw/mwsound/sound.hpp | 4 +- apps/openmw/mwsound/sound_output.hpp | 8 +- .../{soundmanager.cpp => soundmanagerimp.cpp} | 14 +- .../{soundmanager.hpp => soundmanagerimp.hpp} | 61 ++++----- apps/openmw/mwworld/action.cpp | 5 +- apps/openmw/mwworld/scene.cpp | 5 +- apps/openmw/mwworld/weather.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 3 +- 41 files changed, 249 insertions(+), 170 deletions(-) create mode 100644 apps/openmw/mwbase/soundmanager.hpp rename apps/openmw/mwsound/{soundmanager.cpp => soundmanagerimp.cpp} (97%) rename apps/openmw/mwsound/{soundmanager.hpp => soundmanagerimp.hpp} (58%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 1dffc426d..b35b2fbad 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -43,7 +43,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwsound - soundmanager openal_output audiere_decoder mpgsnd_decoder ffmpeg_decoder + soundmanagerimp openal_output audiere_decoder mpgsnd_decoder ffmpeg_decoder ) add_openmw_dir (mwworld @@ -64,7 +64,7 @@ add_openmw_dir (mwmechanics ) add_openmw_dir (mwbase - environment world scriptmanager dialoguemanager journal + environment world scriptmanager dialoguemanager journal soundmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5659b1c37..2467f91d1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -21,7 +21,7 @@ #include "mwscript/scriptmanagerimp.hpp" #include "mwscript/extensions.hpp" -#include "mwsound/soundmanager.hpp" +#include "mwsound/soundmanagerimp.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 25488af6c..7d109b000 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -5,14 +5,13 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwmechanics/mechanicsmanager.hpp" #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" +#include "soundmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -35,7 +34,7 @@ void MWBase::Environment::setWorld (World *world) mWorld = world; } -void MWBase::Environment::setSoundManager (MWSound::SoundManager *soundManager) +void MWBase::Environment::setSoundManager (SoundManager *soundManager) { mSoundManager = soundManager; } @@ -81,7 +80,7 @@ MWBase::World *MWBase::Environment::getWorld() const return mWorld; } -MWSound::SoundManager *MWBase::Environment::getSoundManager() const +MWBase::SoundManager *MWBase::Environment::getSoundManager() const { assert (mSoundManager); return mSoundManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 160a63b0b..d03267c25 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,11 +1,6 @@ #ifndef GAME_BASE_INVIRONMENT_H #define GAME_BASE_INVIRONMENT_H -namespace MWSound -{ - class SoundManager; -} - namespace MWGui { class WindowManager; @@ -27,6 +22,7 @@ namespace MWBase class ScriptManager; class DialogueManager; class Journal; + class SoundManager; /// \brief Central hub for mw-subsystems /// @@ -39,7 +35,7 @@ namespace MWBase static Environment *sThis; World *mWorld; - MWSound::SoundManager *mSoundManager; + SoundManager *mSoundManager; ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; MWMechanics::MechanicsManager *mMechanicsManager; @@ -62,7 +58,7 @@ namespace MWBase void setWorld (World *world); - void setSoundManager (MWSound::SoundManager *soundManager); + void setSoundManager (SoundManager *soundManager); void setScriptManager (MWBase::ScriptManager *scriptManager); @@ -81,7 +77,7 @@ namespace MWBase World *getWorld() const; - MWSound::SoundManager *getSoundManager() const; + SoundManager *getSoundManager() const; MWBase::ScriptManager *getScriptManager() const; diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp new file mode 100644 index 000000000..49171c70c --- /dev/null +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -0,0 +1,122 @@ +#ifndef GAME_MWBASE_SOUNDMANAGER_H +#define GAME_MWBASE_SOUNDMANAGER_H + +#include + +#include + +#include + +#include "../mwworld/ptr.hpp" + +namespace MWWorld +{ + class CellStore; +} + +namespace MWSound +{ + class Sound; +} + +namespace MWBase +{ + typedef boost::shared_ptr SoundPtr; + + /// \brief Interface for sound manager (implemented in MWSound) + class SoundManager + { + public: + + 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. */ + }; + + private: + + SoundManager (const SoundManager&); + ///< not implemented + + SoundManager& operator= (const SoundManager&); + ///< not implemented + + public: + + SoundManager() {} + + virtual ~SoundManager() {} + + virtual void processChangedSettings(const Settings::CategorySettingVector& settings) = 0; + + virtual void stopMusic() = 0; + ///< Stops music if it's playing + + virtual void streamMusic(const std::string& filename) = 0; + ///< Play a soundifle + /// \param filename name of a sound file in "Music/" in the data directory. + + virtual void startRandomTitle() = 0; + ///< Starts a random track from the current playlist + + virtual bool isMusicPlaying() = 0; + ///< Returns true if music is playing + + virtual void playPlaylist(const std::string &playlist) = 0; + ///< Start playing music from the selected folder + /// \param name of the folder that contains the playlist + + virtual void say(MWWorld::Ptr reference, const std::string& filename) = 0; + ///< Make an actor say some text. + /// \param filename name of a sound file in "Sound/" in the data directory. + + virtual void say(const std::string& filename) = 0; + ///< Say some text, without an actor ref + /// \param filename name of a sound file in "Sound/" in the data directory. + + virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const = 0; + ///< Is actor not speaking? + + virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; + ///< Stop an actor speaking + + virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, + int mode=Play_Normal) = 0; + ///< Play a sound, independently of 3D-position + + virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, + float volume, float pitch, int mode=Play_Normal) = 0; + ///< Play a sound from an object + + virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0; + ///< Stop the given object from playing the given sound, + + virtual void stopSound3D(MWWorld::Ptr reference) = 0; + ///< Stop the given object from playing all sounds. + + virtual void stopSound(const MWWorld::CellStore *cell) = 0; + ///< Stop all sounds for the given cell. + + virtual void stopSound(const std::string& soundId) = 0; + ///< Stop a non-3d looping sound + + virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; + ///< Is the given sound currently playing on the given object? + + virtual void updateObject(MWWorld::Ptr reference) = 0; + ///< Update the position of all sounds connected to the given object. + + virtual void update(float duration) = 0; + }; + + inline int operator|(SoundManager::PlayMode a, SoundManager::PlayMode b) + { return static_cast (a) | static_cast (b); } + inline int operator&(SoundManager::PlayMode a, SoundManager::PlayMode b) + { return static_cast (a) & static_cast (b); } +} + +#endif diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 9814b140c..3c22cc5fb 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -18,8 +19,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -39,7 +38,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -64,7 +63,7 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 4624b94d9..43c1e0a43 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -21,8 +22,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -42,7 +41,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Armor::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -67,7 +66,7 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 d8166347e..29a53140a 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" @@ -17,8 +18,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,7 +37,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Book::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index f55d6ed88..ef91b0fac 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Clothing::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 c6d22b3a5..7d31b945d 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" @@ -20,8 +21,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace { struct CustomData : public MWWorld::CustomData @@ -69,7 +68,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Container::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 6939c356e..19eda16ef 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index d8c8b4b3b..eb92accb6 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -17,8 +18,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,7 +37,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -63,7 +62,7 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 f09d3ce36..ed8fd1de6 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -17,8 +18,6 @@ #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" @@ -58,7 +57,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, "meshes\\" + model); } if (!ref->base->sound.empty()) { - MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop); + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->base->sound, 1.0, 1.0, MWBase::SoundManager::Play_Loop); } } @@ -95,7 +94,7 @@ namespace MWClass if (!(ref->base->data.flags & ESM::Light::Carry)) return boost::shared_ptr (new MWWorld::NullAction); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 20441b520..1472b7e8b 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 cb6d40c43..8b5b41495 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -20,8 +21,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - #include namespace MWClass @@ -43,7 +42,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -68,7 +67,7 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); @@ -189,7 +188,7 @@ namespace MWClass Miscellaneous::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { MWWorld::Ptr newPtr; - + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index d3f2912ea..27903fdf7 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 450016209..8a563865d 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Probe::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -64,7 +63,7 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); @@ -174,4 +173,3 @@ namespace MWClass return MWWorld::Ptr(&cell.probes.insert(*ref), &cell); } } - diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 829fe311a..096d3d0ee 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -17,8 +18,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,7 +37,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Repair::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -63,7 +62,7 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::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 b45953130..bc8e3e648 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,8 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwsound/soundmanager.hpp" - namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -40,7 +39,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Weapon::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -65,7 +64,7 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); return boost::shared_ptr ( new MWWorld::ActionTake (ptr)); diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 9d691d371..81e5640d4 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -4,13 +4,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwsound/soundmanager.hpp" - #include "window_manager.hpp" namespace diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 1e0301d8e..19a5e9398 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -4,8 +4,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" + #include "../mwinput/inputmanager.hpp" -#include "../mwsound/soundmanager.hpp" + #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" @@ -98,7 +100,7 @@ void BookWindow::onCloseButtonClicked (MyGUI::Widget* _sender) void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) { - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); MWWorld::ActionTake take(mBook); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index c39e305dc..c0081544b 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -9,7 +9,7 @@ #include "mode.hpp" #include "../mwbase/environment.hpp" -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" namespace { diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 44cb12d2d..66dea0849 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" @@ -20,8 +21,6 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index d2eef26ac..1c7943386 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -8,12 +8,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwgui/widgets.hpp" #include "inventorywindow.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 926c35172..090b7dd6e 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -9,6 +9,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -17,10 +18,6 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwsound/soundmanager.hpp" - -#include "../mwclass/container.hpp" - #include "window_manager.hpp" #include "widgets.hpp" #include "bookwindow.hpp" diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 220cd96e7..475a70631 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" - -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwdialogue/journalentry.hpp" diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 00e5a01dc..67a02e53b 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -2,10 +2,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" + #include "../mwinput/inputmanager.hpp" + #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" -#include "../mwsound/soundmanager.hpp" #include "formatting.hpp" #include "window_manager.hpp" @@ -62,7 +64,7 @@ void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender) void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) { - MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); MWWorld::ActionTake take(mScroll); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 4488096fe..9629c7cca 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -12,11 +12,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwinput/inputmanager.hpp" #include "window_manager.hpp" diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 8f81a1761..b528eecc2 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -16,8 +17,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" -#include "../mwsound/soundmanager.hpp" - #include "window_manager.hpp" #include "inventorywindow.hpp" #include "confirmationdialog.hpp" diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index d9f9692ff..75e39fd87 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -4,10 +4,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" -#include "../mwsound/soundmanager.hpp" #include "window_manager.hpp" #include "inventorywindow.hpp" diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 3e05d72d3..6dc8f3919 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -9,8 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -116,7 +115,7 @@ namespace MWScript std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWSound::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : 0); } }; @@ -142,7 +141,7 @@ namespace MWScript Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWSound::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : 0); } }; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index ac4baa8b2..e5169d878 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -8,7 +8,7 @@ #include "openal_output.hpp" #include "sound_decoder.hpp" #include "sound.hpp" -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER 0x1013 @@ -263,7 +263,7 @@ void OpenAL_SoundStream::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; - if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -400,7 +400,7 @@ void OpenAL_Sound::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; - if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -420,7 +420,7 @@ void OpenAL_Sound3D::update() ALfloat pitch = mPitch; if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance) gain = 0.0f; - else if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) + else if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; pitch *= 0.7f; @@ -642,7 +642,7 @@ void OpenAL_Output::bufferFinished(ALuint buf) } -SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -674,7 +674,7 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float alSourcef(src, AL_MAX_DISTANCE, 1000.0f); alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater) + if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) { volume *= 0.9f; pitch *= 0.7f; @@ -683,7 +683,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, (flags&Play_Loop) ? AL_TRUE : AL_FALSE); + alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); throwALerror(); alSourcei(src, AL_BUFFER, buf); @@ -693,7 +693,7 @@ SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float return sound; } -SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, +MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, float min, float max, int flags) { boost::shared_ptr sound; @@ -726,7 +726,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector alSourcef(src, AL_MAX_DISTANCE, max); alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f); - if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater) + if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) { volume *= 0.9f; pitch *= 0.7f; @@ -736,7 +736,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector alSourcef(src, AL_PITCH, pitch); alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE); + alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); throwALerror(); alSourcei(src, AL_BUFFER, buf); @@ -747,7 +747,7 @@ SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector } -SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags) { boost::shared_ptr sound; ALuint src; @@ -759,7 +759,7 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa try { - if((flags&Play_Loop)) + if((flags&MWBase::SoundManager::Play_Loop)) std::cout <<"Warning: cannot loop stream "<open(fname); @@ -779,7 +779,7 @@ SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, floa alSourcef(src, AL_MAX_DISTANCE, 1000.0f); alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater) + if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) { volume *= 0.9f; pitch *= 0.7f; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index d62d20286..90917c53c 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -42,10 +42,10 @@ namespace MWSound virtual void init(const std::string &devname=""); virtual void deinit(); - virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); - virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, + virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); + virtual MWBase::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, int flags); + virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 0cba6abca..729147f75 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -3,7 +3,7 @@ #include -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" namespace MWSound { @@ -35,7 +35,7 @@ namespace MWSound , mPitch(1.0f) , mMinDistance(20.0f) /* 1 * min_range_scale */ , mMaxDistance(12750.0f) /* 255 * max_range_scale */ - , mFlags(Play_Normal) + , mFlags(MWBase::SoundManager::Play_Normal) { } virtual ~Sound() { } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 100b2208c..2680ec1db 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -6,7 +6,7 @@ #include -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" #include "../mwworld/ptr.hpp" @@ -24,10 +24,10 @@ 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, int flags) = 0; - virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, + virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; + virtual MWBase::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, int flags) = 0; + virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp similarity index 97% rename from apps/openmw/mwsound/soundmanager.cpp rename to apps/openmw/mwsound/soundmanagerimp.cpp index c6332d9f3..8a351590d 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "soundmanager.hpp" +#include "soundmanagerimp.hpp" #include #include @@ -222,7 +222,7 @@ namespace MWSound 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, objpos, basevol, 1.0f, + MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, 20.0f, 12750.0f, Play_Normal); sound->mPos = objpos; sound->mBaseVolume = basevol; @@ -244,7 +244,7 @@ namespace MWSound float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); + MWBase::SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); sound->mBaseVolume = basevol; mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); @@ -277,9 +277,9 @@ namespace MWSound - SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) + MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) { - SoundPtr sound; + MWBase::SoundPtr sound; if(!mOutput->isInitialized()) return sound; try @@ -305,10 +305,10 @@ namespace MWSound return sound; } - SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, + MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, float volume, float pitch, int mode) { - SoundPtr sound; + MWBase::SoundPtr sound; if(!mOutput->isInitialized()) return sound; try diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp similarity index 58% rename from apps/openmw/mwsound/soundmanager.hpp rename to apps/openmw/mwsound/soundmanagerimp.hpp index 83195390c..78a4990a8 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -11,8 +11,9 @@ #include -#include "../mwworld/ptr.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwworld/ptr.hpp" namespace Ogre { @@ -27,27 +28,13 @@ namespace MWSound class Sound; 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; } enum Environment { Env_Normal, Env_Underwater, }; - class SoundManager + class SoundManager : public MWBase::SoundManager { Ogre::ResourceGroupManager& mResourceMgr; @@ -65,7 +52,7 @@ namespace MWSound std::string mCurrentPlaylist; typedef std::pair PtrIDPair; - typedef std::map SoundMap; + typedef std::map SoundMap; SoundMap mActiveSounds; std::string lookup(const std::string &soundId, @@ -84,67 +71,67 @@ namespace MWSound public: SoundManager(bool useSound); - ~SoundManager(); + virtual ~SoundManager(); - void processChangedSettings(const Settings::CategorySettingVector& settings); + virtual void processChangedSettings(const Settings::CategorySettingVector& settings); - void stopMusic(); + virtual void stopMusic(); ///< Stops music if it's playing - void streamMusic(const std::string& filename); + virtual void streamMusic(const std::string& filename); ///< Play a soundifle /// \param filename name of a sound file in "Music/" in the data directory. - void startRandomTitle(); + virtual void startRandomTitle(); ///< Starts a random track from the current playlist - bool isMusicPlaying(); + virtual bool isMusicPlaying(); ///< Returns true if music is playing - void playPlaylist(const std::string &playlist); + virtual void playPlaylist(const std::string &playlist); ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - void say(MWWorld::Ptr reference, const std::string& filename); + virtual void say(MWWorld::Ptr reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. - void say(const std::string& filename); + virtual void say(const std::string& filename); ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const; + virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const; ///< Is actor not speaking? - void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); + virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); ///< Stop an actor speaking - SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); + virtual MWBase::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, + virtual MWBase::SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, float volume, float pitch, int mode=Play_Normal); ///< Play a sound from an object - void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); + virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); ///< Stop the given object from playing the given sound, - void stopSound3D(MWWorld::Ptr reference); + virtual void stopSound3D(MWWorld::Ptr reference); ///< Stop the given object from playing all sounds. - void stopSound(const MWWorld::CellStore *cell); + virtual void stopSound(const MWWorld::CellStore *cell); ///< Stop all sounds for the given cell. - void stopSound(const std::string& soundId); + virtual void stopSound(const std::string& soundId); ///< Stop a non-3d looping sound - bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; + virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - void updateObject(MWWorld::Ptr reference); + virtual void updateObject(MWWorld::Ptr reference); ///< Update the position of all sounds connected to the given object. - void update(float duration); + virtual void update(float duration); }; } diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 0a57d5f67..c142294bb 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -2,8 +2,7 @@ #include "action.hpp" #include "../mwbase/environment.hpp" - -#include "../mwsound/soundmanager.hpp" +#include "../mwbase/soundmanager.hpp" MWWorld::Action::Action() {} @@ -13,7 +12,7 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, - MWSound::Play_NoTrack); + MWBase::SoundManager::Play_NoTrack); executeImp (actor); } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c768fce26..6d4996e78 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -2,11 +2,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME +#include "../mwbase/soundmanager.hpp" #include "../mwmechanics/mechanicsmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwgui/window_manager.hpp" #include "player.hpp" @@ -345,7 +344,7 @@ namespace MWWorld mRendering.addObject(ptr); MWWorld::Class::get(ptr).insertObject(ptr, *mPhysics); } - + void Scene::removeObjectFromScene (const Ptr& ptr) { MWBase::Environment::get().getMechanicsManager()->removeActor (ptr); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b5f2e3a57..0adf87dae 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -9,11 +9,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "player.hpp" using namespace Ogre; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8fb7b1ba7..be9c00aee 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -4,14 +4,13 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" #include "../mwmechanics/mechanicsmanager.hpp" -#include "../mwsound/soundmanager.hpp" - #include "../mwgui/window_manager.hpp" #include "player.hpp" From b5bc7bc424b7ed57ffbe0f1383e56da360c0460d Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 9 Aug 2012 17:01:03 +0400 Subject: [PATCH 364/688] SoundManager dependency on camera pos/dir --- apps/openmw/mwrender/player.cpp | 18 +++++++++++++ apps/openmw/mwrender/player.hpp | 1 + apps/openmw/mwsound/soundmanager.cpp | 39 ++++++++++------------------ apps/openmw/mwsound/soundmanager.hpp | 13 +++++----- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 0d101a839..d8a9d4b37 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -3,9 +3,13 @@ #include #include +#include "../mwbase/environment.hpp" + #include "../mwworld/ptr.hpp" #include "../mwworld/refdata.hpp" +#include "../mwsound/soundmanager.hpp" + namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) @@ -33,6 +37,7 @@ namespace MWRender yawNode->setOrientation(yr); controlFlip(); + updateListener(); return !mVanityModeEnabled; } @@ -56,6 +61,7 @@ namespace MWRender yawNode->yaw(Ogre::Degree(-rot.z)); controlFlip(); + updateListener(); return !mVanityModeEnabled; } @@ -81,4 +87,16 @@ namespace MWRender } } } + + void Player::updateListener() + { + Ogre::Vector3 pos = mCamera->getRealPosition(); + Ogre::Vector3 dir = mCamera->getRealDirection(); + + Ogre::Real xch; + xch = pos.y, pos.y = -pos.z, pos.z = xch; + xch = dir.y, dir.y = -dir.z, dir.z = xch; + + MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir); + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 35333c9f1..b1160435d 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -28,6 +28,7 @@ namespace MWRender bool mVanityModeEnabled; void controlFlip(); + void updateListener(); public: diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index cb54a70bd..17ee8c682 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include #include "../mwbase/environment.hpp" @@ -492,34 +490,17 @@ namespace MWSound MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); const ESM::Cell *cell = player.getCell()->cell; -// Ogre::Camera *cam = MWBase::Environment::get().getWorld()->getPlayer().getRenderer()->getCamera(); - Ogre::Vector3 pos, at, up = Ogre::Vector3::UNIT_Z; - - float *fval = player.getRefData().getPosition().pos; - pos.x = fval[0], pos.y = fval[1], pos.z = fval[2]; - - fval = player.getRefData().getPosition().rot; - at.x = fval[0], at.y = fval[1], at.z = fval[2]; - /* - nPos = cam->getRealPosition(); - nDir = cam->getRealDirection(); - nUp = cam->getRealUp(); - */ Environment env = Env_Normal; - if((cell->data.flags&cell->HasWater) && pos.z < cell->water) + if((cell->data.flags&cell->HasWater) && mListenerPos.z < cell->water) env = Env_Underwater; - // 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. - /* - 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, env); + mOutput->updateListener( + mListenerPos, + mListenerDir, + Ogre::Vector3::UNIT_Z, + env + ); // Check if any sounds are finished playing, and trash them SoundMap::iterator snditer = mActiveSounds.begin(); @@ -577,6 +558,12 @@ namespace MWSound } } + void SoundManager::setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir) + { + mListenerPos = pos; + mListenerDir = dir; + } + // Default readAll implementation, for decoders that can't do anything // better void Sound_Decoder::readAll(std::vector &output) diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 83195390c..acf2ddeb6 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -7,19 +7,13 @@ #include +#include #include #include #include "../mwworld/ptr.hpp" - -namespace Ogre -{ - class Root; - class Camera; -} - namespace MWSound { class Sound_Output; @@ -68,6 +62,9 @@ namespace MWSound typedef std::map SoundMap; SoundMap mActiveSounds; + Ogre::Vector3 mListenerPos; + Ogre::Vector3 mListenerDir; + std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void streamMusicFull(const std::string& filename); @@ -145,6 +142,8 @@ namespace MWSound ///< Update the position of all sounds connected to the given object. void update(float duration); + + void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir); }; } From cb5b69104d18be0383c901a86758b49600fcc354 Mon Sep 17 00:00:00 2001 From: gugus Date: Thu, 9 Aug 2012 23:45:06 +0200 Subject: [PATCH 365/688] PositionCell --- .../mwscript/transformationextensions.cpp | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 46cbf781f..f47a1829a 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -196,6 +196,39 @@ namespace MWScript } }; + template + class OpPositionCell : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Float x = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float y = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float z = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float zRot = runtime[0].mFloat; + runtime.pop(); + std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWBase::Environment::get().getWorld()->moveObjectToCell(ptr,cellID,x,y,z); + float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); + if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity + { + ax = ax/60.; + ay = ay/60.; + zRot = zRot/60.; + } + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + } + }; + template class OpPosition : public Interpreter::Opcode0 { @@ -243,6 +276,8 @@ namespace MWScript const int opcodeGetStartingPosExplicit = 0x2000171; const int opcodePosition = 0x2000172; const int opcodePositionExplicit = 0x2000173; + const int opcodePositionCell = 0x2000174; + const int opcodePositionCellExplicit = 0x2000175; void registerExtensions (Compiler::Extensions& extensions) { @@ -254,6 +289,7 @@ namespace MWScript extensions.registerFunction("getpos",'f',"c",opcodeGetPos,opcodeGetPosExplicit); extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit); extensions.registerInstruction("position","ffff",opcodePosition,opcodePositionExplicit); + extensions.registerInstruction("positioncell","ffffS",opcodePositionCell,opcodePositionCellExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -274,6 +310,8 @@ namespace MWScript interpreter.installSegment5(opcodeGetStartingPosExplicit,new OpGetStartingPos); interpreter.installSegment5(opcodePosition,new OpPosition); interpreter.installSegment5(opcodePositionExplicit,new OpPosition); + interpreter.installSegment5(opcodePositionCell,new OpPositionCell); + interpreter.installSegment5(opcodePositionCellExplicit,new OpPositionCell); } } } From 38c2c5d480d7f159ebeb1566628f60dea577ca9a Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Thu, 9 Aug 2012 18:09:11 -0400 Subject: [PATCH 366/688] Creatures now use object physics --- apps/openmw/mwclass/creature.cpp | 2 +- components/files/ogreplugin.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0f3141f5c..620f809e7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -89,7 +89,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()){ - physics.insertActorPhysics(ptr, model); + physics.insertObjectPhysics(ptr, model); } MWBase::Environment::get().getMechanicsManager()->addActor (ptr); } diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index c5292b3a2..2d56bfb47 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -32,7 +32,7 @@ namespace Ogre { class Root; } -#if (BOOST_VERSION <= 104300) +#if (BOOST_VERSION <= 104500) namespace boost { namespace filesystem { inline path absolute(const path& p, const path& base=current_path()) { From 3668b4aabb15f11f75c1adce817a7615b1f51156 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Fri, 10 Aug 2012 05:47:37 +0200 Subject: [PATCH 367/688] Rewrote the credits.txt, added missing people and changed formatting --- credits.txt | 124 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 40 deletions(-) diff --git a/credits.txt b/credits.txt index 5b8ede117..af54e69bd 100644 --- a/credits.txt +++ b/credits.txt @@ -1,60 +1,104 @@ -CREDITS +Contributors + +The OpenMW project was started in 2008 by Nicolay Korslund. +In the course of years many people have contributed to the project. + +If you feel your name is missing from this list, +Feel free to modify it directly in our GitHub Repository, +or ask a developer to do it for you. + + +Programmers: +Marc Zinnschlag (Zini) - Lead Programmer/Project Manager -Current Developers: Aleksandar Jovanov -Alexander “Ace†Olofsson -Artem “greye†Kotsynyak +Alexander Olofsson (Ace) +Artem Kotsynyak (greye) athile BrotherBrick -Cris “Mirceam†Mihalache +Cris Mihalache (Mirceam) +Eli2 gugus / gus -Jacob “Yacoby†Essex -Jannik “scrawl†Heller -Jason “jhooks†Hooks -Karl-Felix “k1ll†Glatzer -Lukasz “lgro†Gromanowski -Marc “Zini†Zinnschlag +Jacob Essex (Yacoby) +Jannik Heller (scrawl) +Jason Hooks (jhooks) +Karl-Felix Glatzer (k1ll) +Lukasz Gromanowski (lgro) Michael Mc Donnell -Michael “werdanith†Papageorgiou -Nikolay “corristo†Kasyanov -Pieter “pvdk†van der Kloet -Roman "Kromgart" Melnik -Sebastian “swick†Wick -Sylvain "Garvek" T. +Michael Papageorgiou (werdanith) +Nikolay Kasyanov (corristo) +Pieter van der Kloet (pvdk) +Roman Melnik (Kromgart) +Sebastian Wick (swick) +Sylvain T. (Garvek) -Retired Developers: + +Packagers: +Alexander Olofsson (Ace) - Windows +BrotherBrick - Ubuntu Linux +edmundo - Gentoo Linux +Kenny Armstrong (artorius) - Fedora Linux +Nikolay Kasyanov (corristo) - Mac OS X +Sandy Carter (bwrsandman) - Arch Linux + + +Public Relations: +ElderTroll - Release Manager +sir_herrbatka - News Writer +WeirdSexy - Podcaster + + +Website: +juanmnzsk8 - Spanish News Writer +Julien Voisin (jvoisin/ap0) - French News Writer +Kingpix - Italian News Writer +Lukasz Gromanowski (lgro) - Website Administrator +Nikolay Kasyanov (corristo) - Russian News Writer +Okulo - Dutch News Writer +penguinroad - Indonesian News Writer +Ryan Sardonic (Wry) - Wiki Editor +sir_herrbatka - Forum Admin/Polish News Writer +spyboot - German News Writer + + +Formula Research: +fragonard +Greendogo +HiPhish +modred11 +Myckel +natirips +Sadler + + +Artwork: +Necrod - OpenMW Logo +raevol - Wordpress Theme + + +Inactive Contributors: Ardekantur Armin Preiml Diggory Hardy Jan Borsodi -Jan-Peter “peppe†Nilsson +Jan-Peter Nilsson (peppe) Josua Grawitter +Lordrea Nicolay Korslund sergoz Star-Demon Yuri Krupenin -PR team and Translators: -Julien (jvoisin/ap0) Voisin -sirherrbatka -ElderTroll -spyboot -corristo -Okulo -penguinroad -Kingpix -Reverser and Research: -natirips -Sadler -fragonard -Greendogo -Myckel -modred11 -HiPhish +Additional Credits: +In this section we would like to thank people not part of OpenMW for their work. -OpenMW: -Thanks to DokterDume for kindly providing us with the Moon and Star logo used as the application icon and project logo. +Thanks to Maxim Nikolaev, +for allowing us to use his excellent Morrowind fan-art on our website and in other places. -Launcher: -Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Files tab. +Thanks to DokterDume, +for kindly providing us with the Moon and Star logo, +used as the application icon and project logo. + +Thanks to Kevin Ryan, +for creating the icon used for the Data Files tab of the OpenMW Launcher. From 815e0d67081ec9dcd5812f3414ca5d67ea2cee99 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 10 Aug 2012 08:45:51 +0200 Subject: [PATCH 368/688] modified the section about missing names; We really don't want people to randomly add themselfs --- credits.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/credits.txt b/credits.txt index af54e69bd..a0be85392 100644 --- a/credits.txt +++ b/credits.txt @@ -4,8 +4,7 @@ The OpenMW project was started in 2008 by Nicolay Korslund. In the course of years many people have contributed to the project. If you feel your name is missing from this list, -Feel free to modify it directly in our GitHub Repository, -or ask a developer to do it for you. +please notify a developer. Programmers: From f72956b9181fc0be981f353f53fb4a82bba6d2d7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Aug 2012 15:15:48 +0200 Subject: [PATCH 369/688] - added a simple main menu (with Return, Options and Exit buttons) - removed OEngine::ExitListener (what a terrible abuse of framelisteners) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwgui/cursorreplace.cpp | 1 + apps/openmw/mwgui/mainmenu.cpp | 75 ++++++++++++++++ apps/openmw/mwgui/mainmenu.hpp | 21 +++-- apps/openmw/mwgui/settingswindow.cpp | 1 + apps/openmw/mwinput/inputmanager.cpp | 34 +++---- apps/openmw/mwworld/worldimp.cpp | 13 ++- apps/openmw/mwworld/worldimp.hpp | 6 ++ files/mygui/CMakeLists.txt | 1 + files/mygui/mainmenu.cfg | 95 ++++++++++++++++++++ files/mygui/openmw_layers.xml | 2 - files/mygui/openmw_mainmenu.layout | 13 +-- files/mygui/openmw_resources.xml | 124 ++++++++++++++++++++++++++ libs/openengine/ogre/exitlistener.hpp | 37 -------- libs/openengine/ogre/fader.hpp | 2 - 17 files changed, 350 insertions(+), 82 deletions(-) create mode 100644 apps/openmw/mwgui/mainmenu.cpp create mode 100644 files/mygui/mainmenu.cfg delete mode 100644 libs/openengine/ogre/exitlistener.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 05aa161e6..172c6a494 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -29,7 +29,7 @@ add_openmw_dir (mwgui dialogue_history window_base stats_window messagebox journalwindow charactercreation map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow - confirmationdialog alchemywindow referenceinterface spellwindow + confirmationdialog alchemywindow referenceinterface spellwindow mainmenu ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2467f91d1..5fc9774d1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -116,7 +116,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) std::cerr << "Error in framelistener: " << e.what() << std::endl; } - return true; + return !MWBase::Environment::get().getWorld()->getExitNow(); } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8b809d399..f08617d4c 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -248,6 +248,9 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) = 0; virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; + + virtual void exitNow() = 0; ///< exit after this frame has ended + virtual bool getExitNow() = 0; ///< @return true if the application should exit }; } diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp index e66f54326..a4b6a100b 100644 --- a/apps/openmw/mwgui/cursorreplace.cpp +++ b/apps/openmw/mwgui/cursorreplace.cpp @@ -16,4 +16,5 @@ CursorReplace::CursorReplace() OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); OEngine::Render::Atlas::createFromFile("atlas1.cfg", "mwgui1", "textures\\"); + OEngine::Render::Atlas::createFromFile("mainmenu.cfg", "mwgui2", "textures\\"); } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp new file mode 100644 index 000000000..ca9ebbc4a --- /dev/null +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -0,0 +1,75 @@ +#include "mainmenu.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" + +namespace MWGui +{ + + MainMenu::MainMenu(int w, int h) + : OEngine::GUI::Layout("openmw_mainmenu.layout") + { + setCoord(0,0,w,h); + + int height = 64 * 3; + + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(w/2 - 64, h/2 - height/2, 128, height), MyGUI::Align::Default); + int curH = 0; + + mReturn = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mReturn->setImageResource ("Menu_Return"); + mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); + curH += 64; + + + /* + mNewGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mNewGame->setImageResource ("Menu_NewGame"); + curH += 64; + + mLoadGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mLoadGame->setImageResource ("Menu_LoadGame"); + curH += 64; + + + mSaveGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mSaveGame->setImageResource ("Menu_SaveGame"); + curH += 64; + */ + + mOptions = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mOptions->setImageResource ("Menu_Options"); + mOptions->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::showOptions); + curH += 64; + + /* + mCredits = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mCredits->setImageResource ("Menu_Credits"); + curH += 64; + */ + + mExitGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mExitGame->setImageResource ("Menu_ExitGame"); + mExitGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::exitGame); + curH += 64; + + } + + void MainMenu::returnToGame(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + } + + void MainMenu::showOptions(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); + } + + void MainMenu::exitGame(MyGUI::Widget* sender) + { + MWBase::Environment::get().getWorld ()->exitNow(); + } + +} diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 06c59396f..5fa2f6943 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -6,11 +6,22 @@ namespace MWGui class MainMenu : public OEngine::GUI::Layout { public: - MainMenu(int w, int h) - : Layout("openmw_mainmenu.layout") - { - setCoord(0,0,w,h); - } + MainMenu(int w, int h); + + private: + MyGUI::Button* mReturn; + MyGUI::Button* mNewGame; + MyGUI::Button* mLoadGame; + MyGUI::Button* mSaveGame; + MyGUI::Button* mOptions; + MyGUI::Button* mCredits; + MyGUI::Button* mExitGame; + + MyGUI::Widget* mButtonBox; + + void returnToGame(MyGUI::Widget* sender); + void showOptions(MyGUI::Widget* sender); + void exitGame(MyGUI::Widget* sender); }; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 9629c7cca..d1e1f7095 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -263,6 +263,7 @@ namespace MWGui dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); dialog->eventCancelClicked.clear(); + dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel); } void SettingsWindow::onResolutionAccept() diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 35aafa446..5a4718ccf 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -5,7 +5,6 @@ #include -#include #include #include "../mwgui/window_manager.hpp" @@ -20,6 +19,7 @@ #include "../engine.hpp" #include "../mwworld/player.hpp" +#include "../mwbase/world.hpp" #include #include @@ -68,8 +68,6 @@ namespace MWInput A_ToggleWeapon, A_ToggleSpell, - A_Settings, // Temporary hotkey - A_LAST // Marker for the last item }; @@ -78,7 +76,6 @@ namespace MWInput { OEngine::Input::DispatcherPtr disp; OEngine::Render::OgreRenderer &ogre; - OEngine::Render::ExitListener exit; Mangle::Input::OISDriver input; OEngine::Input::Poller poller; MouseLookEventPtr mouse; @@ -140,15 +137,6 @@ private: windows.messageBox ("Screenshot saved", empty); } - void showSettings() - { - if (mDragDrop) - return; - - if (!windows.isGuiMode() || windows.getMode() != MWGui::GM_Settings) - windows.pushGuiMode(MWGui::GM_Settings); - } - /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ void toggleInventory() { @@ -222,11 +210,19 @@ private: player.toggleRunning(); } + void toggleMainMenu() + { + if (windows.isGuiMode () && windows.getMode () == MWGui::GM_MainMenu) + windows.removeGuiMode (MWGui::GM_MainMenu); + else + windows.pushGuiMode (MWGui::GM_MainMenu); + } + // Exit program now button (which is disabled in GUI mode) void exitNow() { if(!windows.isGuiMode()) - exit.exitNow(); + MWBase::Environment::get().getWorld()->exitNow(); } public: @@ -236,7 +232,6 @@ private: bool debug, OMW::Engine& engine) : ogre(_ogre), - exit(ogre.getWindow()), input(ogre.getWindow(), !debug), poller(input), player(_player), @@ -273,10 +268,8 @@ private: "Draw Weapon"); disp->funcs.bind(A_ToggleSpell,boost::bind(&InputImpl::toggleSpell,this), "Ready hands"); - disp->funcs.bind(A_Settings, boost::bind(&InputImpl::showSettings, this), - "Show settings window"); - // Add the exit listener - ogre.getRoot()->addFrameListener(&exit); + disp->funcs.bind(A_GameMenu, boost::bind(&InputImpl::toggleMainMenu, this), + "Toggle main menu"); mouse = MouseLookEventPtr(new MouseLookEvent()); @@ -316,7 +309,7 @@ private: // NOTE: These keys do not require constant polling - use in conjuction with variables in loops. disp->bind(A_Quit, KC_Q); - disp->bind(A_Quit, KC_ESCAPE); + disp->bind(A_GameMenu, KC_ESCAPE); disp->bind(A_Screenshot, KC_SYSRQ); disp->bind(A_Inventory, KC_I); disp->bind(A_Console, KC_F1); @@ -327,7 +320,6 @@ private: disp->bind(A_ToggleWalk, KC_C); disp->bind(A_ToggleWeapon,KC_F); disp->bind(A_ToggleSpell,KC_R); - disp->bind(A_Settings, KC_F2); // Key bindings for polled keys // NOTE: These keys are constantly being polled. Only add keys that must be checked each frame. diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf2d21014..cb33b40f2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm), - mNumFacing(0) + mNumFacing(0), mExit(false) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -1147,4 +1147,15 @@ namespace MWWorld } return pos.z < cell.water; } + + void World::exitNow() + { + mExit = true; + } + + bool World::getExitNow() + { + return mExit; + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 24645cb8e..7af71d349 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -84,6 +84,8 @@ namespace MWWorld unsigned long lastTick; Ogre::Timer mTimer; + bool mExit; + int getDaysPerMonth (int month) const; bool moveObjectImp (const Ptr& ptr, float x, float y, float z); @@ -274,6 +276,10 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::Ptr &object); virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); + + virtual void exitNow(); ///< exit after this frame has ended + virtual bool getExitNow(); ///< @return true if the application should exit + }; } diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index ae007f023..3a5430c6f 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -5,6 +5,7 @@ set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) set(MYGUI_FILES atlas1.cfg + mainmenu.cfg bigbars.png black.png core.skin diff --git a/files/mygui/mainmenu.cfg b/files/mygui/mainmenu.cfg new file mode 100644 index 000000000..7aaf8c1c7 --- /dev/null +++ b/files/mygui/mainmenu.cfg @@ -0,0 +1,95 @@ +[settings] + size_x = 512 + size_y = 512 + + +[menu_newgame.dds] + x = 0 + y = 0 + +[menu_newgame_pressed.dds] + x = 128 + y = 0 + +[menu_newgame_over.dds] + x = 256 + y = 0 + + +[menu_loadgame.dds] + x = 384 + y = 0 + +[menu_loadgame_pressed.dds] + x = 0 + y = 64 + +[menu_loadgame_over.dds] + x = 128 + y = 64 + + +[menu_options.dds] + x = 256 + y = 64 + +[menu_options_pressed.dds] + x = 384 + y = 64 + +[menu_options_over.dds] + x = 0 + y = 128 + + +[menu_credits.dds] + x = 128 + y = 128 + +[menu_credits_pressed.dds] + x = 256 + y = 128 + +[menu_credits_over.dds] + x = 384 + y = 128 + + +[menu_exitgame.dds] + x = 0 + y = 192 + +[menu_exitgame_pressed.dds] + x = 128 + y = 192 + +[menu_exitgame_over.dds] + x = 256 + y = 192 + + +[menu_savegame.dds] + x = 384 + y = 192 + +[menu_savegame_pressed.dds] + x = 0 + y = 256 + +[menu_savegame_over.dds] + x = 128 + y = 256 + + +[menu_return.dds] + x = 256 + y = 256 + +[menu_return_pressed.dds] + x = 384 + y = 256 + +[menu_return_over.dds] + x = 0 + y = 320 + diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 56f800ea3..7a8d586d2 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -4,8 +4,6 @@ - - diff --git a/files/mygui/openmw_mainmenu.layout b/files/mygui/openmw_mainmenu.layout index bf17be7f7..4479a121f 100644 --- a/files/mygui/openmw_mainmenu.layout +++ b/files/mygui/openmw_mainmenu.layout @@ -2,16 +2,5 @@ - - - - - - - - - - - - + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml index f1a8b0dfc..83461bbe8 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -1,6 +1,8 @@ + + @@ -44,6 +46,10 @@ + + + + @@ -113,6 +119,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/openengine/ogre/exitlistener.hpp b/libs/openengine/ogre/exitlistener.hpp deleted file mode 100644 index 5a9d1ff68..000000000 --- a/libs/openengine/ogre/exitlistener.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef OENGINE_OGRE_EXITLISTEN_H -#define OENGINE_OGRE_EXITLISTEN_H - -/* - This FrameListener simply exits the rendering loop when the window - is closed. You can also tell it to exit manually by setting the exit - member to true; - */ - -#include -#include - -namespace OEngine { -namespace Render -{ - struct ExitListener : Ogre::FrameListener - { - Ogre::RenderWindow *window; - bool exit; - - ExitListener(Ogre::RenderWindow *wnd) - : window(wnd), exit(false) {} - - bool frameStarted(const Ogre::FrameEvent &evt) - { - if(window->isClosed()) - exit = true; - - return !exit; - } - - // Function equivalent of setting exit=true. Handy when you need a - // delegate to bind to an event. - void exitNow() { exit = true; } - }; -}} -#endif diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp index f76ac51ef..efb12a5d2 100644 --- a/libs/openengine/ogre/fader.hpp +++ b/libs/openengine/ogre/fader.hpp @@ -9,8 +9,6 @@ inspired by http://www.ogre3d.org/tikiwiki/FadeEffectOverlay (heavily adjusted) */ -#include - namespace Ogre { class TextureUnitState; From 44ff31b50a15915809cb007cf269eee70a3426f2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Aug 2012 16:21:53 +0200 Subject: [PATCH 370/688] removed world exit methods --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 3 --- apps/openmw/mwgui/mainmenu.cpp | 5 ++++- apps/openmw/mwinput/inputmanager.cpp | 4 +++- apps/openmw/mwworld/worldimp.cpp | 12 +----------- apps/openmw/mwworld/worldimp.hpp | 3 --- 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 5fc9774d1..2467f91d1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -116,7 +116,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) std::cerr << "Error in framelistener: " << e.what() << std::endl; } - return !MWBase::Environment::get().getWorld()->getExitNow(); + return true; } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f08617d4c..8b809d399 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -248,9 +248,6 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) = 0; virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; - - virtual void exitNow() = 0; ///< exit after this frame has ended - virtual bool getExitNow() = 0; ///< @return true if the application should exit }; } diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index ca9ebbc4a..e2fefd649 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -1,5 +1,8 @@ #include "mainmenu.hpp" +#include + + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -69,7 +72,7 @@ namespace MWGui void MainMenu::exitGame(MyGUI::Widget* sender) { - MWBase::Environment::get().getWorld ()->exitNow(); + Ogre::Root::getSingleton ().queueEndRendering (); } } diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 5a4718ccf..e29e3c268 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -1,5 +1,7 @@ #include "inputmanager.hpp" +#include + #include #include @@ -222,7 +224,7 @@ private: void exitNow() { if(!windows.isGuiMode()) - MWBase::Environment::get().getWorld()->exitNow(); + Ogre::Root::getSingleton().queueEndRendering (); } public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cb33b40f2..d9fbc5b77 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm), - mNumFacing(0), mExit(false) + mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -1148,14 +1148,4 @@ namespace MWWorld return pos.z < cell.water; } - void World::exitNow() - { - mExit = true; - } - - bool World::getExitNow() - { - return mExit; - } - } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7af71d349..f2f221b39 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -277,9 +277,6 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::Ptr &object); virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); - virtual void exitNow(); ///< exit after this frame has ended - virtual bool getExitNow(); ///< @return true if the application should exit - }; } From 11fadf4a07c952b34292cd1e540ec70539370fa5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Aug 2012 16:25:27 +0200 Subject: [PATCH 371/688] forgot something --- apps/openmw/mwworld/worldimp.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index f2f221b39..4e545f718 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -84,8 +84,6 @@ namespace MWWorld unsigned long lastTick; Ogre::Timer mTimer; - bool mExit; - int getDaysPerMonth (int month) const; bool moveObjectImp (const Ptr& ptr, float x, float y, float z); From 56c9c4dbd95270b8b0b98691bd188d961c490815 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 10 Aug 2012 21:03:46 +0400 Subject: [PATCH 372/688] remove some redundant code --- apps/openmw/mwworld/scene.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ec557e35c..40d0a634a 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -140,19 +140,18 @@ namespace MWWorld const ESM::Position& pos, bool adjustPlayerPos) { - MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); - bool hasWater = cell->cell->data.flags & cell->cell->HasWater; mPhysics->setCurrentWater(hasWater, cell->cell->water); MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayer().getPlayer(); + world->getPlayer().setCell(cell); + if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); } - world->getPlayer().setCell(cell); MWMechanics::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); From bed6545e45e9d456eeda474d2a0ac8327e6c0c13 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 10 Aug 2012 21:06:09 +0400 Subject: [PATCH 373/688] still fix assertion fail --- apps/openmw/mwworld/scene.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 40d0a634a..531546a57 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -144,10 +144,10 @@ namespace MWWorld mPhysics->setCurrentWater(hasWater, cell->cell->water); MWBase::World *world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world->getPlayer().getPlayer(); - world->getPlayer().setCell(cell); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); From 0042020385b7303d20490e810e49e9b6f8e97b0c Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 10 Aug 2012 21:24:07 +0400 Subject: [PATCH 374/688] should fix 'north direction' bug not even compiled --- apps/openmw/mwrender/renderingmanager.cpp | 31 ++++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8719557ca..4534fee2c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -254,27 +254,28 @@ RenderingManager::rotateObject( Ogre::Vector3 &rot, bool adjust) { - if (ptr.getRefData().getHandle() == "player") { - if (adjust) { - return mPlayer->adjustRotation(rot); - } else { - return mPlayer->setRotation(rot); - } + bool isPlayer = ptr.getRefData().getHandle() == "player"; + bool force = true; + + if (isPlayer) { + force = (adjust) ? mPlayer->adjustRotation(rot) : mPlayer->setRotation(rot); } MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); - if (adjust) { float *f = ptr.getRefData().getPosition().rot; - rot.x += f[0], rot.y += f[1], rot.z += f[2]; + rot.x += Ogre::Radian(f[0]).valueDegrees(); + rot.y += Ogre::Radian(f[1]).valueDegrees(); + rot.z += Ogre::Radian(f[2]).valueDegrees(); + } + if (!isPlayer) { + Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Degree(rot.z), Ogre::Vector3::UNIT_Z); + + ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); } - Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr(Ogre::Degree(rot.z), Ogre::Vector3::UNIT_Z); - - ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); - - return true; + return force; } void From 225530c69013f458a7eabf543fd3b93ccd0c94ec Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Fri, 10 Aug 2012 23:19:12 +0400 Subject: [PATCH 375/688] implemented better main loop for OS X (carbon version). Input feels far less laggy --- libs/openengine/ogre/renderer.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 70bbf7940..3096e2e6e 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -17,6 +17,10 @@ #include #include +#if defined(__APPLE__) && !__LP64__ +#include +#endif + using namespace Ogre; using namespace OEngine::Render; @@ -31,7 +35,33 @@ void OgreRenderer::cleanup() void OgreRenderer::start() { +#if defined(__APPLE__) && !defined(__LP64__) + bool quit = false; + // OSX Carbon Message Pump + do { + EventRef event = NULL; + EventTargetRef targetWindow; + targetWindow = GetEventDispatcherTarget(); + + // If we are unable to get the target then we no longer care about events. + if (!targetWindow) return; + + // Grab the next event while possible + while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &event) == noErr) + { + // Dispatch the event + SendEventToEventTarget(event, targetWindow); + ReleaseEvent(event); + } + + if (!Ogre::Root::getSingleton().renderOneFrame()) { + quit = true; + } + + } while (!quit); +#else mRoot->startRendering(); +#endif } bool OgreRenderer::loadPlugins() const From ebf1fe415e667cd94e1227788f8138cde47cf952 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 11 Aug 2012 01:11:52 +0400 Subject: [PATCH 376/688] tiny fix --- libs/openengine/ogre/renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 3096e2e6e..4074a1a99 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -17,7 +17,7 @@ #include #include -#if defined(__APPLE__) && !__LP64__ +#if defined(__APPLE__) && !defined(__LP64__) #include #endif From 7fa1dc93d7837b86ecbdc176468475af28df5d4b Mon Sep 17 00:00:00 2001 From: gugus Date: Sat, 11 Aug 2012 09:52:49 +0200 Subject: [PATCH 377/688] 2nd try: position/ PositionCell --- .../mwscript/transformationextensions.cpp | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 50897278f..5d9de6baf 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -249,16 +249,29 @@ namespace MWScript std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getWorld()->moveObjectToCell(ptr,cellID,x,y,z); - float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); - if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity + + MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getInterior(cellID); + if(!store) { - ax = ax/60.; - ay = ay/60.; - zRot = zRot/60.; + ESM::Cell cell = MWBase::Environment::get().getWorld()->getExterior(cellID); + if(cell) + { + store = MWBase::Environment::get().getWorld()->getExterior(cell.getGridX(),cell.getGridY()); + } + } + if(store) + { + MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z); + float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); + if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity + { + ax = ax/60.; + ay = ay/60.; + zRot = zRot/60.; + } + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); } - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); } }; @@ -280,7 +293,8 @@ namespace MWScript Interpreter::Type_Float zRot = runtime[0].mFloat; runtime.pop(); - MWBase::Environment::get().getWorld()->moveObject(ptr,x,y,z); + MWBase::Environment::get().getWorld()->moveObject(ptr, + MWBase::Environment::get().getWorld()->getExterior(x,y),x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity From 5018db3332ad1c7b70bb595714031e031e723c68 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 12:13:16 +0400 Subject: [PATCH 378/688] removed some redundant code, added some comments --- apps/openmw/mwrender/player.hpp | 8 ++++++++ apps/openmw/mwrender/renderingmanager.hpp | 4 ++++ apps/openmw/mwworld/scene.cpp | 5 ++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index b1160435d..e5361e64b 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -28,6 +28,8 @@ namespace MWRender bool mVanityModeEnabled; void controlFlip(); + + /// Updates sound manager listener data void updateListener(); public: @@ -35,11 +37,17 @@ namespace MWRender Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); /// Set where the player is looking at. Uses Morrowind (euler) angles + /// \return true if player object needs to bo rotated physically bool setRotation(const Ogre::Vector3 &rot); + + /// \return true if player object needs to bo rotated physically bool adjustRotation(const Ogre::Vector3 &rot); std::string getHandle() const; + /// Attach camera to object + /// \note there is no protection from attaching the same camera to + /// several different objects void attachTo(const MWWorld::Ptr &); void toggleViewMode() { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 197c1fda0..c0558b714 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -85,6 +85,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); + + /// Rotates object accordingly to its type + /// \param adjust indicates should rotation be set or adjusted + /// \return true if object needs to be rotated physically bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); void setWaterHeight(const float height); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ec557e35c..531546a57 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -140,19 +140,18 @@ namespace MWWorld const ESM::Position& pos, bool adjustPlayerPos) { - MWBase::Environment::get().getWorld()->getPlayer().setCell (cell); - bool hasWater = cell->cell->data.flags & cell->cell->HasWater; mPhysics->setCurrentWater(hasWater, cell->cell->water); MWBase::World *world = MWBase::Environment::get().getWorld(); + world->getPlayer().setCell(cell); + MWWorld::Ptr player = world->getPlayer().getPlayer(); if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); } - world->getPlayer().setCell(cell); MWMechanics::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); From 45306e4bc3b98bf2eb29fe1c8761c1c49ea12f77 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 13:23:54 +0400 Subject: [PATCH 379/688] fixed rotation adjustment --- apps/openmw/mwrender/player.cpp | 8 ++++---- apps/openmw/mwrender/player.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 24 +++++++++++++---------- apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/scene.cpp | 6 +++++- apps/openmw/mwworld/worldimp.cpp | 10 ++++++---- apps/openmw/mwworld/worldimp.hpp | 2 ++ 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8b27adbe3..f05ea80ce 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -27,10 +27,10 @@ namespace MWRender // we are only interested in X and Y rotation // Rotate around X axis - Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Degree(-rot.z), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); pitchNode->setOrientation(xr); yawNode->setOrientation(yr); @@ -56,8 +56,8 @@ namespace MWRender Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - pitchNode->pitch(Ogre::Degree(rot.x)); - yawNode->yaw(Ogre::Degree(-rot.z)); + pitchNode->pitch(Ogre::Radian(rot.x)); + yawNode->yaw(Ogre::Radian(-rot.z)); controlFlip(); updateListener(); diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index e5361e64b..a19e72ab0 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -37,9 +37,11 @@ namespace MWRender Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); /// Set where the player is looking at. Uses Morrowind (euler) angles + /// \param rot Rotation angles in radians /// \return true if player object needs to bo rotated physically bool setRotation(const Ogre::Vector3 &rot); + /// \param rot Rotation angles in radians /// \return true if player object needs to bo rotated physically bool adjustRotation(const Ogre::Vector3 &rot); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8719557ca..7180fea66 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -254,27 +254,31 @@ RenderingManager::rotateObject( Ogre::Vector3 &rot, bool adjust) { - if (ptr.getRefData().getHandle() == "player") { + bool isPlayer = ptr.getRefData().getHandle() == "player"; + bool force = true; + + if (isPlayer) { if (adjust) { - return mPlayer->adjustRotation(rot); + force = mPlayer->adjustRotation(rot); } else { - return mPlayer->setRotation(rot); + force = mPlayer->setRotation(rot); } } MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); if (adjust) { + /// \note Stored and passed in radians float *f = ptr.getRefData().getPosition().rot; rot.x += f[0], rot.y += f[1], rot.z += f[2]; } + if (!isPlayer) { + Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Radian(rot.y), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Radian(rot.z), Ogre::Vector3::UNIT_Z); - Ogre::Quaternion xr(Ogre::Degree(rot.x), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Degree(rot.y), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr(Ogre::Degree(rot.z), Ogre::Vector3::UNIT_Z); - - ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); - - return true; + ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); + } + return force; } void diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index c0558b714..ef6f18a75 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -87,6 +87,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); /// Rotates object accordingly to its type + /// \param rot euler angles in radians /// \param adjust indicates should rotation be set or adjusted /// \return true if object needs to be rotated physically bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 531546a57..e02e10188 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -150,7 +150,11 @@ namespace MWWorld if (adjustPlayerPos) { world->moveObject(player, pos.pos[0], pos.pos[1], pos.pos[2]); - world->rotateObject(player, pos.rot[0], pos.rot[1], pos.rot[2]); + + float x = Ogre::Radian(pos.rot[0]).valueDegrees(); + float y = Ogre::Radian(pos.rot[1]).valueDegrees(); + float z = Ogre::Radian(pos.rot[2]).valueDegrees(); + world->rotateObject(player, x, y, z); } MWMechanics::MechanicsManager *mechMgr = diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf2d21014..605858eaf 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -629,12 +629,14 @@ namespace MWWorld void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) { - Ogre::Vector3 rot(x, y, z); + Ogre::Vector3 rot; + rot.x = Ogre::Degree(x).valueRadians(); + rot.y = Ogre::Degree(y).valueRadians(); + rot.z = Ogre::Degree(z).valueRadians(); + if (mRendering->rotateObject(ptr, rot, adjust)) { float *objRot = ptr.getRefData().getPosition().rot; - objRot[0] = Ogre::Degree(rot.x).valueRadians(); - objRot[1] = Ogre::Degree(rot.y).valueRadians(); - objRot[2] = Ogre::Degree(rot.z).valueRadians(); + objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; mPhysics->rotateObject( ptr.getRefData().getHandle(), diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 24645cb8e..c3b0ed898 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -210,6 +210,8 @@ namespace MWWorld virtual void scaleObject (const Ptr& ptr, float scale); + /// Rotates object, uses degrees + /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) From 6eb9f15b6db30e3a42bd03461132f207f5e8a1fe Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 11:38:26 +0200 Subject: [PATCH 380/688] fixed time stamp operator --- apps/openmw/mwworld/timestamp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/timestamp.cpp b/apps/openmw/mwworld/timestamp.cpp index e2f3d6c63..126d5490c 100644 --- a/apps/openmw/mwworld/timestamp.cpp +++ b/apps/openmw/mwworld/timestamp.cpp @@ -76,12 +76,12 @@ namespace MWWorld TimeStamp operator+ (const TimeStamp& stamp, double hours) { - return TimeStamp (stamp) + hours; + return TimeStamp (stamp) += hours; } TimeStamp operator+ (double hours, const TimeStamp& stamp) { - return TimeStamp (stamp) + hours; + return TimeStamp (stamp) += hours; } double operator- (const TimeStamp& left, const TimeStamp& right) From b53b27533a8043a3d8a753d7673494e5b18c6ade Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 12:02:51 +0200 Subject: [PATCH 381/688] fixes some include guards --- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 +- apps/openmw/mwdialogue/journalentry.hpp | 4 ++-- apps/openmw/mwdialogue/journalimp.hpp | 2 +- apps/openmw/mwdialogue/quest.hpp | 2 +- apps/openmw/mwdialogue/topic.hpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 69cc88f25..985d25573 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_DIALOGUEMANAGERIMP_H +#ifndef GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #include diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 9a3270439..3e8640185 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MMDIALOGUE_JOURNALENTRY_H -#define GAME_MMDIALOGUE_JOURNALENTRY_H +#ifndef GAME_MWDIALOGUE_JOURNALENTRY_H +#define GAME_MWDIALOGUE_JOURNALENTRY_H #include diff --git a/apps/openmw/mwdialogue/journalimp.hpp b/apps/openmw/mwdialogue/journalimp.hpp index 5fafc265a..7d71fd7d0 100644 --- a/apps/openmw/mwdialogue/journalimp.hpp +++ b/apps/openmw/mwdialogue/journalimp.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_JOURNAL_H +#ifndef GAME_MWDIALOG_JOURNAL_H #define GAME_MWDIALOG_JOURNAL_H #include "../mwbase/journal.hpp" diff --git a/apps/openmw/mwdialogue/quest.hpp b/apps/openmw/mwdialogue/quest.hpp index 5fcc894ea..3afa81fac 100644 --- a/apps/openmw/mwdialogue/quest.hpp +++ b/apps/openmw/mwdialogue/quest.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_QUEST_H +#ifndef GAME_MWDIALOG_QUEST_H #define GAME_MWDIALOG_QUEST_H #include "topic.hpp" diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index 3ad15903f..566f60ab0 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -1,4 +1,4 @@ -#ifndef GAME_MMDIALOG_TOPIC_H +#ifndef GAME_MWDIALOG_TOPIC_H #define GAME_MWDIALOG_TOPIC_H #include From f3edea77b2805b4cc74886881cbff282bada293b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 12:03:22 +0200 Subject: [PATCH 382/688] MSCV compatibility fix --- apps/openmw/mwbase/journal.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 99f6996cf..b3dfea45b 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -5,12 +5,9 @@ #include #include -namespace MWDialogue -{ - class Quest; - class Topic; - struct StampedJournalEntry; -} +#include "../mwdialogue/journalentry.hpp" +#include "../mwdialogue/topic.hpp" +#include "../mwdialogue/quest.hpp" namespace MWBase { From 355ca649c2ca5cfd3d82b7ff0d904c532ef87e81 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 14:54:33 +0400 Subject: [PATCH 383/688] camera flip control rewritten --- apps/openmw/mwrender/player.cpp | 40 +++++++++++++++++++-------------- apps/openmw/mwrender/player.hpp | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index f05ea80ce..94a3f71c8 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -27,7 +27,13 @@ namespace MWRender // we are only interested in X and Y rotation // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); + Ogre::Radian radx(rot.x); + if (radx.valueDegrees() > 89.5f) { + radx = Ogre::Degree(89.5f); + } else if (radx.valueDegrees() < -89.5f) { + radx = Ogre::Degree(-89.5f); + } + Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); // Rotate around Y axis Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); @@ -35,7 +41,6 @@ namespace MWRender pitchNode->setOrientation(xr); yawNode->setOrientation(yr); - controlFlip(); updateListener(); return !mVanityModeEnabled; @@ -56,35 +61,36 @@ namespace MWRender Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - pitchNode->pitch(Ogre::Radian(rot.x)); + float f = controlFlip(Ogre::Radian(rot.x).valueDegrees()); + if (f != 0.0) { + pitchNode->pitch(Ogre::Degree(f)); + } yawNode->yaw(Ogre::Radian(-rot.z)); - controlFlip(); updateListener(); return !mVanityModeEnabled; } - void Player::controlFlip() + float Player::controlFlip(float shift) { Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::Quaternion orient = pitchNode->getOrientation(); float pitchAngle = - (2 * Ogre::Degree(Ogre::Math::ACos(orient.w)).valueDegrees()); + (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - // Limit the pitch between -90 degress and +90 degrees, Quake3-style. - if (pitchAngle > 90.0f) - { - Ogre::Real sqrt = Ogre::Math::Sqrt(0.5f); - if (orient.x > 0) { - // Set orientation to 90 degrees on X-axis. - pitchNode->setOrientation(Ogre::Quaternion(sqrt, sqrt, 0, 0)); - } else if (orient.x < 0) { - // Sets orientation to -90 degrees on X-axis. - pitchNode->setOrientation(Ogre::Quaternion(sqrt, -sqrt, 0, 0)); - } + if (pitchAngle + shift < 89.5f && pitchAngle + shift > -89.5f) { + return shift; } + if (pitchAngle > 0) { + float f = 89.5f - pitchAngle - shift; + return (f > 0.f) ? f : 0.f; + } else if (pitchAngle < 0) { + float f = -89.5 - pitchAngle - shift; + return (f < 0.f) ? f : 0.f; + } + return 0.f; } void Player::updateListener() diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index a19e72ab0..981ecfe0b 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -27,7 +27,7 @@ namespace MWRender bool mFirstPersonView; bool mVanityModeEnabled; - void controlFlip(); + float controlFlip(float shift = 0.f); /// Updates sound manager listener data void updateListener(); From eaf6a8df94822a6bb2b58b44b2b2341bba87ace2 Mon Sep 17 00:00:00 2001 From: gugus Date: Sat, 11 Aug 2012 14:06:58 +0200 Subject: [PATCH 384/688] some correction --- apps/openmw/mwscript/transformationextensions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 5d9de6baf..8576ac22e 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -253,15 +253,15 @@ namespace MWScript MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getInterior(cellID); if(!store) { - ESM::Cell cell = MWBase::Environment::get().getWorld()->getExterior(cellID); + const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); if(cell) { - store = MWBase::Environment::get().getWorld()->getExterior(cell.getGridX(),cell.getGridY()); + store = MWBase::Environment::get().getWorld()->getExterior(cell->getGridX(),cell->getGridY()); } } if(store) { - MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z); + MWBase::Environment::get().getWorld()->moveObject(ptr,*store,x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity @@ -294,7 +294,7 @@ namespace MWScript runtime.pop(); MWBase::Environment::get().getWorld()->moveObject(ptr, - MWBase::Environment::get().getWorld()->getExterior(x,y),x,y,z); + *MWBase::Environment::get().getWorld()->getExterior(x,y),x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity From 8ac2d11f5626a1779f10bda70eb712ded7cd33cb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Aug 2012 14:42:14 +0200 Subject: [PATCH 385/688] changed shading to be more like MW (completely replace material ambient with vertex colors if present), fixes some too dark daedric ruins --- files/materials/objects.shader | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/files/materials/objects.shader b/files/materials/objects.shader index dba239c14..8e5cbf76e 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -116,8 +116,9 @@ shInput(float3, normalPassthrough) shInput(float3, objSpacePositionPassthrough) shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - //shUniform(float, passIteration) @shAutoConstant(passIteration, pass_iteration_number) + #if !HAS_VERTEXCOLOR shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) + #endif shUniform(float4, materialDiffuse) @shAutoConstant(materialDiffuse, surface_diffuse_colour) shUniform(float4, materialEmissive) @shAutoConstant(materialEmissive, surface_emissive_colour) @shForeach(@shGlobalSettingString(num_lights)) @@ -178,8 +179,13 @@ float3 lightDir; float3 diffuse = float3(0,0,0); float d; + +#if HAS_VERTEXCOLOR + // ambient vertex colour tracking, FFP behaviour + float3 ambient = colourPassthrough.xyz * lightAmbient.xyz; +#else float3 ambient = materialAmbient.xyz * lightAmbient.xyz; - +#endif // shadows only for the first (directional) light #if SHADOWS @@ -237,10 +243,6 @@ #endif @shEndForeach - -#if HAS_VERTEXCOLOR - ambient *= colourPassthrough.xyz; -#endif shOutputColour(0).xyz *= (ambient + diffuse + materialEmissive.xyz); #endif @@ -293,6 +295,7 @@ #if MRT shOutputColour(1) = float4(depthPassthrough / far,1,1,1); #endif + } #endif From 47f7a5e988a32b823476d26dd7c384789bb82487 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 17:01:55 +0400 Subject: [PATCH 386/688] fix double pos/rot adjustment --- apps/openmw/mwworld/worldimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 605858eaf..33eebc307 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -558,7 +558,9 @@ namespace MWWorld if (!newCell.isExterior()) { changeToInteriorCell(newCell.cell->name, pos); } else { - changeToExteriorCell(pos); + int cellX = newCell.cell->data.gridX; + int cellY = newCell.cell->data.gridY; + mWorldScene->changeCell(cellX, cellY, pos, false); } } else { if (!mWorldScene->isCellActive(newCell)) { From 2ebf4721d1cc607e0bd1be5ea3b9812dff76ae15 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Aug 2012 15:15:09 +0200 Subject: [PATCH 387/688] small main menu fix --- apps/openmw/mwinput/inputmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index e29e3c268..2f70d14fa 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -214,8 +214,8 @@ private: void toggleMainMenu() { - if (windows.isGuiMode () && windows.getMode () == MWGui::GM_MainMenu) - windows.removeGuiMode (MWGui::GM_MainMenu); + if (windows.isGuiMode () && (windows.getMode () == MWGui::GM_MainMenu || windows.getMode () == MWGui::GM_Settings)) + windows.popGuiMode(); else windows.pushGuiMode (MWGui::GM_MainMenu); } From f7f8ac0a730b7c4e508869edf5ec047ac5b6a57d Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 11 Aug 2012 18:28:37 +0400 Subject: [PATCH 388/688] fix move/rotateObject code --- apps/openmw/mwrender/renderingmanager.cpp | 5 +++-- apps/openmw/mwworld/worldimp.cpp | 14 ++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7180fea66..521521875 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -254,7 +254,8 @@ RenderingManager::rotateObject( Ogre::Vector3 &rot, bool adjust) { - bool isPlayer = ptr.getRefData().getHandle() == "player"; + bool isActive = ptr.getRefData().getBaseNode() != 0; + bool isPlayer = isActive && ptr.getRefData().getHandle() == "player"; bool force = true; if (isPlayer) { @@ -271,7 +272,7 @@ RenderingManager::rotateObject( float *f = ptr.getRefData().getPosition().rot; rot.x += f[0], rot.y += f[1], rot.z += f[2]; } - if (!isPlayer) { + if (!isPlayer && isActive) { Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); Ogre::Quaternion yr(Ogre::Radian(rot.y), Ogre::Vector3::UNIT_Y); Ogre::Quaternion zr(Ogre::Radian(rot.z), Ogre::Vector3::UNIT_Z); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8b0ed206d..79150564e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -563,9 +563,9 @@ namespace MWWorld mWorldScene->changeCell(cellX, cellY, pos, false); } } else { - if (!mWorldScene->isCellActive(newCell)) { + if (!mWorldScene->isCellActive(*currCell)) { copyObjectToCell(ptr, newCell, pos); - } else if (!mWorldScene->isCellActive(*currCell)) { + } else if (!mWorldScene->isCellActive(newCell)) { MWWorld::Class::get(ptr).copyToCell(ptr, newCell); mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); @@ -640,10 +640,12 @@ namespace MWWorld float *objRot = ptr.getRefData().getPosition().rot; objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; - mPhysics->rotateObject( - ptr.getRefData().getHandle(), - ptr.getRefData().getBaseNode()->getOrientation() - ); + if (ptr.getRefData().getBaseNode() != 0) { + mPhysics->rotateObject( + ptr.getRefData().getHandle(), + ptr.getRefData().getBaseNode()->getOrientation() + ); + } } } From dcf1e6645623b7dda09b658c65dfb8984f01d502 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 11 Aug 2012 17:26:01 +0200 Subject: [PATCH 389/688] fix main menu placement after resolution changes --- apps/openmw/mwgui/mainmenu.cpp | 10 +++++++++- apps/openmw/mwgui/mainmenu.hpp | 2 ++ apps/openmw/mwgui/window_manager.cpp | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e2fefd649..7618ecb15 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -13,11 +13,20 @@ namespace MWGui MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") + , mButtonBox(0) + { + onResChange(w,h); + } + + void MainMenu::onResChange(int w, int h) { setCoord(0,0,w,h); int height = 64 * 3; + if (mButtonBox) + MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(w/2 - 64, h/2 - height/2, 128, height), MyGUI::Align::Default); int curH = 0; @@ -57,7 +66,6 @@ namespace MWGui mExitGame->setImageResource ("Menu_ExitGame"); mExitGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::exitGame); curH += 64; - } void MainMenu::returnToGame(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 5fa2f6943..fd583d187 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -8,6 +8,8 @@ namespace MWGui public: MainMenu(int w, int h); + void onResChange(int w, int h); + private: MyGUI::Button* mReturn; MyGUI::Button* mNewGame; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 659af0447..db7600da9 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -639,6 +639,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector int y = Settings::Manager::getInt("resolution y", "Video"); mHud->onResChange(x, y); mConsole->onResChange(x, y); + mMenu->onResChange(x, y); mSettingsWindow->center(); mAlchemyWindow->center(); mScrollWindow->center(); From b68f9d6a2861ab9fe8e9a3185e64e83086d16070 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 17:30:55 +0200 Subject: [PATCH 390/688] Issue #107: MechanicsManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 +- apps/openmw/mwbase/environment.hpp | 14 ++-- apps/openmw/mwbase/mechanicsmanager.hpp | 77 +++++++++++++++++++ apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 +- apps/openmw/mwgui/charactercreation.cpp | 1 + apps/openmw/mwgui/charactercreation.hpp | 1 - apps/openmw/mwgui/stats_window.cpp | 3 +- apps/openmw/mwgui/window_manager.cpp | 2 +- ...icsmanager.cpp => mechanicsmanagerimp.cpp} | 2 +- ...icsmanager.hpp => mechanicsmanagerimp.hpp} | 35 ++++----- apps/openmw/mwworld/scene.cpp | 5 +- apps/openmw/mwworld/worldimp.cpp | 9 +-- 16 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 apps/openmw/mwbase/mechanicsmanager.hpp rename apps/openmw/mwmechanics/{mechanicsmanager.cpp => mechanicsmanagerimp.cpp} (99%) rename apps/openmw/mwmechanics/{mechanicsmanager.hpp => mechanicsmanagerimp.hpp} (60%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 172c6a494..f4454a216 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -60,12 +60,12 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanager stat creaturestats magiceffects movement actors drawstate spells + mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells activespells npcstats ) add_openmw_dir (mwbase - environment world scriptmanager dialoguemanager journal soundmanager + environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2467f91d1..98fee4384 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -32,7 +32,7 @@ #include "mwdialogue/dialoguemanagerimp.hpp" #include "mwdialogue/journalimp.hpp" -#include "mwmechanics/mechanicsmanager.hpp" +#include "mwmechanics/mechanicsmanagerimp.hpp" void OMW::Engine::executeLocalScripts() diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 7d109b000..f75c3588f 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -5,13 +5,12 @@ #include "../mwinput/inputmanager.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" - #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" #include "soundmanager.hpp" +#include "mechanicsmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -49,7 +48,7 @@ void MWBase::Environment::setWindowManager (MWGui::WindowManager *windowManager) mWindowManager = windowManager; } -void MWBase::Environment::setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager) +void MWBase::Environment::setMechanicsManager (MechanicsManager *mechanicsManager) { mMechanicsManager = mechanicsManager; } @@ -98,7 +97,7 @@ MWGui::WindowManager *MWBase::Environment::getWindowManager() const return mWindowManager; } -MWMechanics::MechanicsManager *MWBase::Environment::getMechanicsManager() const +MWBase::MechanicsManager *MWBase::Environment::getMechanicsManager() const { assert (mMechanicsManager); return mMechanicsManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index d03267c25..f2e7249d3 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWGui class WindowManager; } -namespace MWMechanics -{ - class MechanicsManager; -} - namespace MWInput { struct MWInputManager; @@ -23,6 +18,7 @@ namespace MWBase class DialogueManager; class Journal; class SoundManager; + class MechanicsManager; /// \brief Central hub for mw-subsystems /// @@ -38,7 +34,7 @@ namespace MWBase SoundManager *mSoundManager; ScriptManager *mScriptManager; MWGui::WindowManager *mWindowManager; - MWMechanics::MechanicsManager *mMechanicsManager; + MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; MWInput::MWInputManager *mInputManager; @@ -64,7 +60,7 @@ namespace MWBase void setWindowManager (MWGui::WindowManager *windowManager); - void setMechanicsManager (MWMechanics::MechanicsManager *mechanicsManager); + void setMechanicsManager (MechanicsManager *mechanicsManager); void setDialogueManager (DialogueManager *dialogueManager); @@ -79,11 +75,11 @@ namespace MWBase SoundManager *getSoundManager() const; - MWBase::ScriptManager *getScriptManager() const; + ScriptManager *getScriptManager() const; MWGui::WindowManager *getWindowManager() const; - MWMechanics::MechanicsManager *getMechanicsManager() const; + MechanicsManager *getMechanicsManager() const; DialogueManager *getDialogueManager() const; diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp new file mode 100644 index 000000000..c5f1847af --- /dev/null +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -0,0 +1,77 @@ +#ifndef GAME_MWBASE_MECHANICSMANAGER_H +#define GAME_MWBASE_MECHANICSMANAGER_H + +#include +#include + +namespace Ogre +{ + class Vector3; +} + +namespace ESM +{ + struct Class; +} + +namespace MWWorld +{ + class Ptr; + class CellStore; +} + +namespace MWBase +{ + /// \brief Interface for game mechanics manager (implemented in MWMechanics) + class MechanicsManager + { + MechanicsManager (const MechanicsManager&); + ///< not implemented + + MechanicsManager& operator= (const MechanicsManager&); + ///< not implemented + + public: + + MechanicsManager() {} + + virtual ~MechanicsManager() {} + + virtual void addActor (const MWWorld::Ptr& ptr) = 0; + ///< Register an actor for stats management + + virtual void removeActor (const MWWorld::Ptr& ptr) = 0; + ///< Deregister an actor for stats management + + virtual void dropActors (const MWWorld::CellStore *cellStore) = 0; + ///< Deregister all actors in the given cell. + + virtual void watchActor (const MWWorld::Ptr& ptr) = 0; + ///< On each update look for changes in a previously registered actor and update the + /// GUI accordingly. + + virtual void update (std::vector >& movement, + float duration, bool paused) = 0; + ///< Update actor stats and store desired velocity vectors in \a movement + /// + /// \param paused In game type does not currently advance (this usually means some GUI + /// component is up). + + virtual void setPlayerName (const std::string& name) = 0; + ///< Set player name. + + virtual void setPlayerRace (const std::string& id, bool male) = 0; + ///< Set player race. + + virtual void setPlayerBirthsign (const std::string& id) = 0; + ///< Set player birthsign. + + virtual void setPlayerClass (const std::string& id) = 0; + ///< Set player class to stock class. + + virtual void setPlayerClass (const ESM::Class& class_) = 0; + ///< Set player class to custom class. + }; +} + +#endif diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0f3141f5c..57301e1a2 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -4,10 +4,10 @@ #include #include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -104,7 +104,7 @@ namespace MWClass if (!model.empty()) { return "meshes\\" + model; } - return ""; + return ""; } std::string Creature::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 81c0c85f5..30173701e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -11,11 +11,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 985d25573..e3e9fd752 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWDIALOG_DIALOGUEMANAGERIMP_H #define GAME_MWDIALOG_DIALOGUEMANAGERIMP_H +#include "../mwbase/dialoguemanager.hpp" + #include #include @@ -8,8 +10,6 @@ #include "../mwscript/interpretercontext.hpp" #include -#include "../mwbase/dialoguemanager.hpp" - #include "../mwworld/ptr.hpp" #include diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index c0081544b..fd3a7872e 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" namespace { diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index e9c90877e..1fd67bff7 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -7,7 +7,6 @@ #include "../mwbase/world.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/stat.hpp" namespace MWGui diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 190d19594..3f5162bfb 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -8,11 +8,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwmechanics/npcstats.hpp" #include "window_manager.hpp" #include "tooltips.hpp" diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 659af0447..45aee089c 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -21,10 +21,10 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwinput/inputmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/cellstore.hpp" diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp similarity index 99% rename from apps/openmw/mwmechanics/mechanicsmanager.cpp rename to apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fe5485d61..5ad67dde4 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,5 +1,5 @@ -#include "mechanicsmanager.hpp" +#include "mechanicsmanagerimp.hpp" #include diff --git a/apps/openmw/mwmechanics/mechanicsmanager.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp similarity index 60% rename from apps/openmw/mwmechanics/mechanicsmanager.hpp rename to apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 97bb369fd..d5fd3b6f2 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -1,8 +1,7 @@ -#ifndef GAME_MWMECHANICS_MECHANICSMANAGER_H -#define GAME_MWMECHANICS_MECHANICSMANAGER_H +#ifndef GAME_MWMECHANICS_MECHANICSMANAGERIMP_H +#define GAME_MWMECHANICS_MECHANICSMANAGERIMP_H -#include -#include +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" @@ -22,7 +21,7 @@ namespace MWWorld namespace MWMechanics { - class MechanicsManager + class MechanicsManager : public MWBase::MechanicsManager { MWWorld::Ptr mWatched; CreatureStats mWatchedCreature; @@ -38,43 +37,41 @@ namespace MWMechanics public: - MechanicsManager (); + MechanicsManager(); - void configureGUI(); - - void addActor (const MWWorld::Ptr& ptr); + virtual void addActor (const MWWorld::Ptr& ptr); ///< Register an actor for stats management - void removeActor (const MWWorld::Ptr& ptr); + virtual void removeActor (const MWWorld::Ptr& ptr); ///< Deregister an actor for stats management - void dropActors (const MWWorld::CellStore *cellStore); + virtual void dropActors (const MWWorld::CellStore *cellStore); ///< Deregister all actors in the given cell. - void watchActor (const MWWorld::Ptr& ptr); + virtual void watchActor (const MWWorld::Ptr& ptr); ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - void update (std::vector >& movement, float duration, - bool paused); + virtual void update (std::vector >& movement, + float duration, bool paused); ///< Update actor stats and store desired velocity vectors in \a movement /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). - void setPlayerName (const std::string& name); + virtual void setPlayerName (const std::string& name); ///< Set player name. - void setPlayerRace (const std::string& id, bool male); + virtual void setPlayerRace (const std::string& id, bool male); ///< Set player race. - void setPlayerBirthsign (const std::string& id); + virtual void setPlayerBirthsign (const std::string& id); ///< Set player birthsign. - void setPlayerClass (const std::string& id); + virtual void setPlayerClass (const std::string& id); ///< Set player class to stock class. - void setPlayerClass (const ESM::Class& class_); + virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ec557e35c..9318fd6f1 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" - -#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwgui/window_manager.hpp" @@ -154,7 +153,7 @@ namespace MWWorld } world->getPlayer().setCell(cell); - MWMechanics::MechanicsManager *mechMgr = + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->addActor(player); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d9fbc5b77..3a9547a44 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" -#include "../mwmechanics/mechanicsmanager.hpp" - #include "../mwgui/window_manager.hpp" #include "player.hpp" @@ -575,7 +574,7 @@ namespace MWWorld mRendering->moveObjectToCell(copy, vec, currCell); if (MWWorld::Class::get(ptr).isActor()) { - MWMechanics::MechanicsManager *mechMgr = + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); mechMgr->removeActor(ptr); @@ -604,11 +603,11 @@ namespace MWWorld if (cell->isExterior()) { int cellX, cellY; positionToIndex(x, y, cellX, cellY); - + cell = getExterior(cellX, cellY); } moveObject(ptr, *cell, x, y, z); - + return cell != ptr.getCell(); } From 0231533d05eaf715031cdfc155c1e2045aa6c630 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 11 Aug 2012 17:53:39 +0200 Subject: [PATCH 391/688] Issue #107: InputManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 3 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 7 ++-- apps/openmw/mwbase/environment.hpp | 12 ++---- apps/openmw/mwbase/inputmanager.hpp | 37 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 1 - apps/openmw/mwgui/bookwindow.cpp | 2 - apps/openmw/mwgui/container.cpp | 2 - apps/openmw/mwgui/scrollwindow.cpp | 2 - apps/openmw/mwgui/settingswindow.cpp | 3 +- apps/openmw/mwgui/window_manager.cpp | 3 +- .../{inputmanager.cpp => inputmanagerimp.cpp} | 6 +-- .../{inputmanager.hpp => inputmanagerimp.hpp} | 20 +++++----- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwscript/controlextensions.cpp | 10 +---- apps/openmw/mwscript/guiextensions.cpp | 1 - apps/openmw/mwscript/interpretercontext.cpp | 2 - 17 files changed, 65 insertions(+), 50 deletions(-) create mode 100644 apps/openmw/mwbase/inputmanager.hpp rename apps/openmw/mwinput/{inputmanager.cpp => inputmanagerimp.cpp} (98%) rename apps/openmw/mwinput/{inputmanager.hpp => inputmanagerimp.hpp} (62%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index f4454a216..7bd7f1882 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ add_openmw_dir (mwrender ) add_openmw_dir (mwinput - inputmanager + inputmanagerimp mouselookevent ) @@ -66,6 +66,7 @@ add_openmw_dir (mwmechanics add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager + inputmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 98fee4384..c484c22ea 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -13,7 +13,7 @@ #include #include -#include "mwinput/inputmanager.hpp" +#include "mwinput/inputmanagerimp.hpp" #include "mwgui/window_manager.hpp" #include "mwgui/cursorreplace.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index f75c3588f..28e71fcf0 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -3,14 +3,13 @@ #include -#include "../mwinput/inputmanager.hpp" - #include "world.hpp" #include "scriptmanager.hpp" #include "dialoguemanager.hpp" #include "journal.hpp" #include "soundmanager.hpp" #include "mechanicsmanager.hpp" +#include "inputmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -63,7 +62,7 @@ void MWBase::Environment::setJournal (Journal *journal) mJournal = journal; } -void MWBase::Environment::setInputManager (MWInput::MWInputManager *inputManager) +void MWBase::Environment::setInputManager (InputManager *inputManager) { mInputManager = inputManager; } @@ -115,7 +114,7 @@ MWBase::Journal *MWBase::Environment::getJournal() const return mJournal; } -MWInput::MWInputManager *MWBase::Environment::getInputManager() const +MWBase::InputManager *MWBase::Environment::getInputManager() const { assert (mInputManager); return mInputManager; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index f2e7249d3..4a21b57a4 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -6,11 +6,6 @@ namespace MWGui class WindowManager; } -namespace MWInput -{ - struct MWInputManager; -} - namespace MWBase { class World; @@ -19,6 +14,7 @@ namespace MWBase class Journal; class SoundManager; class MechanicsManager; + class InputManager; /// \brief Central hub for mw-subsystems /// @@ -37,7 +33,7 @@ namespace MWBase MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; - MWInput::MWInputManager *mInputManager; + InputManager *mInputManager; float mFrameDuration; Environment (const Environment&); @@ -66,7 +62,7 @@ namespace MWBase void setJournal (Journal *journal); - void setInputManager (MWInput::MWInputManager *inputManager); + void setInputManager (InputManager *inputManager); void setFrameDuration (float duration); ///< Set length of current frame in seconds. @@ -85,7 +81,7 @@ namespace MWBase Journal *getJournal() const; - MWInput::MWInputManager *getInputManager() const; + InputManager *getInputManager() const; float getFrameDuration() const; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp new file mode 100644 index 000000000..d865bfb0e --- /dev/null +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -0,0 +1,37 @@ +#ifndef GAME_MWBASE_INPUTMANAGER_H +#define GAME_MWBASE_INPUTMANAGER_H + +#include + +#include + +namespace MWBase +{ + /// \brief Interface for input manager (implemented in MWInput) + class InputManager + { + InputManager (const InputManager&); + ///< not implemented + + InputManager& operator= (const InputManager&); + ///< not implemented + + public: + + InputManager() {} + + virtual ~InputManager() {} + + virtual void update() = 0; + + virtual void changeInputMode(bool guiMode) = 0; + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + + virtual void setDragDrop(bool dragDrop) = 0; + + virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 644da49e2..251577d3b 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -19,7 +19,6 @@ #include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" -#include "../mwinput/inputmanager.hpp" #include "../mwgui/dialogue.hpp" #include "../mwgui/window_manager.hpp" diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 19a5e9398..92f0226ed 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -6,8 +6,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 66dea0849..89cd10233 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -19,8 +19,6 @@ #include "../mwclass/container.hpp" -#include "../mwinput/inputmanager.hpp" - #include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index 67a02e53b..fb2239c6d 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -4,8 +4,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index d1e1f7095..06177aaa8 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -13,11 +13,10 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "window_manager.hpp" #include "confirmationdialog.hpp" diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 45aee089c..b4eaaacf4 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -21,10 +21,9 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwinput/inputmanager.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/cellstore.hpp" diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp similarity index 98% rename from apps/openmw/mwinput/inputmanager.cpp rename to apps/openmw/mwinput/inputmanagerimp.cpp index e29e3c268..d53e67d48 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "inputmanager.hpp" +#include "inputmanagerimp.hpp" #include @@ -501,8 +501,8 @@ private: impl->adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); } - void MWInputManager::toggleControlSwitch(std::string sw, bool value) + void MWInputManager::toggleControlSwitch (const std::string& sw, bool value) { - impl->toggleControlSwitch(sw, value); + impl->toggleControlSwitch(sw, value); } } diff --git a/apps/openmw/mwinput/inputmanager.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp similarity index 62% rename from apps/openmw/mwinput/inputmanager.hpp rename to apps/openmw/mwinput/inputmanagerimp.hpp index 2486f82d6..862c4fd20 100644 --- a/apps/openmw/mwinput/inputmanager.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -1,10 +1,12 @@ -#ifndef _MWINPUT_MWINPUTMANAGER_H -#define _MWINPUT_MWINPUTMANAGER_H +#ifndef _MWINPUT_MWINPUTMANAGERIMP_H +#define _MWINPUT_MWINPUTMANAGERIMP_H #include "../mwgui/mode.hpp" #include +#include "../mwbase/inputmanager.hpp" + namespace OEngine { namespace Render @@ -38,7 +40,7 @@ namespace MWInput This class is just an interface. All the messy details are in inputmanager.cpp. */ - struct MWInputManager + struct MWInputManager : public MWBase::InputManager { InputImpl *impl; @@ -48,17 +50,17 @@ namespace MWInput MWGui::WindowManager &_windows, bool debug, OMW::Engine& engine); - ~MWInputManager(); + virtual ~MWInputManager(); - void update(); + virtual void update(); - void changeInputMode(bool guiMode); + virtual void changeInputMode(bool guiMode); - void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); - void setDragDrop(bool dragDrop); + virtual void setDragDrop(bool dragDrop); - void toggleControlSwitch(std::string sw, bool value); + virtual void toggleControlSwitch (const std::string& sw, bool value); }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8719557ca..590c17709 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -22,12 +22,12 @@ #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" // FIXME #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" #include "../mwgui/window_manager.hpp" // FIXME -#include "../mwinput/inputmanager.hpp" // FIXME #include "shadows.hpp" #include "localmap.hpp" diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 084698c5b..bd14e7b8d 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -8,19 +8,16 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" -#include "../mwinput/inputmanager.hpp" - #include "interpretercontext.hpp" #include "ref.hpp" -#include - namespace MWScript { namespace Control @@ -41,11 +38,6 @@ namespace MWScript MWBase::Environment::get() .getInputManager() ->toggleControlSwitch(mControl, mEnable); - - if (mEnable) - std::cout << "enable: " << mControl << std::endl; - else - std::cout << "disable: " << mControl << std::endl; } }; diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 8e5897298..3d14692d9 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -10,7 +10,6 @@ #include "../mwbase/environment.hpp" #include "../mwgui/window_manager.hpp" -#include "../mwinput/inputmanager.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 327eed913..131d7865b 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -15,8 +15,6 @@ #include "../mwgui/window_manager.hpp" -#include "../mwinput/inputmanager.hpp" - #include "locals.hpp" #include "globalscripts.hpp" From 0bc48cc5eaaa23793fbeb644c20a747e2fb969fd Mon Sep 17 00:00:00 2001 From: gugus Date: Sat, 11 Aug 2012 18:07:20 +0200 Subject: [PATCH 392/688] little fix (thanks greye!) --- apps/openmw/mwscript/transformationextensions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 8576ac22e..cc0444f86 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -292,9 +292,10 @@ namespace MWScript runtime.pop(); Interpreter::Type_Float zRot = runtime[0].mFloat; runtime.pop(); - + int cx,cy; + MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); MWBase::Environment::get().getWorld()->moveObject(ptr, - *MWBase::Environment::get().getWorld()->getExterior(x,y),x,y,z); + *MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity From 0c8045d60661b615a4b7089898c294d1e710453d Mon Sep 17 00:00:00 2001 From: gugus Date: Sat, 11 Aug 2012 19:16:00 +0200 Subject: [PATCH 393/688] allow exterior cell for positioncell. Crash! --- apps/openmw/mwscript/transformationextensions.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index cc0444f86..f3d22f670 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -249,13 +249,21 @@ namespace MWScript std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - - MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getInterior(cellID); - if(!store) + bool interior = true; + MWWorld::CellStore* store; + try { + MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getInterior(cellID); + } + catch(std::exception &e) + { + std::cout << "trying exterior"; const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); if(cell) { + std::cout << "exteriorfffffffffffffffffmZEJFB"; + //cell->getGridX(); + //MWBase::Environment::get().getWorld()->getExterior(cell->getGridX(),cell->getGridY()); store = MWBase::Environment::get().getWorld()->getExterior(cell->getGridX(),cell->getGridY()); } } From 2beaa9d9ae1be8bf9dea6d19bfe1536485550e44 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 11 Aug 2012 23:26:20 +0400 Subject: [PATCH 394/688] Workaround to allow main loop to know Ogre::Root's mQueuedEnd value This change is needed because of changes in exit handling --- libs/openengine/ogre/renderer.cpp | 26 ++++++++++++++++++++++---- libs/openengine/ogre/renderer.hpp | 21 +++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 4074a1a99..e342f4c5f 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -24,6 +24,21 @@ using namespace Ogre; using namespace OEngine::Render; +#if defined(__APPLE__) && !defined(__LP64__) + +CustomRoot::CustomRoot(const Ogre::String& pluginFileName, + const Ogre::String& configFileName, + const Ogre::String& logFileName) +: Ogre::Root(pluginFileName, configFileName, logFileName) +{} + +bool CustomRoot::isQueuedEnd() const +{ + return mQueuedEnd; +} + +#endif + void OgreRenderer::cleanup() { delete mFader; @@ -36,7 +51,6 @@ void OgreRenderer::cleanup() void OgreRenderer::start() { #if defined(__APPLE__) && !defined(__LP64__) - bool quit = false; // OSX Carbon Message Pump do { EventRef event = NULL; @@ -54,11 +68,11 @@ void OgreRenderer::start() ReleaseEvent(event); } - if (!Ogre::Root::getSingleton().renderOneFrame()) { - quit = true; + if (!mRoot->renderOneFrame()) { + break; } - } while (!quit); + } while (!mRoot->isQueuedEnd()); #else mRoot->startRendering(); #endif @@ -120,7 +134,11 @@ void OgreRenderer::configure(const std::string &logPath, // Disable logging log->setDebugOutputEnabled(false); +#if defined(__APPLE__) && !defined(__LP64__) + mRoot = new CustomRoot("", "", ""); +#else mRoot = new Root("", "", ""); +#endif #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) loadPlugins(); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 247c8f95a..9b7003368 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -27,9 +27,15 @@ #include "OgreTexture.h" #include +#if defined(__APPLE__) && !defined(__LP64__) +#include +#endif + namespace Ogre { +#if !defined(__APPLE__) || defined(__LP64__) class Root; +#endif class RenderWindow; class SceneManager; class Camera; @@ -48,10 +54,25 @@ namespace OEngine std::string fsaa; }; +#if defined(__APPLE__) && !defined(__LP64__) + class CustomRoot : public Ogre::Root { + public: + bool isQueuedEnd() const; + + CustomRoot(const Ogre::String& pluginFileName = "plugins.cfg", + const Ogre::String& configFileName = "ogre.cfg", + const Ogre::String& logFileName = "Ogre.log"); + }; +#endif + class Fader; class OgreRenderer { +#if defined(__APPLE__) && !defined(__LP64__) + CustomRoot *mRoot; +#else Ogre::Root *mRoot; +#endif Ogre::RenderWindow *mWindow; Ogre::SceneManager *mScene; Ogre::Camera *mCamera; From fb91f76a2b70bf10366d8f8f97b53d4aa3c0acfd Mon Sep 17 00:00:00 2001 From: gugus Date: Sat, 11 Aug 2012 22:08:04 +0200 Subject: [PATCH 395/688] fixed a crash --- apps/openmw/mwscript/transformationextensions.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index f3d22f670..3f1cff7e7 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -249,21 +249,16 @@ namespace MWScript std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - bool interior = true; - MWWorld::CellStore* store; + MWWorld::CellStore* store = 0; try { MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getInterior(cellID); } catch(std::exception &e) { - std::cout << "trying exterior"; const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); if(cell) { - std::cout << "exteriorfffffffffffffffffmZEJFB"; - //cell->getGridX(); - //MWBase::Environment::get().getWorld()->getExterior(cell->getGridX(),cell->getGridY()); store = MWBase::Environment::get().getWorld()->getExterior(cell->getGridX(),cell->getGridY()); } } From e7329d5f8bc36f29578304c6a111f9518e0623dd Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 12 Aug 2012 00:36:49 -0400 Subject: [PATCH 396/688] Creatures now have a properly positioned box shape --- apps/openmw/mwworld/physicssystem.cpp | 13 ++----------- components/nifbullet/bullet_nif_loader.cpp | 8 +++++++- components/nifbullet/bullet_nif_loader.hpp | 2 ++ libs/openengine/bullet/BulletShapeLoader.h | 3 ++- libs/openengine/bullet/physic.cpp | 15 ++++++++++++++- libs/openengine/bullet/physic.hpp | 2 +- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7f773b5cb..3bdf76bcd 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -173,7 +173,7 @@ namespace MWWorld act->setWalkDirection(btVector3(0,0,0)); } playerMove::playercmd& pm_ref = playerphysics->cmd; - //playerphysics->ps.snappingImplemented = false; + pm_ref.rightmove = 0; pm_ref.forwardmove = 0; pm_ref.upmove = 0; @@ -259,17 +259,8 @@ namespace MWWorld const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position) { handleToMesh[handle] = mesh; - OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle,scale); + OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody(mesh,handle,scale, position, rotation); mEngine->addRigidBody(body); - btTransform tr; - btBoxShape* box = dynamic_cast(body->getCollisionShape()); - if(box != NULL){ - tr.setOrigin(btVector3(position.x,position.y,position.z + box->getHalfExtentsWithMargin().getZ())); - } - else - tr.setOrigin(btVector3(position.x,position.y,position.z)); - tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); - body->setWorldTransform(tr); } void PhysicsSystem::addActor (const std::string& handle, const std::string& mesh, diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 13c2cbbb7..7af56d04c 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -73,6 +73,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) resourceName = cShape->getName(); cShape->collide = false; mBoundingBox = NULL; + boxTranslation = Ogre::Vector3(0,0,0); mTriMesh = new btTriangleMesh(); @@ -126,8 +127,10 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) delete m_meshInterface; } }; + cShape->boxTranslation = boxTranslation; if(mBoundingBox != NULL) cShape->Shape = mBoundingBox; + else { currentShape = new TriangleMeshShape(mTriMesh,true); @@ -220,8 +223,11 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, if(node->hasBounds) { - btVector3 boxsize = getbtVector((node->boundXYZ)); + + btVector3 boxsize = getbtVector((node->boundXYZ)); + boxTranslation = node->boundPos; + mBoundingBox = new btBoxShape(boxsize); } diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index 8bf91d127..beb4274b0 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -102,6 +102,8 @@ private: std::string resourceName; std::string resourceGroup; + Ogre::Vector3 boxTranslation; + BulletShape* cShape;//current shape btTriangleMesh *mTriMesh; btBoxShape *mBoundingBox; diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index 316ee523c..c09f0dc7e 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -4,7 +4,7 @@ #include #include #include - +#include //For some reason, Ogre Singleton cannot be used in another namespace, that's why there is no namespace here. //But the risk of name collision seems pretty low here. @@ -31,6 +31,7 @@ public: virtual ~BulletShape(); btCollisionShape* Shape; + Ogre::Vector3 boxTranslation; //this flag indicate if the shape is used for collision or if it's for raycasting only. bool collide; }; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 743024c40..9d0a290e9 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -332,7 +332,7 @@ namespace Physic mHeightFieldMap.erase(name); } - RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) + RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation) { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; @@ -354,6 +354,19 @@ namespace Physic btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape); RigidBody* body = new RigidBody(CI,name); body->collide = shape->collide; + + btTransform tr; + btBoxShape* box = dynamic_cast(body->getCollisionShape()); + if(box != NULL){ + Ogre::Vector3 transrot = rotation * (shape->boxTranslation * scale); + Ogre::Vector3 newPosition = transrot + position; + tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); + } + else + tr.setOrigin(btVector3(position.x,position.y,position.z)); + tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); + body->setWorldTransform(tr); + return body; } diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 9ae8e7607..42f648766 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -146,7 +146,7 @@ namespace Physic * Create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map, * so you can get it with the getRigidBody function. */ - RigidBody* createRigidBody(std::string mesh,std::string name,float scale); + RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); /** * Add a HeightField to the simulation From a3b27ae756212eaed6fd2942cf40542e91de3492 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 10:50:03 +0200 Subject: [PATCH 397/688] Issue #107: Moved ClassPoint from window manager to character creation --- apps/openmw/mwgui/charactercreation.cpp | 8 ++++++++ apps/openmw/mwgui/window_manager.hpp | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index fd3a7872e..2c0b3de85 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -104,6 +104,14 @@ namespace {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} } } }; + + struct ClassPoint + { + const char *id; + // Specialization points to match, in order: Stealth, Combat, Magic + // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz + unsigned int points[3]; + }; } using namespace MWGui; diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 3653615a6..69a9717dd 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -79,14 +79,6 @@ namespace MWGui class AlchemyWindow; class SpellWindow; - struct ClassPoint - { - const char *id; - // Specialization points to match, in order: Stealth, Combat, Magic - // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz - unsigned int points[3]; - }; - class WindowManager { public: From dbac3d2e5f7da94bef0baf014e1ec6f0e7dcb658 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 10:52:29 +0200 Subject: [PATCH 398/688] Issue #107: fixing WindowManager::removeDialog --- apps/openmw/mwgui/window_manager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index b4eaaacf4..ca5223a18 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -424,7 +424,6 @@ void WindowManager::updateSkillArea() void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) { - assert(dialog); if (!dialog) return; dialog->setVisible(false); From 020fde70b80e58cfd9feb7419d5d5d782a1d372e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 11:10:17 +0200 Subject: [PATCH 399/688] Issue #107: moving function implementations from hpp to cpp file --- apps/openmw/mwgui/window_manager.cpp | 76 ++++++++++++++++++++++++++++ apps/openmw/mwgui/window_manager.hpp | 71 ++++++++------------------ 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index ca5223a18..38ea74a57 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -743,3 +743,79 @@ void WindowManager::executeInConsole (const std::string& path) { mConsole->executeFile (path); } + +void WindowManager::wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) +{ + mFPS = fps; + mTriangleCount = triangleCount; + mBatchCount = batchCount; +} + +MyGUI::Gui* WindowManager::getGui() const { return mGui; } + +MWGui::DialogueWindow* WindowManager::getDialogueWindow() { return mDialogueWindow; } +MWGui::ContainerWindow* WindowManager::getContainerWindow() { return mContainerWindow; } +MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } +MWGui::BookWindow* WindowManager::getBookWindow() { return mBookWindow; } +MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } +MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } +MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } +MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } +MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } +MWGui::Console* WindowManager::getConsole() { return mConsole; } + +bool WindowManager::isAllowed (GuiWindow wnd) const +{ + return mAllowed & wnd; +} + +void WindowManager::allow (GuiWindow wnd) +{ + mAllowed = (GuiWindow)(mAllowed | wnd); + updateVisible(); +} + +void WindowManager::disallowAll() +{ + mAllowed = GW_None; + updateVisible(); +} + +void WindowManager::toggleVisible (GuiWindow wnd) +{ + mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); + updateVisible(); +} + +bool WindowManager::isGuiMode() const +{ + return !mGuiModes.empty(); +} + +MWGui::GuiMode WindowManager::getMode() const +{ + if (mGuiModes.empty()) + throw std::runtime_error ("getMode() called, but there is no active mode"); + + return mGuiModes.back(); +} + +std::map > WindowManager::getPlayerSkillValues() +{ + return mPlayerSkillValues; +} + +std::map > WindowManager::getPlayerAttributeValues() +{ + return mPlayerAttributes; +} + +WindowManager::SkillList WindowManager::getPlayerMinorSkills() +{ + return mPlayerMinorSkills; +} + +WindowManager::SkillList WindowManager::getPlayerMajorSkills() +{ + return mPlayerMajorSkills; +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 69a9717dd..60fc63c8c 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -100,61 +100,34 @@ namespace MWGui void popGuiMode(); void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack - GuiMode getMode() const - { - if (mGuiModes.empty()) - throw std::runtime_error ("getMode() called, but there is no active mode"); - return mGuiModes.back(); - } + GuiMode getMode() const; - bool isGuiMode() const { return !mGuiModes.empty(); } + bool isGuiMode() const; - void toggleVisible(GuiWindow wnd) - { - mShown = (mShown & wnd) ? (GuiWindow) (mShown & ~wnd) : (GuiWindow) (mShown | wnd); - updateVisible(); - } + void toggleVisible(GuiWindow wnd); // Disallow all inventory mode windows - void disallowAll() - { - mAllowed = GW_None; - updateVisible(); - } + void disallowAll(); // Allow one or more windows - void allow(GuiWindow wnd) - { - mAllowed = (GuiWindow)(mAllowed | wnd); - updateVisible(); - } + void allow(GuiWindow wnd); - bool isAllowed(GuiWindow wnd) const - { - return mAllowed & wnd; - } + bool isAllowed(GuiWindow wnd) const; - MWGui::DialogueWindow* getDialogueWindow() {return mDialogueWindow;} - MWGui::ContainerWindow* getContainerWindow() {return mContainerWindow;} - MWGui::InventoryWindow* getInventoryWindow() {return mInventoryWindow;} - MWGui::BookWindow* getBookWindow() {return mBookWindow;} - MWGui::ScrollWindow* getScrollWindow() {return mScrollWindow;} - MWGui::CountDialog* getCountDialog() {return mCountDialog;} - MWGui::ConfirmationDialog* getConfirmationDialog() {return mConfirmationDialog;} - MWGui::TradeWindow* getTradeWindow() {return mTradeWindow;} - MWGui::SpellWindow* getSpellWindow() {return mSpellWindow;} - MWGui::Console* getConsole() {return mConsole;} + MWGui::DialogueWindow* getDialogueWindow(); + MWGui::ContainerWindow* getContainerWindow(); + MWGui::InventoryWindow* getInventoryWindow(); + MWGui::BookWindow* getBookWindow(); + MWGui::ScrollWindow* getScrollWindow(); + MWGui::CountDialog* getCountDialog(); + MWGui::ConfirmationDialog* getConfirmationDialog(); + MWGui::TradeWindow* getTradeWindow(); + MWGui::SpellWindow* getSpellWindow(); + MWGui::Console* getConsole(); - MyGUI::Gui* getGui() const { return mGui; } + MyGUI::Gui* getGui() const; - void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) - { - mFPS = fps; - mTriangleCount = triangleCount; - mBatchCount = batchCount; - } - -// MWMechanics::DynamicStat getValue(const std::string& id); + void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); @@ -211,10 +184,10 @@ namespace MWGui void onFrame (float frameDuration); - std::map > getPlayerSkillValues() { return mPlayerSkillValues; } - std::map > getPlayerAttributeValues() { return mPlayerAttributes; } - SkillList getPlayerMinorSkills() { return mPlayerMinorSkills; } - SkillList getPlayerMajorSkills() { return mPlayerMajorSkills; } + std::map > getPlayerSkillValues(); + std::map > getPlayerAttributeValues(); + SkillList getPlayerMinorSkills(); + SkillList getPlayerMajorSkills(); /** * Fetches a GMST string from the store, if there is no setting with the given From e0ba7cf952104664e08cfcca9f2e025eb2ebe4d3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 13:46:14 +0200 Subject: [PATCH 400/688] Issue #107: Fixing up the window manager interface --- apps/openmw/mwgui/charactercreation.cpp | 12 ++++++------ apps/openmw/mwgui/window_manager.cpp | 12 +++++++----- apps/openmw/mwgui/window_manager.hpp | 10 +++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 2c0b3de85..5e0df5cbd 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -268,20 +268,20 @@ void CharacterCreation::spawnDialog(const char id) mReviewDialog->setFatigue(mPlayerFatigue); { - std::map > attributes = mWM->getPlayerAttributeValues(); - for (std::map >::iterator it = attributes.begin(); + std::map > attributes = mWM->getPlayerAttributeValues(); + for (std::map >::iterator it = attributes.begin(); it != attributes.end(); ++it) { - mReviewDialog->setAttribute(it->first, it->second); + mReviewDialog->setAttribute(static_cast (it->first), it->second); } } { - std::map > skills = mWM->getPlayerSkillValues(); - for (std::map >::iterator it = skills.begin(); + std::map > skills = mWM->getPlayerSkillValues(); + for (std::map >::iterator it = skills.begin(); it != skills.end(); ++it) { - mReviewDialog->setSkillValue(it->first, it->second); + mReviewDialog->setSkillValue(static_cast (it->first), it->second); } mReviewDialog->configureSkills(mWM->getPlayerMajorSkills(), mWM->getPlayerMinorSkills()); } diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 38ea74a57..94cad2818 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -338,10 +338,12 @@ void WindowManager::setValue (const std::string& id, const MWMechanics::Stat& value) +void WindowManager::setValue (int parSkill, const MWMechanics::Stat& value) { - mStatsWindow->setValue(parSkill, value); - mCharGen->setValue(parSkill, value); + /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we + /// allow custom skills. + mStatsWindow->setValue(static_cast (parSkill), value); + mCharGen->setValue(static_cast (parSkill), value); mPlayerSkillValues[parSkill] = value; } @@ -800,12 +802,12 @@ MWGui::GuiMode WindowManager::getMode() const return mGuiModes.back(); } -std::map > WindowManager::getPlayerSkillValues() +std::map > WindowManager::getPlayerSkillValues() { return mPlayerSkillValues; } -std::map > WindowManager::getPlayerAttributeValues() +std::map > WindowManager::getPlayerAttributeValues() { return mPlayerAttributes; } diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 60fc63c8c..de90037b9 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -131,7 +131,7 @@ namespace MWGui ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); + void setValue (int parSkill, const MWMechanics::Stat& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); @@ -184,8 +184,8 @@ namespace MWGui void onFrame (float frameDuration); - std::map > getPlayerSkillValues(); - std::map > getPlayerAttributeValues(); + std::map > getPlayerSkillValues(); + std::map > getPlayerAttributeValues(); SkillList getPlayerMinorSkills(); SkillList getPlayerMajorSkills(); @@ -233,9 +233,9 @@ namespace MWGui ESM::Class mPlayerClass; std::string mPlayerName; std::string mPlayerRaceId; - std::map > mPlayerAttributes; + std::map > mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; + std::map > mPlayerSkillValues; MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; From 4ca3cb81d49e2ce68d56c6d9fe6731780c6f3107 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:07:48 +0200 Subject: [PATCH 401/688] Issue #107: some include clean up --- apps/openmw/mwgui/window_manager.cpp | 37 ++++++++++++++++------------ apps/openmw/mwgui/window_manager.hpp | 21 ++++++++-------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 94cad2818..369eae089 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -1,4 +1,25 @@ #include "window_manager.hpp" + +#include +#include + +#include "MyGUI_UString.h" + +#include +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/inputmanager.hpp" + +#include "../mwworld/ptr.hpp" +#include "../mwworld/cellstore.hpp" + +#include "console.hpp" +#include "journalwindow.hpp" +#include "charactercreation.hpp" #include "text_input.hpp" #include "review.hpp" #include "dialogue.hpp" @@ -21,22 +42,6 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" -#include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/inputmanager.hpp" - -#include "../mwworld/ptr.hpp" -#include "../mwworld/cellstore.hpp" - -#include "console.hpp" -#include "journalwindow.hpp" -#include "charactercreation.hpp" - -#include - -#include -#include - using namespace MWGui; WindowManager::WindowManager( diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index de90037b9..59a6ab395 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -10,12 +10,11 @@ this class. **/ -#include "MyGUI_UString.h" +#include +#include #include #include -#include -#include #include "../mwmechanics/stat.hpp" @@ -23,8 +22,9 @@ namespace MyGUI { - class Gui; - class Widget; + class Gui; + class Widget; + class UString; } namespace Compiler @@ -38,16 +38,17 @@ namespace MWWorld class CellStore; } -namespace MWMechanics -{ - class MechanicsManager; -} - namespace OEngine { namespace GUI { class Layout; + class MyGUIManager; + } + + namespace Render + { + class OgreRenderer; } } From f37d3cd3c9c607ee0932c740d5ebbd571aa1966b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:09:55 +0200 Subject: [PATCH 402/688] Issue #107: added a few todo comments --- apps/openmw/mwgui/window_manager.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 59a6ab395..fccaa8347 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -115,6 +115,7 @@ namespace MWGui bool isAllowed(GuiWindow wnd) const; + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world MWGui::DialogueWindow* getDialogueWindow(); MWGui::ContainerWindow* getContainerWindow(); MWGui::InventoryWindow* getInventoryWindow(); @@ -185,6 +186,7 @@ namespace MWGui void onFrame (float frameDuration); + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. std::map > getPlayerSkillValues(); std::map > getPlayerAttributeValues(); SkillList getPlayerMinorSkills(); @@ -230,6 +232,7 @@ namespace MWGui CharacterCreation* mCharGen; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; std::string mPlayerName; From 484cce12a85e320b29e818341afe05ace8410830 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 14:36:46 +0200 Subject: [PATCH 403/688] Issue #107: removed redundant getStore function from window manager --- apps/openmw/mwdialogue/journalimp.cpp | 2 ++ apps/openmw/mwgui/birth.cpp | 15 ++++++++++----- apps/openmw/mwgui/class.cpp | 10 ++++++---- apps/openmw/mwgui/race.cpp | 9 ++++++--- apps/openmw/mwgui/review.cpp | 7 +++++-- apps/openmw/mwgui/stats_window.cpp | 14 +++++++------- apps/openmw/mwgui/widgets.cpp | 17 +++++++++++------ apps/openmw/mwgui/window_manager.cpp | 5 ----- apps/openmw/mwgui/window_manager.hpp | 4 +--- apps/openmw/mwrender/localmap.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwscript/interpretercontext.cpp | 1 + apps/openmw/mwworld/scene.cpp | 2 ++ 13 files changed, 54 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 052b92194..dc322cade 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -1,6 +1,8 @@ #include "journalimp.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 0f1a04c27..023702c8b 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -1,11 +1,16 @@ #include "birth.hpp" -#include "window_manager.hpp" -#include "widgets.hpp" -#include "components/esm_store/store.hpp" #include #include +#include "components/esm_store/store.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" +#include "widgets.hpp" + using namespace MWGui; using namespace Widgets; @@ -114,7 +119,7 @@ void BirthDialog::updateBirths() { mBirthList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.birthSigns.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.birthSigns.list.end(); @@ -144,7 +149,7 @@ void BirthDialog::updateSpells() const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::BirthSign *birth = store.birthSigns.find(mCurrentBirthId); std::string texturePath = std::string("textures\\") + birth->texture; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 48e01eb1e..e5b3e261d 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -7,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "tooltips.hpp" @@ -57,8 +60,7 @@ void GenerateClassResultDialog::setClassId(const std::string &classId) { mCurrentClassId = classId; mClassImage->setImageTexture(std::string("textures\\levelup\\") + mCurrentClassId + ".dds"); - const ESMS::ESMStore &store = mWindowManager.getStore(); - mClassName->setCaption(store.classes.find(mCurrentClassId)->name); + mClassName->setCaption(MWBase::Environment::get().getWorld()->getStore().classes.find(mCurrentClassId)->name); } // widget controls @@ -193,7 +195,7 @@ void PickClassDialog::updateClasses() { mClassList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.classes.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.classes.list.end(); @@ -217,7 +219,7 @@ void PickClassDialog::updateStats() { if (mCurrentClassId.empty()) return; - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Class *klass = store.classes.search(mCurrentClassId); if (!klass) return; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index f90a9edde..62434cb91 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -8,6 +8,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -209,7 +212,7 @@ void RaceDialog::updateRaces() { mRaceList->removeAllItems(); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); ESMS::RecListT::MapType::const_iterator it = store.races.list.begin(); ESMS::RecListT::MapType::const_iterator end = store.races.list.end(); @@ -243,7 +246,7 @@ void RaceDialog::updateSkills() const int lineHeight = 18; MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.races.find(mCurrentRaceId); int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? for (int i = 0; i < count; ++i) @@ -281,7 +284,7 @@ void RaceDialog::updateSpellPowers() const int lineHeight = 18; MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.races.find(mCurrentRaceId); std::vector::const_iterator it = race->powers.list.begin(); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 7061f65c1..997899c52 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -7,6 +7,9 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -138,7 +141,7 @@ void ReviewDialog::setPlayerName(const std::string &name) void ReviewDialog::setRace(const std::string &raceId) { mRaceId = raceId; - const ESM::Race *race = mWindowManager.getStore().races.search(mRaceId); + const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().races.search(mRaceId); if (race) { ToolTips::createRaceToolTip(mRaceWidget, race); @@ -156,7 +159,7 @@ void ReviewDialog::setClass(const ESM::Class& class_) void ReviewDialog::setBirthSign(const std::string& signId) { mBirthSignId = signId; - const ESM::BirthSign *sign = mWindowManager.getStore().birthSigns.search(mBirthSignId); + const ESM::BirthSign *sign = MWBase::Environment::get().getWorld()->getStore().birthSigns.search(mBirthSignId); if (sign) { mBirthSignWidget->setCaption(sign->name); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 3f5162bfb..4ee56abc7 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -57,7 +57,7 @@ StatsWindow::StatsWindow (WindowManager& parWindowManager) { 0, 0 } }; - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (int i=0; names[i][0]; ++i) { setText (names[i][0], store.gameSettings.find (names[i][1])->str); @@ -383,12 +383,12 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId, float modified = stat.getModified(); int progressPercent = (modified - float(static_cast(modified))) * 100; - const ESM::Skill* skill = mWindowManager.getStore().skills.search(skillId); + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().skills.search(skillId); assert(skill); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; - const ESM::Attribute* attr = mWindowManager.getStore().attributes.search(skill->data.attribute); + const ESM::Attribute* attr = MWBase::Environment::get().getWorld()->getStore().attributes.search(skill->data.attribute); assert(attr); std::string state = "normal"; @@ -443,7 +443,7 @@ void StatsWindow::updateSkillArea() if (!mMiscSkills.empty()) addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); - const ESMS::ESMStore &store = mWindowManager.getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); // race tooltip const ESM::Race* playerRace = store.races.find (MWBase::Environment::get().getWorld()->getPlayer().getRace()); @@ -485,8 +485,8 @@ void StatsWindow::updateSkillArea() text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->ranks[it->second+1]; ESM::RankData rankData = faction->data.rankData[it->second+1]; - const ESM::Attribute* attr1 = mWindowManager.getStore().attributes.search(faction->data.attribute1); - const ESM::Attribute* attr2 = mWindowManager.getStore().attributes.search(faction->data.attribute2); + const ESM::Attribute* attr1 = MWBase::Environment::get().getWorld()->getStore().attributes.search(faction->data.attribute1); + const ESM::Attribute* attr2 = MWBase::Environment::get().getWorld()->getStore().attributes.search(faction->data.attribute2); assert(attr1 && attr2); text += "\n#BF9959#{" + attr1->name + "}: " + boost::lexical_cast(rankData.attribute1) @@ -496,7 +496,7 @@ void StatsWindow::updateSkillArea() text += "\n#BF9959"; for (int i=0; i<6; ++i) { - const ESM::Skill* skill = mWindowManager.getStore().skills.search(faction->data.skillID[i]); + const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().skills.search(faction->data.skillID[i]); assert(skill); text += "#{"+ESM::Skill::sSkillNameIds[faction->data.skillID[i]]+"}"; if (i<5) diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index c54ac6e38..152938707 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -1,9 +1,14 @@ #include "widgets.hpp" -#include "window_manager.hpp" -#include "components/esm_store/store.hpp" #include +#include "components/esm_store/store.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "window_manager.hpp" + #undef min #undef max @@ -190,7 +195,7 @@ void MWAttribute::initialiseOverride() assignWidget(mAttributeNameWidget, "StatName"); assignWidget(mAttributeValueWidget, "StatValue"); - + MyGUI::ButtonPtr button; assignWidget(button, "StatNameButton"); if (button) @@ -224,7 +229,7 @@ void MWSpell::setSpellId(const std::string &spellId) void MWSpell::createEffectWidgets(std::vector &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord, int flags) { - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.spells.search(mId); MYGUI_ASSERT(spell, "spell with id '" << mId << "' not found"); @@ -255,7 +260,7 @@ void MWSpell::updateWidgets() { if (mSpellNameWidget && mWindowManager) { - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Spell *spell = store.spells.search(mId); if (spell) static_cast(mSpellNameWidget)->setCaption(spell->name); @@ -384,7 +389,7 @@ void MWSpellEffect::updateWidgets() if (!mWindowManager) return; - const ESMS::ESMStore &store = mWindowManager->getStore(); + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::MagicEffect *magicEffect = store.magicEffects.search(mEffectParams.mEffectID); if (!magicEffect) return; diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 369eae089..eada0c88a 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -497,11 +497,6 @@ void WindowManager::onFrame (float frameDuration) mConsole->checkReferenceAvailable(); } -const ESMS::ESMStore& WindowManager::getStore() const -{ - return MWBase::Environment::get().getWorld()->getStore(); -} - void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { if (!(cell->cell->data.flags & ESM::Cell::Interior)) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index fccaa8347..224c2a920 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -13,8 +13,8 @@ #include #include -#include #include +#include #include "../mwmechanics/stat.hpp" @@ -201,8 +201,6 @@ namespace MWGui */ const std::string &getGameSettingString(const std::string &id, const std::string &default_); - const ESMS::ESMStore& getStore() const; - void processChangedSettings(const Settings::CategorySettingVector& changed); void executeInConsole (const std::string& path); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 962f19a57..b5fa135e0 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 590c17709..d039418ba 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 131d7865b..0ba04fb38 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 9318fd6f1..1c7093e60 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,5 +1,7 @@ #include "scene.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" From 35a64edf2acd3d588aa114ee1049c04bc4a5ddc8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 16:07:04 +0200 Subject: [PATCH 404/688] Issue #107: removed a template from the window manager --- apps/openmw/mwgui/charactercreation.cpp | 83 ++++++++++++++----------- apps/openmw/mwgui/class.cpp | 31 ++++----- apps/openmw/mwgui/window_manager.hpp | 10 --- 3 files changed, 58 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 5e0df5cbd..72394de80 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -187,8 +187,8 @@ void CharacterCreation::spawnDialog(const char id) switch (id) { case GM_Name: - if(mNameDialog) - mWM->removeDialog(mNameDialog); + mWM->removeDialog(mNameDialog); + mNameDialog = 0; mNameDialog = new TextInputDialog(*mWM); mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name")); mNameDialog->setTextInput(mPlayerName); @@ -198,8 +198,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Race: - if (mRaceDialog) - mWM->removeDialog(mRaceDialog); + mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; mRaceDialog = new RaceDialog(*mWM); mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); mRaceDialog->setRaceId(mPlayerRaceId); @@ -209,16 +209,16 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Class: - if (mClassChoiceDialog) - mWM->removeDialog(mClassChoiceDialog); + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; mClassChoiceDialog = new ClassChoiceDialog(*mWM); mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); mClassChoiceDialog->open(); break; case GM_ClassPick: - if (mPickClassDialog) - mWM->removeDialog(mPickClassDialog); + mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; mPickClassDialog = new PickClassDialog(*mWM); mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mPickClassDialog->setClassId(mPlayerClass.name); @@ -228,8 +228,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_Birth: - if (mBirthSignDialog) - mWM->removeDialog(mBirthSignDialog); + mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; mBirthSignDialog = new BirthDialog(*mWM); mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); @@ -238,8 +238,8 @@ void CharacterCreation::spawnDialog(const char id) break; case GM_ClassCreate: - if (mCreateClassDialog) - mWM->removeDialog(mCreateClassDialog); + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; mCreateClassDialog = new CreateClassDialog(*mWM); mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); @@ -255,8 +255,8 @@ void CharacterCreation::spawnDialog(const char id) showClassQuestionDialog(); break; case GM_Review: - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mReviewDialog = new ReviewDialog(*mWM); mReviewDialog->setPlayerName(mPlayerName); mReviewDialog->setRace(mPlayerRaceId); @@ -311,24 +311,24 @@ void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& v void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mWM->popGuiMode(); } void CharacterCreation::onReviewDialogBack() { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mWM->pushGuiMode(GM_Birth); } void CharacterCreation::onReviewActivateDialog(int parDialog) { - if (mReviewDialog) - mWM->removeDialog(mReviewDialog); + mWM->removeDialog(mReviewDialog); + mReviewDialog = 0; mCreationStage = CSE_ReviewNext; mWM->popGuiMode(); @@ -363,6 +363,7 @@ void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) mWM->setPlayerClass(mPlayerClass); } mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } //TODO This bit gets repeated a few times; wrap it in a function @@ -391,6 +392,7 @@ void CharacterCreation::onPickClassDialogBack() if (!classId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId); mWM->removeDialog(mPickClassDialog); + mPickClassDialog = 0; } mWM->popGuiMode(); @@ -399,10 +401,8 @@ void CharacterCreation::onPickClassDialogBack() void CharacterCreation::onClassChoice(int _index) { - if (mClassChoiceDialog) - { - mWM->removeDialog(mClassChoiceDialog); - } + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = 0; mWM->popGuiMode(); @@ -432,6 +432,7 @@ void CharacterCreation::onNameDialogDone(WindowBase* parWindow) mWM->setValue("name", mPlayerName); MWBase::Environment::get().getMechanicsManager()->setPlayerName(mPlayerName); mWM->removeDialog(mNameDialog); + mNameDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -459,6 +460,7 @@ void CharacterCreation::onRaceDialogBack() if (!mPlayerRaceId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; } mWM->popGuiMode(); @@ -474,6 +476,7 @@ void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) if (!mPlayerRaceId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); mWM->removeDialog(mRaceDialog); + mRaceDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -501,6 +504,7 @@ void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) if (!mPlayerBirthSignId.empty()) MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mPlayerBirthSignId); mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; } if (mCreationStage >= CSE_BirthSignChosen) @@ -521,6 +525,7 @@ void CharacterCreation::onBirthSignDialogBack() { MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId()); mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = 0; } mWM->popGuiMode(); @@ -556,6 +561,7 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) mWM->setPlayerClass(klass); mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; } if (mCreationStage == CSE_ReviewNext) @@ -577,8 +583,8 @@ void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) void CharacterCreation::onCreateClassDialogBack() { - if (mCreateClassDialog) - mWM->removeDialog(mCreateClassDialog); + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = 0; mWM->popGuiMode(); mWM->pushGuiMode(GM_Class); @@ -588,8 +594,9 @@ void CharacterCreation::onClassQuestionChosen(int _index) { MWBase::Environment::get().getSoundManager()->stopSay(); - if (mGenerateClassQuestionDialog) - mWM->removeDialog(mGenerateClassQuestionDialog); + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + if (_index < 0 || _index >= 3) { mWM->popGuiMode(); @@ -666,8 +673,9 @@ void CharacterCreation::showClassQuestionDialog() } } - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); mGenerateClassResultDialog->setClassId(mGenerateClass); mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); @@ -683,8 +691,9 @@ void CharacterCreation::showClassQuestionDialog() return; } - if (mGenerateClassQuestionDialog) - mWM->removeDialog(mGenerateClassQuestionDialog); + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = 0; + mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); InfoBoxDialog::ButtonList buttons; @@ -704,8 +713,9 @@ void CharacterCreation::onGenerateClassBack() if(mCreationStage < CSE_ClassChosen) mCreationStage = CSE_ClassChosen; - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); mWM->popGuiMode(); @@ -714,8 +724,9 @@ void CharacterCreation::onGenerateClassBack() void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) { - if (mGenerateClassResultDialog) - mWM->removeDialog(mGenerateClassResultDialog); + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = 0; + MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass); const ESM::Class *klass = MWBase::Environment::get().getWorld()->getStore().classes.find(mGenerateClass); mPlayerClass = *klass; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index e5b3e261d..7e3194d6b 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -558,26 +558,17 @@ void CreateClassDialog::open() void CreateClassDialog::onDialogCancel() { - if (mSpecDialog) - { - mWindowManager.removeDialog(mSpecDialog); - mSpecDialog = 0; - } - if (mAttribDialog) - { - mWindowManager.removeDialog(mAttribDialog); - mAttribDialog = 0; - } - if (mSkillDialog) - { - mWindowManager.removeDialog(mSkillDialog); - mSkillDialog = 0; - } - if (mDescDialog) - { - mWindowManager.removeDialog(mDescDialog); - mDescDialog = 0; - } + mWindowManager.removeDialog(mSpecDialog); + mSpecDialog = 0; + + mWindowManager.removeDialog(mAttribDialog); + mAttribDialog = 0; + + mWindowManager.removeDialog(mSkillDialog); + mSkillDialog = 0; + + mWindowManager.removeDialog(mDescDialog); + mDescDialog = 0; } void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender) diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 224c2a920..db3945e55 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -177,8 +177,6 @@ namespace MWGui void unsetSelectedSpell(); void unsetSelectedWeapon(); - template - void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. void messageBox (const std::string& message, const std::vector& buttons); @@ -271,14 +269,6 @@ namespace MWGui */ void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); }; - - template - void WindowManager::removeDialog(T*& dialog) - { - OEngine::GUI::Layout *d = static_cast(dialog); - removeDialog(d); - dialog = 0; - } } #endif From b6f427bf5ef9848bfc4a3e190890e3a33ff545d7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 17:20:46 +0200 Subject: [PATCH 405/688] fix OpenMW not exiting when the window is closed (alt f4 etc) --- apps/openmw/mwrender/renderingmanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7180fea66..2ff18fbeb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -775,6 +775,7 @@ void RenderingManager::windowResized(Ogre::RenderWindow* rw) void RenderingManager::windowClosed(Ogre::RenderWindow* rw) { + Ogre::Root::getSingleton ().queueEndRendering (); } bool RenderingManager::waterShaderSupported() From 6534c2a55a76a269a2c3f9c498268ba91b2ce047 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 12 Aug 2012 18:11:09 +0200 Subject: [PATCH 406/688] Issue #107: WindowManager is accessed only through the interface class from now on --- apps/openmw/CMakeLists.txt | 4 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/environment.cpp | 9 +- apps/openmw/mwbase/environment.hpp | 12 +- apps/openmw/mwbase/windowmanager.hpp | 210 ++++++++++++++ apps/openmw/mwclass/activator.cpp | 5 +- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/misc.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 7 +- apps/openmw/mwdialogue/journalimp.cpp | 2 +- apps/openmw/mwgui/alchemywindow.cpp | 5 +- apps/openmw/mwgui/alchemywindow.hpp | 2 +- apps/openmw/mwgui/birth.cpp | 4 +- apps/openmw/mwgui/birth.hpp | 5 +- apps/openmw/mwgui/bookwindow.cpp | 4 +- apps/openmw/mwgui/bookwindow.hpp | 3 +- apps/openmw/mwgui/charactercreation.cpp | 2 +- apps/openmw/mwgui/charactercreation.hpp | 8 +- apps/openmw/mwgui/class.cpp | 20 +- apps/openmw/mwgui/class.hpp | 21 +- apps/openmw/mwgui/confirmationdialog.cpp | 2 +- apps/openmw/mwgui/confirmationdialog.hpp | 2 +- apps/openmw/mwgui/container.cpp | 4 +- apps/openmw/mwgui/container.hpp | 2 +- apps/openmw/mwgui/countdialog.cpp | 2 +- apps/openmw/mwgui/countdialog.hpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 4 +- apps/openmw/mwgui/dialogue.hpp | 2 +- apps/openmw/mwgui/dialogue_history.cpp | 5 +- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/inventorywindow.cpp | 4 +- apps/openmw/mwgui/inventorywindow.hpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 5 +- apps/openmw/mwgui/journalwindow.hpp | 4 +- apps/openmw/mwgui/mainmenu.cpp | 4 +- apps/openmw/mwgui/map_window.cpp | 5 +- apps/openmw/mwgui/map_window.hpp | 4 +- apps/openmw/mwgui/messagebox.cpp | 2 +- apps/openmw/mwgui/messagebox.hpp | 28 +- apps/openmw/mwgui/race.cpp | 4 +- apps/openmw/mwgui/race.hpp | 2 +- apps/openmw/mwgui/review.cpp | 4 +- apps/openmw/mwgui/review.hpp | 2 +- apps/openmw/mwgui/scrollwindow.cpp | 4 +- apps/openmw/mwgui/scrollwindow.hpp | 2 +- apps/openmw/mwgui/settingswindow.cpp | 4 +- apps/openmw/mwgui/settingswindow.hpp | 3 +- apps/openmw/mwgui/spellwindow.cpp | 4 +- apps/openmw/mwgui/spellwindow.hpp | 2 +- apps/openmw/mwgui/stats_window.cpp | 4 +- apps/openmw/mwgui/stats_window.hpp | 3 +- apps/openmw/mwgui/text_input.cpp | 5 +- apps/openmw/mwgui/text_input.hpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 4 +- apps/openmw/mwgui/tooltips.hpp | 8 +- apps/openmw/mwgui/tradewindow.cpp | 4 +- apps/openmw/mwgui/tradewindow.hpp | 2 +- apps/openmw/mwgui/widgets.cpp | 3 +- apps/openmw/mwgui/widgets.hpp | 33 ++- apps/openmw/mwgui/window_base.cpp | 5 +- apps/openmw/mwgui/window_base.hpp | 11 +- apps/openmw/mwgui/window_manager.hpp | 274 ------------------ apps/openmw/mwgui/window_pinnable_base.cpp | 6 +- apps/openmw/mwgui/window_pinnable_base.hpp | 5 +- ...indow_manager.cpp => windowmanagerimp.cpp} | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 254 ++++++++++++++++ apps/openmw/mwinput/inputmanagerimp.cpp | 8 +- apps/openmw/mwinput/inputmanagerimp.hpp | 4 +- .../mwmechanics/mechanicsmanagerimp.cpp | 7 +- apps/openmw/mwrender/localmap.cpp | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 3 +- apps/openmw/mwscript/guiextensions.cpp | 3 +- apps/openmw/mwscript/interpretercontext.cpp | 3 +- apps/openmw/mwworld/actionalchemy.cpp | 2 +- apps/openmw/mwworld/actionopen.cpp | 3 +- apps/openmw/mwworld/actionread.cpp | 3 +- apps/openmw/mwworld/actiontake.cpp | 3 +- apps/openmw/mwworld/scene.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 3 +- 93 files changed, 662 insertions(+), 484 deletions(-) create mode 100644 apps/openmw/mwbase/windowmanager.hpp delete mode 100644 apps/openmw/mwgui/window_manager.hpp rename apps/openmw/mwgui/{window_manager.cpp => windowmanagerimp.cpp} (99%) create mode 100644 apps/openmw/mwgui/windowmanagerimp.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 7bd7f1882..02fe0b72c 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -25,7 +25,7 @@ add_openmw_dir (mwinput ) add_openmw_dir (mwgui - text_input widgets race class birth review window_manager console dialogue + text_input widgets race class birth review windowmanagerimp console dialogue dialogue_history window_base stats_window messagebox journalwindow charactercreation map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow @@ -66,7 +66,7 @@ add_openmw_dir (mwmechanics add_openmw_dir (mwbase environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager - inputmanager + inputmanager windowmanager ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c484c22ea..75835120f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -15,7 +15,7 @@ #include "mwinput/inputmanagerimp.hpp" -#include "mwgui/window_manager.hpp" +#include "mwgui/windowmanagerimp.hpp" #include "mwgui/cursorreplace.hpp" #include "mwscript/scriptmanagerimp.hpp" diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 28e71fcf0..8d786db91 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -10,6 +10,7 @@ #include "soundmanager.hpp" #include "mechanicsmanager.hpp" #include "inputmanager.hpp" +#include "windowmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; @@ -42,7 +43,7 @@ void MWBase::Environment::setScriptManager (ScriptManager *scriptManager) mScriptManager = scriptManager; } -void MWBase::Environment::setWindowManager (MWGui::WindowManager *windowManager) +void MWBase::Environment::setWindowManager (WindowManager *windowManager) { mWindowManager = windowManager; } @@ -90,7 +91,7 @@ MWBase::ScriptManager *MWBase::Environment::getScriptManager() const return mScriptManager; } -MWGui::WindowManager *MWBase::Environment::getWindowManager() const +MWBase::WindowManager *MWBase::Environment::getWindowManager() const { assert (mWindowManager); return mWindowManager; @@ -145,6 +146,10 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; +/// \todo Re-enable (currently throwing an exception) +// delete mWindowManager; + mWindowManager = 0; + delete mWorld; mWorld = 0; } diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 4a21b57a4..a80e7ef87 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -1,11 +1,6 @@ #ifndef GAME_BASE_INVIRONMENT_H #define GAME_BASE_INVIRONMENT_H -namespace MWGui -{ - class WindowManager; -} - namespace MWBase { class World; @@ -15,6 +10,7 @@ namespace MWBase class SoundManager; class MechanicsManager; class InputManager; + class WindowManager; /// \brief Central hub for mw-subsystems /// @@ -29,7 +25,7 @@ namespace MWBase World *mWorld; SoundManager *mSoundManager; ScriptManager *mScriptManager; - MWGui::WindowManager *mWindowManager; + WindowManager *mWindowManager; MechanicsManager *mMechanicsManager; DialogueManager *mDialogueManager; Journal *mJournal; @@ -54,7 +50,7 @@ namespace MWBase void setScriptManager (MWBase::ScriptManager *scriptManager); - void setWindowManager (MWGui::WindowManager *windowManager); + void setWindowManager (WindowManager *windowManager); void setMechanicsManager (MechanicsManager *mechanicsManager); @@ -73,7 +69,7 @@ namespace MWBase ScriptManager *getScriptManager() const; - MWGui::WindowManager *getWindowManager() const; + WindowManager *getWindowManager() const; MechanicsManager *getMechanicsManager() const; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp new file mode 100644 index 000000000..931353a90 --- /dev/null +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -0,0 +1,210 @@ +#ifndef GAME_MWBASE_WINDOWMANAGER_H +#define GAME_MWBASE_WINDOWMANAGER_H + +#include +#include +#include + +#include + +#include "../mwmechanics/stat.hpp" + +#include "../mwgui/mode.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; + class UString; +} + +namespace OEngine +{ + namespace GUI + { + class Layout; + } +} + +namespace ESM +{ + struct Class; +} + +namespace MWWorld +{ + class CellStore; + class Ptr; +} + +namespace MWGui +{ + class Console; + class SpellWindow; + class TradeWindow; + class ConfirmationDialog; + class CountDialog; + class ScrollWindow; + class BookWindow; + class InventoryWindow; + class ContainerWindow; + class DialogueWindow; +} + +namespace MWBase +{ + /// \brief Interface for widnow manager (implemented in MWGui) + class WindowManager + { + WindowManager (const WindowManager&); + ///< not implemented + + WindowManager& operator= (const WindowManager&); + ///< not implemented + + public: + + typedef std::vector SkillList; + + WindowManager() {} + + virtual ~WindowManager() {} + + /** + * Should be called each frame to update windows/gui elements. + * This could mean updating sizes of gui elements or opening + * new dialogs. + */ + virtual void update() = 0; + + virtual void pushGuiMode (MWGui::GuiMode mode) = 0; + virtual void popGuiMode() = 0; + + virtual void removeGuiMode (MWGui::GuiMode mode) = 0; + ///< can be anywhere in the stack + + virtual MWGui::GuiMode getMode() const = 0; + + virtual bool isGuiMode() const = 0; + + virtual void toggleVisible (MWGui::GuiWindow wnd) = 0; + + /// Disallow all inventory mode windows + virtual void disallowAll() = 0; + + /// Allow one or more windows + virtual void allow (MWGui::GuiWindow wnd) = 0; + + virtual bool isAllowed (MWGui::GuiWindow wnd) const = 0; + + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world + virtual MWGui::DialogueWindow* getDialogueWindow() = 0; + virtual MWGui::ContainerWindow* getContainerWindow() = 0; + virtual MWGui::InventoryWindow* getInventoryWindow() = 0; + virtual MWGui::BookWindow* getBookWindow() = 0; + virtual MWGui::ScrollWindow* getScrollWindow() = 0; + virtual MWGui::CountDialog* getCountDialog() = 0; + virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; + virtual MWGui::TradeWindow* getTradeWindow() = 0; + virtual MWGui::SpellWindow* getSpellWindow() = 0; + virtual MWGui::Console* getConsole() = 0; + + virtual MyGUI::Gui* getGui() const = 0; + + virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount) = 0; + + /// Set value for the given ID. + virtual void setValue (const std::string& id, const MWMechanics::Stat& value) = 0; + virtual void setValue (int parSkill, const MWMechanics::Stat& value) = 0; + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; + virtual void setValue (const std::string& id, const std::string& value) = 0; + virtual void setValue (const std::string& id, int value) = 0; + + virtual void setPlayerClass (const ESM::Class &class_) = 0; + ///< set current class of player + + virtual void configureSkills (const SkillList& major, const SkillList& minor) = 0; + ///< configure skill groups, each set contains the skill ID for that group. + + virtual void setReputation (int reputation) = 0; + ///< set the current reputation value + + virtual void setBounty (int bounty) = 0; + ///< set the current bounty value + + virtual void updateSkillArea() = 0; + ///< update display of skills, factions, birth sign, reputation and bounty + + virtual void changeCell(MWWorld::CellStore* cell) = 0; + ///< change the active cell + + virtual void setPlayerPos(const float x, const float y) = 0; + ///< set player position in map space + + virtual void setPlayerDir(const float x, const float y) = 0; + ///< set player view direction in map space + + virtual void setFocusObject(const MWWorld::Ptr& focus) = 0; + virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) = 0; + + virtual void setMouseVisible(bool visible) = 0; + virtual void getMousePosition(int &x, int &y) = 0; + virtual void getMousePosition(float &x, float &y) = 0; + virtual void setDragDrop(bool dragDrop) = 0; + virtual bool getWorldMouseOver() = 0; + + virtual void toggleFogOfWar() = 0; + + virtual void toggleFullHelp() = 0; + ///< show extra info in item tooltips (owner, script) + + virtual bool getFullHelp() const = 0; + + virtual void setInteriorMapTexture(const int x, const int y) = 0; + ///< set the index of the map texture that should be used (for interiors) + + /// sets the visibility of the hud health/magicka/stamina bars + virtual void setHMSVisibility(bool visible) = 0; + + /// sets the visibility of the hud minimap + virtual void setMinimapVisibility(bool visible) = 0; + virtual void setWeaponVisibility(bool visible) = 0; + virtual void setSpellVisibility(bool visible) = 0; + + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) = 0; + virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) = 0; + virtual void unsetSelectedSpell() = 0; + virtual void unsetSelectedWeapon() = 0; + + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; + ///< Hides dialog and schedules dialog to be deleted. + + virtual void messageBox (const std::string& message, const std::vector& buttons) = 0; + virtual int readPressedButton() = 0; + ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + + virtual void onFrame (float frameDuration) = 0; + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + virtual std::map > getPlayerSkillValues() = 0; + virtual std::map > getPlayerAttributeValues() = 0; + virtual SkillList getPlayerMinorSkills() = 0; + virtual SkillList getPlayerMajorSkills() = 0; + + /** + * Fetches a GMST string from the store, if there is no setting with the given + * ID or it is not a string the default string is returned. + * + * @param id Identifier for the GMST setting, e.g. "aName" + * @param default Default value if the GMST setting cannot be used. + */ + virtual const std::string &getGameSettingString(const std::string &id, const std::string &default_) = 0; + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + + virtual void executeInConsole (const std::string& path) = 0; + }; +} + +#endif diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 9b0082efc..7b8cbd3da 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -4,6 +4,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" @@ -12,7 +13,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass @@ -34,7 +34,7 @@ namespace MWClass physics.insertObjectPhysics(ptr, model); } } - + std::string Activator::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = @@ -104,4 +104,3 @@ namespace MWClass return MWWorld::Ptr(&cell.activators.insert(*ref), &cell); } } - diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 3c22cc5fb..8a117af5d 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -16,7 +17,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 43c1e0a43..c48d7ff4d 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -19,7 +20,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 29a53140a..1e187342d 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actionread.hpp" @@ -15,7 +16,6 @@ #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index ef91b0fac..597eb22fe 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwgui/tooltips.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 7d31b945d..ad6da90dd 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/nullaction.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/actionopen.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 57301e1a2..a22c2d661 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -17,7 +18,6 @@ #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 19eda16ef..3b283a9d1 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index eb92accb6..8f9f8a315 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -6,13 +6,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index ed8fd1de6..40ecf0a0f 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 1472b7e8b..8fdd95760 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 8b5b41495..2cd0f63a1 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -15,7 +16,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/manualref.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 30173701e..94c98ba42 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -12,6 +12,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -26,7 +27,6 @@ #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" namespace diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 27903fdf7..6bff85553 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 8a563865d..25aae445b 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 096d3d0ee..ebba8c44e 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -6,13 +6,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index bc8e3e648..2a9863cdf 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -6,6 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" @@ -14,7 +15,6 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 251577d3b..acd786892 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -13,6 +13,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/journal.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -20,22 +21,20 @@ #include "../mwworld/containerstore.hpp" #include "../mwgui/dialogue.hpp" -#include "../mwgui/window_manager.hpp" #include -#include "../mwscript/extensions.hpp" - #include #include #include #include #include +#include #include #include "../mwscript/compilercontext.hpp" #include "../mwscript/interpretercontext.hpp" -#include +#include "../mwscript/extensions.hpp" #include "../mwclass/npc.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index dc322cade..d626cd315 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -5,8 +5,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/messagebox.hpp" namespace MWDialogue diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 81e5640d4..5cf09b18a 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -5,13 +5,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" -#include "window_manager.hpp" - namespace { std::string getIconPath(MWWorld::Ptr ptr) @@ -27,7 +26,7 @@ namespace namespace MWGui { - AlchemyWindow::AlchemyWindow(WindowManager& parWindowManager) + AlchemyWindow::AlchemyWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_alchemy_window.layout", parWindowManager) , ContainerBase(0) { diff --git a/apps/openmw/mwgui/alchemywindow.hpp b/apps/openmw/mwgui/alchemywindow.hpp index 81c33a96d..53e7178d5 100644 --- a/apps/openmw/mwgui/alchemywindow.hpp +++ b/apps/openmw/mwgui/alchemywindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class AlchemyWindow : public WindowBase, public ContainerBase { public: - AlchemyWindow(WindowManager& parWindowManager); + AlchemyWindow(MWBase::WindowManager& parWindowManager); virtual void open(); diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 023702c8b..05a337cbc 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -7,14 +7,14 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" using namespace MWGui; using namespace Widgets; -BirthDialog::BirthDialog(WindowManager& parWindowManager) +BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_birth.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 770e4ba36..92665081d 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -10,14 +10,13 @@ namespace MWGui { + /// \todo remove using namespace MyGUI; - class WindowManager; - class BirthDialog : public WindowBase { public: - BirthDialog(WindowManager& parWindowManager); + BirthDialog(MWBase::WindowManager& parWindowManager); enum Gender { diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 92f0226ed..57e59657a 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -5,16 +5,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" #include "formatting.hpp" -#include "window_manager.hpp" using namespace MWGui; -BookWindow::BookWindow (WindowManager& parWindowManager) : +BookWindow::BookWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_book.layout", parWindowManager) { getWidget(mCloseButton, "CloseButton"); diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index 9ea011433..fedb783b2 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class BookWindow : public WindowBase { public: - BookWindow(WindowManager& parWindowManager); + BookWindow(MWBase::WindowManager& parWindowManager); void open(MWWorld::Ptr book); void setTakeButtonShow(bool show); @@ -43,4 +43,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 72394de80..8c82b3e43 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -116,7 +116,7 @@ namespace using namespace MWGui; -CharacterCreation::CharacterCreation(WindowManager* _wm) +CharacterCreation::CharacterCreation(MWBase::WindowManager* _wm) : mNameDialog(0) , mRaceDialog(0) , mClassChoiceDialog(0) diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 1fd67bff7..d65763d0c 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -1,17 +1,15 @@ #ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP -#include "window_manager.hpp" - #include #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/stat.hpp" namespace MWGui { - class WindowManager; class WindowBase; class TextInputDialog; @@ -31,7 +29,7 @@ namespace MWGui public: typedef std::vector SkillList; - CharacterCreation(WindowManager* _wm); + CharacterCreation(MWBase::WindowManager* _wm); ~CharacterCreation(); //Show a dialog @@ -60,7 +58,7 @@ namespace MWGui BirthDialog* mBirthSignDialog; ReviewDialog* mReviewDialog; - WindowManager* mWM; + MWBase::WindowManager* mWM; //Player data std::string mPlayerName; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 7e3194d6b..eaf191819 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -9,8 +9,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "tooltips.hpp" #undef min @@ -20,7 +20,7 @@ using namespace MWGui; /* GenerateClassResultDialog */ -GenerateClassResultDialog::GenerateClassResultDialog(WindowManager& parWindowManager) +GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager) { // Centre dialog @@ -77,7 +77,7 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) /* PickClassDialog */ -PickClassDialog::PickClassDialog(WindowManager& parWindowManager) +PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_class.layout", parWindowManager) { // Centre dialog @@ -283,7 +283,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin) widget->setSize(width, pos); } -InfoBoxDialog::InfoBoxDialog(WindowManager& parWindowManager) +InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_infobox.layout", parWindowManager) , mCurrentButton(-1) { @@ -367,7 +367,7 @@ void InfoBoxDialog::onButtonClicked(MyGUI::WidgetPtr _sender) /* ClassChoiceDialog */ -ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) +ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager) : InfoBoxDialog(parWindowManager) { setText(""); @@ -381,7 +381,7 @@ ClassChoiceDialog::ClassChoiceDialog(WindowManager& parWindowManager) /* CreateClassDialog */ -CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager) +CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_create_class.layout", parWindowManager) , mSpecDialog(nullptr) , mAttribDialog(nullptr) @@ -694,7 +694,7 @@ void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) /* SelectSpecializationDialog */ -SelectSpecializationDialog::SelectSpecializationDialog(WindowManager& parWindowManager) +SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_specialization.layout", parWindowManager) { // Centre dialog @@ -759,7 +759,7 @@ void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectAttributeDialog */ -SelectAttributeDialog::SelectAttributeDialog(WindowManager& parWindowManager) +SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_attribute.layout", parWindowManager) { // Centre dialog @@ -811,7 +811,7 @@ void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectSkillDialog */ -SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager) +SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_select_skill.layout", parWindowManager) { // Centre dialog @@ -907,7 +907,7 @@ void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) /* DescriptionDialog */ -DescriptionDialog::DescriptionDialog(WindowManager& parWindowManager) +DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_class_description.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 4d8d9fa23..4baceed1e 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -12,14 +12,13 @@ namespace MWGui { + /// \todo remove! using namespace MyGUI; - class WindowManager; - class InfoBoxDialog : public WindowBase { public: - InfoBoxDialog(WindowManager& parWindowManager); + InfoBoxDialog(MWBase::WindowManager& parWindowManager); typedef std::vector ButtonList; @@ -64,13 +63,13 @@ namespace MWGui Class_Create = 2, Class_Back = 3 }; - ClassChoiceDialog(WindowManager& parWindowManager); + ClassChoiceDialog(MWBase::WindowManager& parWindowManager); }; class GenerateClassResultDialog : public WindowBase { public: - GenerateClassResultDialog(WindowManager& parWindowManager); + GenerateClassResultDialog(MWBase::WindowManager& parWindowManager); std::string getClassId() const; void setClassId(const std::string &classId); @@ -99,7 +98,7 @@ namespace MWGui class PickClassDialog : public WindowBase { public: - PickClassDialog(WindowManager& parWindowManager); + PickClassDialog(MWBase::WindowManager& parWindowManager); const std::string &getClassId() const { return mCurrentClassId; } void setClassId(const std::string &classId); @@ -138,7 +137,7 @@ namespace MWGui class SelectSpecializationDialog : public WindowBase { public: - SelectSpecializationDialog(WindowManager& parWindowManager); + SelectSpecializationDialog(MWBase::WindowManager& parWindowManager); ~SelectSpecializationDialog(); ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } @@ -169,7 +168,7 @@ namespace MWGui class SelectAttributeDialog : public WindowBase { public: - SelectAttributeDialog(WindowManager& parWindowManager); + SelectAttributeDialog(MWBase::WindowManager& parWindowManager); ~SelectAttributeDialog(); ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } @@ -202,7 +201,7 @@ namespace MWGui class SelectSkillDialog : public WindowBase { public: - SelectSkillDialog(WindowManager& parWindowManager); + SelectSkillDialog(MWBase::WindowManager& parWindowManager); ~SelectSkillDialog(); ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } @@ -238,7 +237,7 @@ namespace MWGui class DescriptionDialog : public WindowBase { public: - DescriptionDialog(WindowManager& parWindowManager); + DescriptionDialog(MWBase::WindowManager& parWindowManager); ~DescriptionDialog(); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } @@ -254,7 +253,7 @@ namespace MWGui class CreateClassDialog : public WindowBase { public: - CreateClassDialog(WindowManager& parWindowManager); + CreateClassDialog(MWBase::WindowManager& parWindowManager); virtual ~CreateClassDialog(); std::string getName() const; diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 00bdca638..1c68da9e5 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -7,7 +7,7 @@ namespace MWGui { - ConfirmationDialog::ConfirmationDialog(WindowManager& parWindowManager) : + ConfirmationDialog::ConfirmationDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_confirmation_dialog.layout", parWindowManager) { getWidget(mMessage, "Message"); diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index d278274a0..a78028b1c 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -8,7 +8,7 @@ namespace MWGui class ConfirmationDialog : public WindowBase { public: - ConfirmationDialog(WindowManager& parWindowManager); + ConfirmationDialog(MWBase::WindowManager& parWindowManager); void open(const std::string& message); typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 89cd10233..75e2bb3b8 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/containerstore.hpp" @@ -19,7 +20,6 @@ #include "../mwclass/container.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" @@ -591,7 +591,7 @@ MWWorld::ContainerStore& ContainerBase::getContainerStore() // ------------------------------------------------------------------------------------------------ -ContainerWindow::ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) +ContainerWindow::ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) , WindowBase("openmw_container_window.layout", parWindowManager) { diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 88e445a7b..27c3288ae 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -127,7 +127,7 @@ namespace MWGui class ContainerWindow : public ContainerBase, public WindowBase { public: - ContainerWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + ContainerWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); virtual ~ContainerWindow(); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 07f1acb73..6d9415354 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -7,7 +7,7 @@ namespace MWGui { - CountDialog::CountDialog(WindowManager& parWindowManager) : + CountDialog::CountDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_count_window.layout", parWindowManager) { getWidget(mSlider, "CountSlider"); diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index aac17b846..6002dadfe 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -8,7 +8,7 @@ namespace MWGui class CountDialog : public WindowBase { public: - CountDialog(WindowManager& parWindowManager); + CountDialog(MWBase::WindowManager& parWindowManager); void open(const std::string& item, const std::string& message, const int maxCount); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 30089dd46..4342b1130 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -11,9 +11,9 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "dialogue_history.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" @@ -42,7 +42,7 @@ std::string::size_type find_str_ci(const std::string& str, const std::string& su } -DialogueWindow::DialogueWindow(WindowManager& parWindowManager) +DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_dialogue_window.layout", parWindowManager) , mEnabled(true) , mShowTrade(false) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index e2824bead..e7f2b076c 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -29,7 +29,7 @@ namespace MWGui class DialogueWindow: public WindowBase, public ReferenceInterface { public: - DialogueWindow(WindowManager& parWindowManager); + DialogueWindow(MWBase::WindowManager& parWindowManager); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp index 009f42044..f72f199ea 100644 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ b/apps/openmw/mwgui/dialogue_history.cpp @@ -1,5 +1,7 @@ #include "dialogue_history.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" + #include "widgets.hpp" #include "components/esm_store/store.hpp" @@ -71,4 +73,3 @@ void DialogueHistory::addDialogText(const UString& parText) addText(parText); addText("\n"); } - diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 1c7943386..bdbb316b0 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -9,6 +9,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -16,7 +17,6 @@ #include "../mwgui/widgets.hpp" #include "inventorywindow.hpp" -#include "window_manager.hpp" #include "container.hpp" #include "console.hpp" diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 090b7dd6e..7b68f7b6d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -10,6 +10,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" @@ -18,7 +19,6 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/inventorystore.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "bookwindow.hpp" #include "scrollwindow.hpp" @@ -40,7 +40,7 @@ namespace namespace MWGui { - InventoryWindow::InventoryWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop) + InventoryWindow::InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop) : ContainerBase(dragAndDrop) , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) , mTrading(false) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 82da3efea..fbdb79977 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -9,7 +9,7 @@ namespace MWGui class InventoryWindow : public ContainerBase, public WindowPinnableBase { public: - InventoryWindow(WindowManager& parWindowManager,DragAndDrop* dragAndDrop); + InventoryWindow(MWBase::WindowManager& parWindowManager,DragAndDrop* dragAndDrop); virtual void open(); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 475a70631..597c27c6d 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -4,11 +4,10 @@ #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwdialogue/journalentry.hpp" -#include "window_manager.hpp" - namespace { struct book @@ -82,7 +81,7 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) } -MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager) +MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_journal.layout", parWindowManager) , mLastPos(0) , mVisible(false) diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 0c85ebf08..fc05bbdbc 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -10,12 +10,10 @@ namespace MWGui { - class WindowManager; - class JournalWindow : public WindowBase { public: - JournalWindow(WindowManager& parWindowManager); + JournalWindow(MWBase::WindowManager& parWindowManager); void open(); virtual void setVisible(bool visible); // only used to play close sound diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e2fefd649..bb6b8163e 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -2,11 +2,9 @@ #include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "window_manager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 4ebeb3874..1ffedaac4 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -1,5 +1,6 @@ #include "map_window.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" #include @@ -154,7 +155,7 @@ void LocalMapBase::setPlayerDir(const float x, const float y) // ------------------------------------------------------------------------------------------ -MapWindow::MapWindow(WindowManager& parWindowManager) : +MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager), mGlobal(false) { diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 8d3392b82..447c16901 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -45,11 +45,11 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase { public: - MapWindow(WindowManager& parWindowManager); + MapWindow(MWBase::WindowManager& parWindowManager); virtual ~MapWindow(){} void setCellName(const std::string& cellName); - + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 2b00ca05c..b660af7dd 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -2,7 +2,7 @@ using namespace MWGui; -MessageBoxManager::MessageBoxManager (WindowManager *windowManager) +MessageBoxManager::MessageBoxManager (MWBase::WindowManager *windowManager) { mWindowManager = windowManager; // defines diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 75393ec94..5e4c468d5 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -5,13 +5,13 @@ #include #include "window_base.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" #undef MessageBox namespace MWGui { - class InteractiveMessageBox; class MessageBoxManager; class MessageBox; @@ -25,27 +25,27 @@ namespace MWGui class MessageBoxManager { public: - MessageBoxManager (WindowManager* windowManager); + MessageBoxManager (MWBase::WindowManager* windowManager); void onFrame (float frameDuration); void createMessageBox (const std::string& message); bool createInteractiveMessageBox (const std::string& message, const std::vector& buttons); bool isInteractiveMessageBox (); - + void removeMessageBox (float time, MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - + int readPressedButton (); - - WindowManager *mWindowManager; - + + MWBase::WindowManager *mWindowManager; + private: std::vector mMessageBoxes; InteractiveMessageBox* mInterMessageBoxe; std::vector mTimers; float mMessageBoxSpeed; }; - + class MessageBox : public OEngine::GUI::Layout { public: @@ -53,9 +53,9 @@ namespace MWGui void setMessage (const std::string& message); int getHeight (); void update (int height); - + bool mMarkedToDelete; - + protected: MessageBoxManager& mMessageBoxManager; int mHeight; @@ -65,16 +65,16 @@ namespace MWGui int mBottomPadding; int mNextBoxPadding; }; - + class InteractiveMessageBox : public OEngine::GUI::Layout { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); - + bool mMarkedToDelete; - + private: MessageBoxManager& mMessageBoxManager; MyGUI::EditPtr mMessageWidget; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 62434cb91..ceb0452fb 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -10,15 +10,15 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" using namespace MWGui; using namespace Widgets; -RaceDialog::RaceDialog(WindowManager& parWindowManager) +RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_race.layout", parWindowManager) , mGenderIndex(0) , mFaceIndex(0) diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index b523b8690..3da6b0ace 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -24,7 +24,7 @@ namespace MWGui class RaceDialog : public WindowBase { public: - RaceDialog(WindowManager& parWindowManager); + RaceDialog(MWBase::WindowManager& parWindowManager); enum Gender { diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 997899c52..8dd894c25 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -9,8 +9,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" -#include "window_manager.hpp" #include "widgets.hpp" #include "tooltips.hpp" @@ -22,7 +22,7 @@ using namespace Widgets; const int ReviewDialog::sLineHeight = 18; -ReviewDialog::ReviewDialog(WindowManager& parWindowManager) +ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_chargen_review.layout", parWindowManager) , mLastPos(0) { diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 27b167033..f0bde6ecd 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -30,7 +30,7 @@ namespace MWGui }; typedef std::vector SkillList; - ReviewDialog(WindowManager& parWindowManager); + ReviewDialog(MWBase::WindowManager& parWindowManager); void setPlayerName(const std::string &name); void setRace(const std::string &raceId); diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index fb2239c6d..ff83b0e3e 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -3,16 +3,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/actiontake.hpp" #include "../mwworld/player.hpp" #include "formatting.hpp" -#include "window_manager.hpp" using namespace MWGui; -ScrollWindow::ScrollWindow (WindowManager& parWindowManager) : +ScrollWindow::ScrollWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_scroll.layout", parWindowManager) { getWidget(mTextView, "TextView"); diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index d58596b4b..b8f52fb65 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -10,7 +10,7 @@ namespace MWGui class ScrollWindow : public WindowBase { public: - ScrollWindow (WindowManager& parWindowManager); + ScrollWindow (MWBase::WindowManager& parWindowManager); void open (MWWorld::Ptr scroll); void setTakeButtonShow(bool show); diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 06177aaa8..f6597a64e 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -14,10 +14,10 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwrender/renderingmanager.hpp" -#include "window_manager.hpp" #include "confirmationdialog.hpp" namespace @@ -81,7 +81,7 @@ namespace namespace MWGui { - SettingsWindow::SettingsWindow(WindowManager& parWindowManager) : + SettingsWindow::SettingsWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_settings_window.layout", parWindowManager) { getWidget(mOkButton, "OkButton"); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 63fbed46b..ca11b6f9c 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -13,7 +13,7 @@ namespace MWGui class SettingsWindow : public WindowBase { public: - SettingsWindow(WindowManager& parWindowManager); + SettingsWindow(MWBase::WindowManager& parWindowManager); private: static int const sFovMin = 30; @@ -77,4 +77,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index b528eecc2..8754f5d10 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -9,6 +9,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -17,7 +18,6 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellsuccess.hpp" -#include "window_manager.hpp" #include "inventorywindow.hpp" #include "confirmationdialog.hpp" @@ -42,7 +42,7 @@ namespace namespace MWGui { - SpellWindow::SpellWindow(WindowManager& parWindowManager) + SpellWindow::SpellWindow(MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_spell_window.layout", parWindowManager) , mHeight(0) , mWidth(0) diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 87e4783ba..caa67fd74 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -8,7 +8,7 @@ namespace MWGui class SpellWindow : public WindowPinnableBase { public: - SpellWindow(WindowManager& parWindowManager); + SpellWindow(MWBase::WindowManager& parWindowManager); void updateSpells(); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 4ee56abc7..32dac71de 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -9,20 +9,20 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwmechanics/npcstats.hpp" -#include "window_manager.hpp" #include "tooltips.hpp" using namespace MWGui; const int StatsWindow::sLineHeight = 18; -StatsWindow::StatsWindow (WindowManager& parWindowManager) +StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) , mSkillAreaWidget(NULL) , mSkillClientWidget(NULL) diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index 2469c12e9..a46ab7531 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -22,7 +22,7 @@ namespace MWGui typedef std::vector SkillList; - StatsWindow(WindowManager& parWindowManager); + StatsWindow(MWBase::WindowManager& parWindowManager); /// automatically updates all the data in the stats window, but only if it has changed. void onFrame(); @@ -82,4 +82,3 @@ namespace MWGui }; } #endif - diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 7d5b0cc6d..802cd3063 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -1,9 +1,10 @@ #include "text_input.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" using namespace MWGui; -TextInputDialog::TextInputDialog(WindowManager& parWindowManager) +TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_text_input.layout", parWindowManager) { // Centre dialog diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index 835d5deaa..7a3325722 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -18,7 +18,7 @@ namespace MWGui class TextInputDialog : public WindowBase { public: - TextInputDialog(WindowManager& parWindowManager); + TextInputDialog(MWBase::WindowManager& parWindowManager); std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 679c7a59e..edc787cef 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -8,16 +8,16 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" -#include "window_manager.hpp" #include "widgets.hpp" using namespace MWGui; using namespace MyGUI; -ToolTips::ToolTips(WindowManager* windowManager) : +ToolTips::ToolTips(MWBase::WindowManager* windowManager) : Layout("openmw_tooltips.layout") , mGameMode(true) , mWindowManager(windowManager) diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 036bdfaa3..700f5f723 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -9,8 +9,6 @@ namespace MWGui { - class WindowManager; - // Info about tooltip that is supplied by the MWWorld::Class object struct ToolTipInfo { @@ -29,7 +27,7 @@ namespace MWGui class ToolTips : public OEngine::GUI::Layout { public: - ToolTips(WindowManager* windowManager); + ToolTips(MWBase::WindowManager* windowManager); void onFrame(float frameDuration); @@ -45,7 +43,7 @@ namespace MWGui void setFocusObject(const MWWorld::Ptr& focus); void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); - ///< set the screen-space position of the tooltip for focused object + ///< set the screen-space position of the tooltip for focused object static std::string getValueString(const int value, const std::string& prefix); ///< @return "prefix: value" or "" if value is 0 @@ -71,7 +69,7 @@ namespace MWGui private: MyGUI::Widget* mDynamicToolTipBox; - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; MWWorld::Ptr mFocusObject; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 75e39fd87..8471367c6 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -5,16 +5,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/manualref.hpp" -#include "window_manager.hpp" #include "inventorywindow.hpp" namespace MWGui { - TradeWindow::TradeWindow(WindowManager& parWindowManager) : + TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_trade_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop , mCurrentBalance(0) diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index b391fd8fa..1daeefa96 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -23,7 +23,7 @@ namespace MWGui class TradeWindow : public ContainerBase, public WindowBase { public: - TradeWindow(WindowManager& parWindowManager); + TradeWindow(MWBase::WindowManager& parWindowManager); void startTrade(MWWorld::Ptr actor); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 152938707..40ee1c7ad 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -6,8 +6,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #undef min #undef max diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index d4947895b..9a2762369 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -10,14 +10,19 @@ #undef MYGUI_EXPORT #define MYGUI_EXPORT +namespace MWBase +{ + class WindowManager; +} + /* This file contains various custom widgets used in OpenMW. */ namespace MWGui { + /// \todo remove! using namespace MyGUI; - class WindowManager; namespace Widgets { @@ -81,12 +86,12 @@ namespace MWGui typedef MWMechanics::Stat SkillValue; - void setWindowManager(WindowManager *m) { mManager = m; } + void setWindowManager(MWBase::WindowManager *m) { mManager = m; } /// \todo remove void setSkillId(ESM::Skill::SkillEnum skillId); void setSkillNumber(int skillId); void setSkillValue(const SkillValue& value); - WindowManager *getWindowManager() const { return mManager; } + MWBase::WindowManager *getWindowManager() const { return mManager; } ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } const SkillValue& getSkillValue() const { return mValue; } @@ -109,7 +114,7 @@ namespace MWGui void updateWidgets(); - WindowManager *mManager; + MWBase::WindowManager *mManager; ESM::Skill::SkillEnum mSkillId; SkillValue mValue; MyGUI::WidgetPtr mSkillNameWidget, mSkillValueWidget; @@ -124,11 +129,11 @@ namespace MWGui typedef MWMechanics::Stat AttributeValue; - void setWindowManager(WindowManager *m) { mManager = m; } + void setWindowManager(MWBase::WindowManager *m) { mManager = m; } void setAttributeId(int attributeId); void setAttributeValue(const AttributeValue& value); - WindowManager *getWindowManager() const { return mManager; } + MWBase::WindowManager *getWindowManager() const { return mManager; } int getAttributeId() const { return mId; } const AttributeValue& getAttributeValue() const { return mValue; } @@ -151,7 +156,7 @@ namespace MWGui void updateWidgets(); - WindowManager *mManager; + MWBase::WindowManager *mManager; int mId; AttributeValue mValue; MyGUI::WidgetPtr mAttributeNameWidget, mAttributeValueWidget; @@ -170,7 +175,7 @@ namespace MWGui typedef MWMechanics::Stat SpellValue; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellId(const std::string &id); /** @@ -192,7 +197,7 @@ namespace MWGui private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; std::string mId; MyGUI::TextBox* mSpellNameWidget; }; @@ -212,7 +217,7 @@ namespace MWGui EF_Constant = 0x02 // constant effect means that duration will not be displayed }; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setEffectList(const SpellEffectList& list); static SpellEffectList effectListFromESM(const ESM::EffectList* effects); @@ -234,7 +239,7 @@ namespace MWGui private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; SpellEffectList mEffectList; }; typedef MWEffectList* MWEffectListPtr; @@ -247,7 +252,7 @@ namespace MWGui typedef ESM::ENAMstruct SpellEffectValue; - void setWindowManager(WindowManager* parWindowManager) { mWindowManager = parWindowManager; } + void setWindowManager(MWBase::WindowManager* parWindowManager) { mWindowManager = parWindowManager; } void setSpellEffect(const SpellEffectParams& params); std::string effectIDToString(const short effectID); @@ -262,12 +267,12 @@ namespace MWGui virtual ~MWSpellEffect(); virtual void initialiseOverride(); - + private: void updateWidgets(); - WindowManager* mWindowManager; + MWBase::WindowManager* mWindowManager; SpellEffectParams mEffectParams; MyGUI::ImageBox* mImageWidget; MyGUI::TextBox* mTextWidget; diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/window_base.cpp index 433057931..dbb37efbb 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/window_base.cpp @@ -1,11 +1,12 @@ #include "window_base.hpp" -#include "window_manager.hpp" #include +#include "../mwbase/windowmanager.hpp" + using namespace MWGui; -WindowBase::WindowBase(const std::string& parLayout, WindowManager& parWindowManager) +WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) : Layout(parLayout) , mWindowManager(parWindowManager) { diff --git a/apps/openmw/mwgui/window_base.hpp b/apps/openmw/mwgui/window_base.hpp index 9cfdbe261..74d874bb8 100644 --- a/apps/openmw/mwgui/window_base.hpp +++ b/apps/openmw/mwgui/window_base.hpp @@ -3,6 +3,11 @@ #include +namespace MWBase +{ + class WindowManager; +} + namespace MWGui { class WindowManager; @@ -10,7 +15,7 @@ namespace MWGui class WindowBase: public OEngine::GUI::Layout { public: - WindowBase(const std::string& parLayout, WindowManager& parWindowManager); + WindowBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; @@ -25,9 +30,9 @@ namespace MWGui EventHandle_WindowBase eventDone; protected: - WindowManager& mWindowManager; + /// \todo remove + MWBase::WindowManager& mWindowManager; }; } #endif - diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp deleted file mode 100644 index db3945e55..000000000 --- a/apps/openmw/mwgui/window_manager.hpp +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef MWGUI_WINDOWMANAGER_H -#define MWGUI_WINDOWMANAGER_H - -/** - This class owns and controls all the MW specific windows in the - GUI. It can enable/disable Gui mode, and is responsible for sending - and retrieving information from the Gui. - - MyGUI should be initialized separately before creating instances of - this class. -**/ - -#include -#include - -#include -#include - -#include "../mwmechanics/stat.hpp" - -#include "mode.hpp" - -namespace MyGUI -{ - class Gui; - class Widget; - class UString; -} - -namespace Compiler -{ - class Extensions; -} - -namespace MWWorld -{ - class Ptr; - class CellStore; -} - -namespace OEngine -{ - namespace GUI - { - class Layout; - class MyGUIManager; - } - - namespace Render - { - class OgreRenderer; - } -} - -namespace MWGui -{ - class WindowBase; - class HUD; - class MapWindow; - class MainMenu; - class StatsWindow; - class InventoryWindow; - class Console; - class JournalWindow; - class CharacterCreation; - class ContainerWindow; - class DragAndDrop; - class InventoryWindow; - class ToolTips; - class ScrollWindow; - class BookWindow; - class TextInputDialog; - class InfoBoxDialog; - class DialogueWindow; - class MessageBoxManager; - class CountDialog; - class TradeWindow; - class SettingsWindow; - class ConfirmationDialog; - class AlchemyWindow; - class SpellWindow; - - class WindowManager - { - public: - typedef std::pair Faction; - typedef std::vector FactionList; - typedef std::vector SkillList; - - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); - virtual ~WindowManager(); - - /** - * Should be called each frame to update windows/gui elements. - * This could mean updating sizes of gui elements or opening - * new dialogs. - */ - void update(); - - void pushGuiMode(GuiMode mode); - void popGuiMode(); - void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack - - GuiMode getMode() const; - - bool isGuiMode() const; - - void toggleVisible(GuiWindow wnd); - - // Disallow all inventory mode windows - void disallowAll(); - - // Allow one or more windows - void allow(GuiWindow wnd); - - bool isAllowed(GuiWindow wnd) const; - - /// \todo investigate, if we really need to expose every single lousy UI element to the outside world - MWGui::DialogueWindow* getDialogueWindow(); - MWGui::ContainerWindow* getContainerWindow(); - MWGui::InventoryWindow* getInventoryWindow(); - MWGui::BookWindow* getBookWindow(); - MWGui::ScrollWindow* getScrollWindow(); - MWGui::CountDialog* getCountDialog(); - MWGui::ConfirmationDialog* getConfirmationDialog(); - MWGui::TradeWindow* getTradeWindow(); - MWGui::SpellWindow* getSpellWindow(); - MWGui::Console* getConsole(); - - MyGUI::Gui* getGui() const; - - void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); - - ///< Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue (int parSkill, const MWMechanics::Stat& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - void setValue (const std::string& id, const std::string& value); - void setValue (const std::string& id, int value); - - void setPlayerClass (const ESM::Class &class_); ///< set current class of player - void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. - void setReputation (int reputation); ///< set the current reputation value - void setBounty (int bounty); ///< set the current bounty value - void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty - - void changeCell(MWWorld::CellStore* cell); ///< change the active cell - void setPlayerPos(const float x, const float y); ///< set player position in map space - void setPlayerDir(const float x, const float y); ///< set player view direction in map space - - void setFocusObject(const MWWorld::Ptr& focus); - void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); - - void setMouseVisible(bool visible); - void getMousePosition(int &x, int &y); - void getMousePosition(float &x, float &y); - void setDragDrop(bool dragDrop); - bool getWorldMouseOver(); - - void toggleFogOfWar(); - void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) - bool getFullHelp() const; - - void setInteriorMapTexture(const int x, const int y); - ///< set the index of the map texture that should be used (for interiors) - - // sets the visibility of the hud health/magicka/stamina bars - void setHMSVisibility(bool visible); - // sets the visibility of the hud minimap - void setMinimapVisibility(bool visible); - void setWeaponVisibility(bool visible); - void setSpellVisibility(bool visible); - - void setSelectedSpell(const std::string& spellId, int successChancePercent); - void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); - void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); - void unsetSelectedSpell(); - void unsetSelectedWeapon(); - - void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - - void messageBox (const std::string& message, const std::vector& buttons); - int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) - - void onFrame (float frameDuration); - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - std::map > getPlayerSkillValues(); - std::map > getPlayerAttributeValues(); - SkillList getPlayerMinorSkills(); - SkillList getPlayerMajorSkills(); - - /** - * Fetches a GMST string from the store, if there is no setting with the given - * ID or it is not a string the default string is returned. - * - * @param id Identifier for the GMST setting, e.g. "aName" - * @param default Default value if the GMST setting cannot be used. - */ - const std::string &getGameSettingString(const std::string &id, const std::string &default_); - - void processChangedSettings(const Settings::CategorySettingVector& changed); - - void executeInConsole (const std::string& path); - - private: - OEngine::GUI::MyGUIManager *mGuiManager; - HUD *mHud; - MapWindow *mMap; - MainMenu *mMenu; - ToolTips *mToolTips; - StatsWindow *mStatsWindow; - MessageBoxManager *mMessageBoxManager; - Console *mConsole; - JournalWindow* mJournal; - DialogueWindow *mDialogueWindow; - ContainerWindow *mContainerWindow; - DragAndDrop* mDragAndDrop; - InventoryWindow *mInventoryWindow; - ScrollWindow* mScrollWindow; - BookWindow* mBookWindow; - CountDialog* mCountDialog; - TradeWindow* mTradeWindow; - SettingsWindow* mSettingsWindow; - ConfirmationDialog* mConfirmationDialog; - AlchemyWindow* mAlchemyWindow; - SpellWindow* mSpellWindow; - - CharacterCreation* mCharGen; - - /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. - // Various stats about player as needed by window manager - ESM::Class mPlayerClass; - std::string mPlayerName; - std::string mPlayerRaceId; - std::map > mPlayerAttributes; - SkillList mPlayerMajorSkills, mPlayerMinorSkills; - std::map > mPlayerSkillValues; - MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; - - - MyGUI::Gui *mGui; // Gui - std::vector mGuiModes; - - std::vector mGarbageDialogs; - void cleanupGarbage(); - - GuiWindow mShown; // Currently shown windows in inventory mode - - /* Currently ALLOWED windows in inventory mode. This is used at - the start of the game, when windows are enabled one by one - through script commands. You can manipulate this through using - allow() and disableAll(). - */ - GuiWindow mAllowed; - - void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings - - int mShowFPSLevel; - float mFPS; - unsigned int mTriangleCount; - unsigned int mBatchCount; - - void onDialogueWindowBye(); - - /** - * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, - * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result - */ - void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); - }; -} - -#endif diff --git a/apps/openmw/mwgui/window_pinnable_base.cpp b/apps/openmw/mwgui/window_pinnable_base.cpp index ecdf311c6..4ddf49d27 100644 --- a/apps/openmw/mwgui/window_pinnable_base.cpp +++ b/apps/openmw/mwgui/window_pinnable_base.cpp @@ -1,9 +1,10 @@ #include "window_pinnable_base.hpp" -#include "window_manager.hpp" + +#include "../mwbase/windowmanager.hpp" using namespace MWGui; -WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager) +WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager) : WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false) { MyGUI::WindowPtr t = static_cast(mMainWidget); @@ -30,4 +31,3 @@ void WindowPinnableBase::onWindowButtonPressed(MyGUI::Window* sender, const std: eventDone(this); } - diff --git a/apps/openmw/mwgui/window_pinnable_base.hpp b/apps/openmw/mwgui/window_pinnable_base.hpp index 86bc3b85c..250dde1f8 100644 --- a/apps/openmw/mwgui/window_pinnable_base.hpp +++ b/apps/openmw/mwgui/window_pinnable_base.hpp @@ -10,13 +10,13 @@ namespace MWGui class WindowPinnableBase: public WindowBase { public: - WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager); + WindowPinnableBase(const std::string& parLayout, MWBase::WindowManager& parWindowManager); void setVisible(bool b); bool pinned() { return mPinned; } private: void onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName); - + protected: virtual void onPinToggled() = 0; @@ -26,4 +26,3 @@ namespace MWGui } #endif - diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp similarity index 99% rename from apps/openmw/mwgui/window_manager.cpp rename to apps/openmw/mwgui/windowmanagerimp.cpp index eada0c88a..db8401fce 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1,4 +1,4 @@ -#include "window_manager.hpp" +#include "windowmanagerimp.hpp" #include #include diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp new file mode 100644 index 000000000..eaa6a1683 --- /dev/null +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -0,0 +1,254 @@ +#ifndef MWGUI_WINDOWMANAGERIMP_H +#define MWGUI_WINDOWMANAGERIMP_H + +/** + This class owns and controls all the MW specific windows in the + GUI. It can enable/disable Gui mode, and is responsible for sending + and retrieving information from the Gui. + + MyGUI should be initialized separately before creating instances of + this class. +**/ + +#include +#include + +#include + +#include "../mwbase/windowmanager.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; + class UString; +} + +namespace Compiler +{ + class Extensions; +} + +namespace OEngine +{ + namespace GUI + { + class Layout; + class MyGUIManager; + } + + namespace Render + { + class OgreRenderer; + } +} + +namespace MWGui +{ + class WindowBase; + class HUD; + class MapWindow; + class MainMenu; + class StatsWindow; + class InventoryWindow; + class JournalWindow; + class CharacterCreation; + class DragAndDrop; + class ToolTips; + class TextInputDialog; + class InfoBoxDialog; + class MessageBoxManager; + class SettingsWindow; + class AlchemyWindow; + + class WindowManager : public MWBase::WindowManager + { + public: + typedef std::pair Faction; + typedef std::vector FactionList; + + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); + virtual ~WindowManager(); + + /** + * Should be called each frame to update windows/gui elements. + * This could mean updating sizes of gui elements or opening + * new dialogs. + */ + virtual void update(); + + virtual void pushGuiMode(GuiMode mode); + virtual void popGuiMode(); + virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack + + virtual GuiMode getMode() const; + + virtual bool isGuiMode() const; + + virtual void toggleVisible(GuiWindow wnd); + + // Disallow all inventory mode windows + virtual void disallowAll(); + + // Allow one or more windows + virtual void allow(GuiWindow wnd); + + virtual bool isAllowed(GuiWindow wnd) const; + + /// \todo investigate, if we really need to expose every single lousy UI element to the outside world + virtual MWGui::DialogueWindow* getDialogueWindow(); + virtual MWGui::ContainerWindow* getContainerWindow(); + virtual MWGui::InventoryWindow* getInventoryWindow(); + virtual MWGui::BookWindow* getBookWindow(); + virtual MWGui::ScrollWindow* getScrollWindow(); + virtual MWGui::CountDialog* getCountDialog(); + virtual MWGui::ConfirmationDialog* getConfirmationDialog(); + virtual MWGui::TradeWindow* getTradeWindow(); + virtual MWGui::SpellWindow* getSpellWindow(); + virtual MWGui::Console* getConsole(); + + virtual MyGUI::Gui* getGui() const; + + virtual void wmUpdateFps(float fps, unsigned int triangleCount, unsigned int batchCount); + + ///< Set value for the given ID. + virtual void setValue (const std::string& id, const MWMechanics::Stat& value); + virtual void setValue (int parSkill, const MWMechanics::Stat& value); + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + virtual void setValue (const std::string& id, const std::string& value); + virtual void setValue (const std::string& id, int value); + + virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player + virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. + virtual void setReputation (int reputation); ///< set the current reputation value + virtual void setBounty (int bounty); ///< set the current bounty value + virtual void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty + + virtual void changeCell(MWWorld::CellStore* cell); ///< change the active cell + virtual void setPlayerPos(const float x, const float y); ///< set player position in map space + virtual void setPlayerDir(const float x, const float y); ///< set player view direction in map space + + virtual void setFocusObject(const MWWorld::Ptr& focus); + virtual void setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y); + + virtual void setMouseVisible(bool visible); + virtual void getMousePosition(int &x, int &y); + virtual void getMousePosition(float &x, float &y); + virtual void setDragDrop(bool dragDrop); + virtual bool getWorldMouseOver(); + + virtual void toggleFogOfWar(); + virtual void toggleFullHelp(); ///< show extra info in item tooltips (owner, script) + virtual bool getFullHelp() const; + + virtual void setInteriorMapTexture(const int x, const int y); + ///< set the index of the map texture that should be used (for interiors) + + // sets the visibility of the hud health/magicka/stamina bars + virtual void setHMSVisibility(bool visible); + // sets the visibility of the hud minimap + virtual void setMinimapVisibility(bool visible); + virtual void setWeaponVisibility(bool visible); + virtual void setSpellVisibility(bool visible); + + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); + virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); + virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); + virtual void unsetSelectedSpell(); + virtual void unsetSelectedWeapon(); + + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. + + virtual void messageBox (const std::string& message, const std::vector& buttons); + virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + + virtual void onFrame (float frameDuration); + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + virtual std::map > getPlayerSkillValues(); + virtual std::map > getPlayerAttributeValues(); + virtual SkillList getPlayerMinorSkills(); + virtual SkillList getPlayerMajorSkills(); + + /** + * Fetches a GMST string from the store, if there is no setting with the given + * ID or it is not a string the default string is returned. + * + * @param id Identifier for the GMST setting, e.g. "aName" + * @param default Default value if the GMST setting cannot be used. + */ + virtual const std::string &getGameSettingString(const std::string &id, const std::string &default_); + + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + + virtual void executeInConsole (const std::string& path); + + private: + OEngine::GUI::MyGUIManager *mGuiManager; + HUD *mHud; + MapWindow *mMap; + MainMenu *mMenu; + ToolTips *mToolTips; + StatsWindow *mStatsWindow; + MessageBoxManager *mMessageBoxManager; + Console *mConsole; + JournalWindow* mJournal; + DialogueWindow *mDialogueWindow; + ContainerWindow *mContainerWindow; + DragAndDrop* mDragAndDrop; + InventoryWindow *mInventoryWindow; + ScrollWindow* mScrollWindow; + BookWindow* mBookWindow; + CountDialog* mCountDialog; + TradeWindow* mTradeWindow; + SettingsWindow* mSettingsWindow; + ConfirmationDialog* mConfirmationDialog; + AlchemyWindow* mAlchemyWindow; + SpellWindow* mSpellWindow; + + CharacterCreation* mCharGen; + + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. + // Various stats about player as needed by window manager + ESM::Class mPlayerClass; + std::string mPlayerName; + std::string mPlayerRaceId; + std::map > mPlayerAttributes; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map > mPlayerSkillValues; + MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; + + + MyGUI::Gui *mGui; // Gui + std::vector mGuiModes; + + std::vector mGarbageDialogs; + void cleanupGarbage(); + + GuiWindow mShown; // Currently shown windows in inventory mode + + /* Currently ALLOWED windows in inventory mode. This is used at + the start of the game, when windows are enabled one by one + through script commands. You can manipulate this through using + allow() and disableAll(). + */ + GuiWindow mAllowed; + + void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings + + int mShowFPSLevel; + float mFPS; + unsigned int mTriangleCount; + unsigned int mBatchCount; + + void onDialogueWindowBye(); + + /** + * Called when MyGUI tries to retrieve a tag. This usually corresponds to a GMST string, + * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result + */ + void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); + }; +} + +#endif diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d53e67d48..0b409fd50 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -9,7 +9,7 @@ #include -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include #include @@ -83,7 +83,7 @@ namespace MWInput MouseLookEventPtr mouse; OEngine::GUI::EventInjectorPtr guiEvents; MWWorld::Player &player; - MWGui::WindowManager &windows; + MWBase::WindowManager &windows; OMW::Engine& mEngine; bool mDragDrop; @@ -230,7 +230,7 @@ private: public: InputImpl(OEngine::Render::OgreRenderer &_ogre, MWWorld::Player &_player, - MWGui::WindowManager &_windows, + MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine) : ogre(_ogre), @@ -457,7 +457,7 @@ private: /***CONSTRUCTOR***/ MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre, MWWorld::Player &player, - MWGui::WindowManager &windows, + MWBase::WindowManager &windows, bool debug, OMW::Engine& engine) { diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 862c4fd20..5092198da 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -20,7 +20,7 @@ namespace MWWorld class Player; } -namespace MWGui +namespace MWBase { class WindowManager; } @@ -47,7 +47,7 @@ namespace MWInput public: MWInputManager(OEngine::Render::OgreRenderer &_ogre, MWWorld::Player&_player, - MWGui::WindowManager &_windows, + MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine); virtual ~MWInputManager(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5ad67dde4..7fd0b29c3 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -5,12 +5,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" - namespace MWMechanics { void MechanicsManager::buildPlayer() @@ -265,8 +264,8 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getPlayer().getClass().name); mUpdatePlayer = false; - MWGui::WindowManager::SkillList majorSkills (5); - MWGui::WindowManager::SkillList minorSkills (5); + MWBase::WindowManager::SkillList majorSkills (5); + MWBase::WindowManager::SkillList minorSkills (5); for (int i=0; i<5; ++i) { diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index b5fa135e0..704a10cfe 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -8,8 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "renderconst.hpp" #include "renderingmanager.hpp" diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d039418ba..8fb151b5c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -24,12 +24,11 @@ #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" // FIXME +#include "../mwbase/windowmanager.hpp" // FIXME #include "../mwworld/ptr.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" // FIXME - #include "shadows.hpp" #include "localmap.hpp" #include "water.hpp" diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 3d14692d9..d740e5feb 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -8,8 +8,7 @@ #include #include "../mwbase/environment.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 0ba04fb38..075ac5646 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -10,12 +10,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "../mwgui/window_manager.hpp" - #include "locals.hpp" #include "globalscripts.hpp" diff --git a/apps/openmw/mwworld/actionalchemy.cpp b/apps/openmw/mwworld/actionalchemy.cpp index a7ee4fd0e..bba75bc49 100644 --- a/apps/openmw/mwworld/actionalchemy.cpp +++ b/apps/openmw/mwworld/actionalchemy.cpp @@ -1,7 +1,7 @@ #include "actionalchemy.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" namespace MWWorld { diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index c73ef9149..15a9f510d 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -1,9 +1,8 @@ #include "actionopen.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" -#include "../mwclass/container.hpp" -#include "../mwgui/window_manager.hpp" #include "../mwgui/container.hpp" #include "class.hpp" diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index c81d79e03..5c6ab93c1 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -1,7 +1,8 @@ #include "actionread.hpp" #include "../mwbase/environment.hpp" -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" + #include "../mwgui/bookwindow.hpp" #include "../mwgui/scrollwindow.hpp" diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 5207f1a10..90f3c000e 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -3,8 +3,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "class.hpp" #include "containerstore.hpp" diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1c7093e60..e67280ee8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -6,8 +6,7 @@ #include "../mwbase/world.hpp" /// FIXME #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" - -#include "../mwgui/window_manager.hpp" +#include "../mwbase/windowmanager.hpp" #include "player.hpp" #include "localscripts.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3a9547a44..4eda332ef 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -6,12 +6,11 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" -#include "../mwgui/window_manager.hpp" - #include "player.hpp" #include "manualref.hpp" #include "cellfunctors.hpp" From 86d6f190bf3fd958b996afd245282e14b939c061 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 20:45:02 +0200 Subject: [PATCH 407/688] Input system rewrite --- CMakeLists.txt | 12 +- apps/openmw/CMakeLists.txt | 1 + apps/openmw/engine.cpp | 24 +- apps/openmw/mwbase/inputmanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 825 +++---- apps/openmw/mwinput/inputmanagerimp.hpp | 173 +- apps/openmw/mwinput/mouselookevent.cpp | 28 - apps/openmw/mwinput/mouselookevent.hpp | 57 - extern/oics/CMakeLists.txt | 20 + extern/oics/ICSChannel.cpp | 258 +++ extern/oics/ICSChannel.h | 122 ++ extern/oics/ICSChannelListener.h | 46 + extern/oics/ICSControl.cpp | 161 ++ extern/oics/ICSControl.h | 107 + extern/oics/ICSControlListener.h | 46 + extern/oics/ICSInputControlSystem.cpp | 929 ++++++++ extern/oics/ICSInputControlSystem.h | 256 +++ .../oics/ICSInputControlSystem_joystick.cpp | 665 ++++++ .../oics/ICSInputControlSystem_keyboard.cpp | 156 ++ extern/oics/ICSInputControlSystem_mouse.cpp | 397 ++++ extern/oics/ICSPrerequisites.cpp | 27 + extern/oics/ICSPrerequisites.h | 111 + extern/oics/tinystr.cpp | 116 + extern/oics/tinystr.h | 319 +++ extern/oics/tinyxml.cpp | 1888 +++++++++++++++++ extern/oics/tinyxml.h | 1802 ++++++++++++++++ extern/oics/tinyxmlerror.cpp | 53 + extern/oics/tinyxmlparser.cpp | 1638 ++++++++++++++ files/input-default.xml | 100 + libs/mangle/.gitignore | 3 - libs/mangle/Doxyfile | 1510 ------------- libs/mangle/LICENSE.txt | 26 - libs/mangle/README.txt | 129 -- .../input/clients/ogre_input_capture.hpp | 29 - libs/mangle/input/driver.hpp | 69 - libs/mangle/input/event.hpp | 46 - libs/mangle/input/filters/eventlist.hpp | 47 - libs/mangle/input/servers/ois_driver.cpp | 154 -- libs/mangle/input/servers/ois_driver.hpp | 50 - libs/mangle/input/servers/sdl_driver.cpp | 54 - libs/mangle/input/servers/sdl_driver.hpp | 27 - libs/mangle/input/tests/.gitignore | 2 - libs/mangle/input/tests/Makefile | 15 - libs/mangle/input/tests/common.cpp | 35 - libs/mangle/input/tests/evtlist_test.cpp | 45 - libs/mangle/input/tests/ois_driver_test.cpp | 51 - .../input/tests/output/evtlist_test.out | 12 - .../input/tests/output/ois_driver_test.out | 5 - .../input/tests/output/sdl_driver_test.out | 5 - libs/mangle/input/tests/plugins.cfg | 12 - libs/mangle/input/tests/sdl_driver_test.cpp | 16 - libs/mangle/input/tests/test.sh | 18 - libs/mangle/rend2d/driver.hpp | 63 - libs/mangle/rend2d/servers/sdl_driver.cpp | 259 --- libs/mangle/rend2d/servers/sdl_driver.hpp | 125 -- libs/mangle/rend2d/servers/sdl_gl_driver.cpp | 311 --- libs/mangle/rend2d/servers/sdl_gl_driver.hpp | 132 -- libs/mangle/rend2d/sprite.hpp | 57 - libs/mangle/rend2d/tests/.gitignore | 1 - libs/mangle/rend2d/tests/Makefile | 15 - .../rend2d/tests/output/sdl_move_test.out | 0 libs/mangle/rend2d/tests/output/sdl_test.out | 11 - .../rend2d/tests/output/sdlgl_move_test.out | 0 libs/mangle/rend2d/tests/sdl_move_test.cpp | 30 - libs/mangle/rend2d/tests/sdl_test.cpp | 65 - libs/mangle/rend2d/tests/sdlgl_move_test.cpp | 31 - libs/mangle/rend2d/tests/test.sh | 18 - libs/mangle/rend2d/tests/tile1-blue.png | Bin 273 -> 0 bytes libs/mangle/rend2d/tests/tile1-yellow.png | Bin 257 -> 0 bytes libs/mangle/testall.sh | 16 - libs/mangle/tests/.gitignore | 1 - libs/mangle/tests/Makefile | 14 - .../tests/ogrevfs_audiere_openal_test.cpp | 49 - .../output/ogrevfs_audiere_openal_test.out | 1 - libs/mangle/tests/sound.zip | Bin 18159 -> 0 bytes libs/mangle/tests/test.sh | 18 - libs/mangle/tools/shared_ptr.hpp | 3 - libs/openengine/gui/events.cpp | 77 - libs/openengine/gui/events.hpp | 32 - libs/openengine/ogre/mouselook.cpp | 57 - libs/openengine/ogre/mouselook.hpp | 56 - 81 files changed, 9806 insertions(+), 4335 deletions(-) delete mode 100644 apps/openmw/mwinput/mouselookevent.cpp delete mode 100644 apps/openmw/mwinput/mouselookevent.hpp create mode 100644 extern/oics/CMakeLists.txt create mode 100644 extern/oics/ICSChannel.cpp create mode 100644 extern/oics/ICSChannel.h create mode 100644 extern/oics/ICSChannelListener.h create mode 100644 extern/oics/ICSControl.cpp create mode 100644 extern/oics/ICSControl.h create mode 100644 extern/oics/ICSControlListener.h create mode 100644 extern/oics/ICSInputControlSystem.cpp create mode 100644 extern/oics/ICSInputControlSystem.h create mode 100644 extern/oics/ICSInputControlSystem_joystick.cpp create mode 100644 extern/oics/ICSInputControlSystem_keyboard.cpp create mode 100644 extern/oics/ICSInputControlSystem_mouse.cpp create mode 100644 extern/oics/ICSPrerequisites.cpp create mode 100644 extern/oics/ICSPrerequisites.h create mode 100644 extern/oics/tinystr.cpp create mode 100644 extern/oics/tinystr.h create mode 100644 extern/oics/tinyxml.cpp create mode 100644 extern/oics/tinyxml.h create mode 100644 extern/oics/tinyxmlerror.cpp create mode 100644 extern/oics/tinyxmlparser.cpp create mode 100644 files/input-default.xml delete mode 100644 libs/mangle/.gitignore delete mode 100644 libs/mangle/Doxyfile delete mode 100644 libs/mangle/LICENSE.txt delete mode 100644 libs/mangle/README.txt delete mode 100644 libs/mangle/input/clients/ogre_input_capture.hpp delete mode 100644 libs/mangle/input/driver.hpp delete mode 100644 libs/mangle/input/event.hpp delete mode 100644 libs/mangle/input/filters/eventlist.hpp delete mode 100644 libs/mangle/input/servers/ois_driver.cpp delete mode 100644 libs/mangle/input/servers/ois_driver.hpp delete mode 100644 libs/mangle/input/servers/sdl_driver.cpp delete mode 100644 libs/mangle/input/servers/sdl_driver.hpp delete mode 100644 libs/mangle/input/tests/.gitignore delete mode 100644 libs/mangle/input/tests/Makefile delete mode 100644 libs/mangle/input/tests/common.cpp delete mode 100644 libs/mangle/input/tests/evtlist_test.cpp delete mode 100644 libs/mangle/input/tests/ois_driver_test.cpp delete mode 100644 libs/mangle/input/tests/output/evtlist_test.out delete mode 100644 libs/mangle/input/tests/output/ois_driver_test.out delete mode 100644 libs/mangle/input/tests/output/sdl_driver_test.out delete mode 100644 libs/mangle/input/tests/plugins.cfg delete mode 100644 libs/mangle/input/tests/sdl_driver_test.cpp delete mode 100755 libs/mangle/input/tests/test.sh delete mode 100644 libs/mangle/rend2d/driver.hpp delete mode 100644 libs/mangle/rend2d/servers/sdl_driver.cpp delete mode 100644 libs/mangle/rend2d/servers/sdl_driver.hpp delete mode 100644 libs/mangle/rend2d/servers/sdl_gl_driver.cpp delete mode 100644 libs/mangle/rend2d/servers/sdl_gl_driver.hpp delete mode 100644 libs/mangle/rend2d/sprite.hpp delete mode 100644 libs/mangle/rend2d/tests/.gitignore delete mode 100644 libs/mangle/rend2d/tests/Makefile delete mode 100644 libs/mangle/rend2d/tests/output/sdl_move_test.out delete mode 100644 libs/mangle/rend2d/tests/output/sdl_test.out delete mode 100644 libs/mangle/rend2d/tests/output/sdlgl_move_test.out delete mode 100644 libs/mangle/rend2d/tests/sdl_move_test.cpp delete mode 100644 libs/mangle/rend2d/tests/sdl_test.cpp delete mode 100644 libs/mangle/rend2d/tests/sdlgl_move_test.cpp delete mode 100755 libs/mangle/rend2d/tests/test.sh delete mode 100644 libs/mangle/rend2d/tests/tile1-blue.png delete mode 100644 libs/mangle/rend2d/tests/tile1-yellow.png delete mode 100755 libs/mangle/testall.sh delete mode 100644 libs/mangle/tests/.gitignore delete mode 100644 libs/mangle/tests/Makefile delete mode 100644 libs/mangle/tests/ogrevfs_audiere_openal_test.cpp delete mode 100644 libs/mangle/tests/output/ogrevfs_audiere_openal_test.out delete mode 100644 libs/mangle/tests/sound.zip delete mode 100755 libs/mangle/tests/test.sh delete mode 100644 libs/mangle/tools/shared_ptr.hpp delete mode 100644 libs/openengine/gui/events.cpp delete mode 100644 libs/openengine/gui/events.hpp delete mode 100644 libs/openengine/ogre/mouselook.cpp delete mode 100644 libs/openengine/ogre/mouselook.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb98..79e33f181 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,19 +96,13 @@ ENDIF() set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) -set(MANGLE_INPUT ${LIBDIR}/mangle/input/servers/ois_driver.cpp) -set(MANGLE_ALL ${MANGLE_INPUT}) -source_group(libs\\mangle FILES ${MANGLE_ALL}) - set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp - ${LIBDIR}/openengine/ogre/mouselook.cpp ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/atlas.cpp ) set(OENGINE_GUI - ${LIBDIR}/openengine/gui/events.cpp ${LIBDIR}/openengine/gui/manager.cpp ) @@ -135,7 +129,7 @@ set(OENGINE_BULLET set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_BULLET}) source_group(libs\\openengine FILES ${OENGINE_ALL}) -set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL}) +set(OPENMW_LIBS ${OENGINE_ALL}) set(OPENMW_LIBS_HEADER) # Sound setup @@ -291,6 +285,9 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") +configure_file(${OpenMW_SOURCE_DIR}/files/input-default.xml + "${OpenMW_BINARY_DIR}/input-default.xml") + configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg "${OpenMW_BINARY_DIR}/transparency-overrides.cfg") @@ -443,6 +440,7 @@ endif(WIN32) # Extern add_subdirectory (extern/shiny) +add_subdirectory (extern/oics) # Components add_subdirectory (components) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 02fe0b72c..72c5117d2 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -104,6 +104,7 @@ target_link_libraries(openmw ${MYGUI_PLATFORM_LIBRARIES} "shiny" "shiny.OgrePlatform" + "oics" components ) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 75835120f..128d97553 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -67,7 +67,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.setFrameDuration (evt.timeSinceLastFrame); // update input - MWBase::Environment::get().getInputManager()->update(); + MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame); // sound if (mUseSound) @@ -270,6 +270,24 @@ void OMW::Engine::go() else if (boost::filesystem::exists(globaldefault)) settings.loadUser(globaldefault); + // Get the path for the keybinder xml file + std::string keybinderDefault; + + // load user settings if they exist, otherwise just load the default settings as user settings + const std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + const std::string keybinderDefaultLocal = (mCfgMgr.getLocalPath() / "input-default.xml").string(); + const std::string keybinderDefaultGlobal = (mCfgMgr.getGlobalPath() / "input-default.xml").string(); + + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); + + if (boost::filesystem::exists(keybinderDefaultLocal)) + keybinderDefault = keybinderDefaultLocal; + else if (boost::filesystem::exists(keybinderDefaultGlobal)) + keybinderDefault = keybinderDefaultGlobal; + else + throw std::runtime_error ("No default input settings found! Make sure the file \"input-default.xml\" was properly installed."); + + mFpsLevel = settings.getInt("fps", "HUD"); // load nif overrides @@ -366,9 +384,9 @@ void OMW::Engine::go() // Sets up the input system - mEnvironment.setInputManager (new MWInput::MWInputManager (*mOgre, + mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderDefault, keybinderUser, keybinderUserExists)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index d865bfb0e..5d73025a7 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -22,7 +22,7 @@ namespace MWBase virtual ~InputManager() {} - virtual void update() = 0; + virtual void update(float dt) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8def5c74d..d1c400cea 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,357 +1,200 @@ #include "inputmanagerimp.hpp" #include +#include -#include -#include +#include +#include -#include +#include + +#include +#include + +#include #include -#include "../mwbase/windowmanager.hpp" - -#include -#include - -#include - -#include "mouselookevent.hpp" - #include "../engine.hpp" #include "../mwworld/player.hpp" #include "../mwbase/world.hpp" - -#include -#include -#include -#include +#include "../mwbase/windowmanager.hpp" namespace MWInput { - enum Actions + InputManager::InputManager(OEngine::Render::OgreRenderer &ogre, + MWWorld::Player &player, + MWBase::WindowManager &windows, + bool debug, + OMW::Engine& engine, + const std::string& defaultFile, + const std::string& userFile, bool userFileExists) + : mOgre(ogre) + , mPlayer(player) + , mWindows(windows) + , mEngine(engine) + , mMouseLookEnabled(true) + , mMouseX(ogre.getWindow()->getWidth ()/2.f) + , mMouseY(ogre.getWindow()->getHeight ()/2.f) + , mUserFile(userFile) + , mDragDrop(false) { - A_Quit, // Exit the program + Ogre::RenderWindow* window = ogre.getWindow (); + size_t windowHnd; - A_Screenshot, // Take a screenshot + window->getCustomAttribute("WINDOW", &windowHnd); - A_Inventory, // Toggle inventory screen + std::ostringstream windowHndStr; + OIS::ParamList pl; - A_Console, // Toggle console screen + windowHndStr << windowHnd; + pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); - A_MoveLeft, // Move player left / right - A_MoveRight, - A_MoveForward, // Forward / Backward - A_MoveBackward, - - A_Activate, - - A_Use, //Use weapon, spell, etc. - A_Jump, - A_AutoMove, //Toggle Auto-move forward - A_Rest, //Rest - A_Journal, //Journal - A_Weapon, //Draw/Sheath weapon - A_Spell, //Ready/Unready Casting - A_AlwaysRun, //Toggle Always Run - A_CycleSpellLeft, //cycling through spells - A_CycleSpellRight, - A_CycleWeaponLeft,//Cycling through weapons - A_CycleWeaponRight, - A_ToggleSneak, //Toggles Sneak, add Push-Sneak later - A_ToggleWalk, //Toggle Walking/Running - A_Crouch, - - A_QuickSave, - A_QuickLoad, - A_QuickMenu, - A_GameMenu, - A_ToggleWeapon, - A_ToggleSpell, - - A_LAST // Marker for the last item - }; - - // Class that handles all input and key bindings for OpenMW - class InputImpl - { - OEngine::Input::DispatcherPtr disp; - OEngine::Render::OgreRenderer &ogre; - Mangle::Input::OISDriver input; - OEngine::Input::Poller poller; - MouseLookEventPtr mouse; - OEngine::GUI::EventInjectorPtr guiEvents; - MWWorld::Player &player; - MWBase::WindowManager &windows; - OMW::Engine& mEngine; - - bool mDragDrop; - - std::map mControlSwitch; - - /* InputImpl Methods */ -public: - void adjustMouseRegion(int width, int height) - { - input.adjustMouseClippingSize(width, height); - } -private: - void toggleSpell() - { - if (windows.isGuiMode()) return; - - MWMechanics::DrawState_ state = player.getDrawState(); - if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) + // Set non-exclusive mouse and keyboard input if the user requested + // it. + if (debug) { - player.setDrawState(MWMechanics::DrawState_Spell); - std::cout << "Player has now readied his hands for spellcasting!\n"; + #if defined OIS_WIN32_PLATFORM + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_FOREGROUND" ))); + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_NONEXCLUSIVE"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_FOREGROUND"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_NONEXCLUSIVE"))); + #elif defined OIS_LINUX_PLATFORM + pl.insert(std::make_pair(std::string("x11_mouse_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_mouse_hide"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_keyboard_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("XAutoRepeatOn"), + std::string("true"))); + #endif } + + #ifdef __APPLE_CC__ + // Give the application window focus to receive input events + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + #endif + + mInputManager = OIS::InputManager::createInputSystem( pl ); + + // Create all devices + mKeyboard = static_cast(mInputManager->createInputObject + ( OIS::OISKeyboard, true )); + mMouse = static_cast(mInputManager->createInputObject + ( OIS::OISMouse, true )); + + mKeyboard->setEventCallback (this); + mMouse->setEventCallback (this); + + adjustMouseRegion (window->getWidth(), window->getHeight()); + + MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); + + std::string configFile; + if (userFileExists) + configFile = userFile; else + configFile = defaultFile; + + std::cout << "Loading input configuration: " << configFile << std::endl; + + mInputCtrl = new ICS::InputControlSystem(configFile, true, NULL, NULL, A_LAST); + + for (int i = 0; i < A_LAST; ++i) { - player.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n"; + mInputCtrl->getChannel (i)->addListener (this); } + + mControlSwitch["playercontrols"] = true; + mControlSwitch["playerfighting"] = true; + mControlSwitch["playerjumping"] = true; + mControlSwitch["playerlooking"] = true; + mControlSwitch["playermagic"] = true; + mControlSwitch["playerviewswitch"] = true; + mControlSwitch["vanitymode"] = true; + + changeInputMode(false); } - void toggleWeapon() + InputManager::~InputManager() { - if (windows.isGuiMode()) return; + mInputCtrl->save (mUserFile); - MWMechanics::DrawState_ state = player.getDrawState(); - if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - { - player.setDrawState(MWMechanics::DrawState_Weapon); - std::cout << "Player is now drawing his weapon.\n"; - } - else - { - player.setDrawState(MWMechanics::DrawState_Nothing); - std::cout << "Player does not have any kind of attack ready now.\n"; - } + delete mInputCtrl; + + mInputManager->destroyInputObject(mKeyboard); + mInputManager->destroyInputObject(mMouse); + OIS::InputManager::destroyInputSystem(mInputManager); } - void screenshot() + void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) { - mEngine.screenshot(); - - std::vector empty; - windows.messageBox ("Screenshot saved", empty); - } - - /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ - void toggleInventory() - { - using namespace MWGui; - if (mDragDrop) return; - bool gameMode = !windows.isGuiMode(); + int action = channel->getNumber(); + if (currentValue == 1) + { + // trigger action activated - // Toggle between game mode and inventory mode - if(gameMode) - windows.pushGuiMode(GM_Inventory); - else if(windows.getMode() == GM_Inventory) - windows.popGuiMode(); - - // .. but don't touch any other mode. + switch (action) + { + case A_GameMenu: + toggleMainMenu (); + break; + case A_Quit: + exitNow(); + break; + case A_Screenshot: + screenshot(); + break; + case A_Inventory: + toggleInventory (); + break; + case A_Console: + toggleConsole (); + break; + case A_Activate: + activate(); + break; + case A_Journal: + toggleJournal (); + break; + case A_AutoMove: + toggleAutoMove (); + break; + case A_ToggleSneak: + /// \todo implement + break; + case A_ToggleWalk: + toggleWalking (); + break; + case A_ToggleWeapon: + toggleWeapon (); + break; + case A_ToggleSpell: + toggleSpell (); + break; + } + } } - // Toggle console - void toggleConsole() - { - using namespace MWGui; - - if (mDragDrop) - return; - - bool gameMode = !windows.isGuiMode(); - - // Switch to console mode no matter what mode we are currently - // in, except of course if we are already in console mode - if (!gameMode) - { - if (windows.getMode() == GM_Console) - windows.popGuiMode(); - else - windows.pushGuiMode(GM_Console); - } - else - windows.pushGuiMode(GM_Console); - } - - void toggleJournal() - { - using namespace MWGui; - - // Toggle between game mode and journal mode - bool gameMode = !windows.isGuiMode(); - - if(gameMode) - windows.pushGuiMode(GM_Journal); - else if(windows.getMode() == GM_Journal) - windows.popGuiMode(); - // .. but don't touch any other mode. - } - - void activate() - { - mEngine.activate(); - } - - void toggleAutoMove() - { - if (windows.isGuiMode()) return; - player.setAutoMove (!player.getAutoMove()); - } - - void toggleWalking() - { - if (windows.isGuiMode()) return; - player.toggleRunning(); - } - - void toggleMainMenu() - { - if (windows.isGuiMode () && (windows.getMode () == MWGui::GM_MainMenu || windows.getMode () == MWGui::GM_Settings)) - windows.popGuiMode(); - else - windows.pushGuiMode (MWGui::GM_MainMenu); - } - - // Exit program now button (which is disabled in GUI mode) - void exitNow() - { - if(!windows.isGuiMode()) - Ogre::Root::getSingleton().queueEndRendering (); - } - - public: - InputImpl(OEngine::Render::OgreRenderer &_ogre, - MWWorld::Player &_player, - MWBase::WindowManager &_windows, - bool debug, - OMW::Engine& engine) - : ogre(_ogre), - input(ogre.getWindow(), !debug), - poller(input), - player(_player), - windows(_windows), - mEngine (engine), - mDragDrop(false) - { - using namespace OEngine::Input; - using namespace OEngine::Render; - using namespace OEngine::GUI; - using namespace Mangle::Input; - using namespace OIS; - - disp = DispatcherPtr(new Dispatcher(A_LAST)); - - // Bind MW-specific functions - disp->funcs.bind(A_Quit, boost::bind(&InputImpl::exitNow, this), - "Quit program"); - disp->funcs.bind(A_Screenshot, boost::bind(&InputImpl::screenshot, this), - "Screenshot"); - disp->funcs.bind(A_Inventory, boost::bind(&InputImpl::toggleInventory, this), - "Toggle inventory screen"); - disp->funcs.bind(A_Console, boost::bind(&InputImpl::toggleConsole, this), - "Toggle console"); - disp->funcs.bind(A_Journal, boost::bind(&InputImpl::toggleJournal, this), - "Toggle journal"); - disp->funcs.bind(A_Activate, boost::bind(&InputImpl::activate, this), - "Activate"); - disp->funcs.bind(A_AutoMove, boost::bind(&InputImpl::toggleAutoMove, this), - "Auto Move"); - disp->funcs.bind(A_ToggleWalk, boost::bind(&InputImpl::toggleWalking, this), - "Toggle Walk/Run"); - disp->funcs.bind(A_ToggleWeapon,boost::bind(&InputImpl::toggleWeapon,this), - "Draw Weapon"); - disp->funcs.bind(A_ToggleSpell,boost::bind(&InputImpl::toggleSpell,this), - "Ready hands"); - disp->funcs.bind(A_GameMenu, boost::bind(&InputImpl::toggleMainMenu, this), - "Toggle main menu"); - - mouse = MouseLookEventPtr(new MouseLookEvent()); - - // This event handler pumps events into MyGUI - guiEvents = EventInjectorPtr(new EventInjector(windows.getGui())); - - // Hook 'mouse' and 'disp' up as event handlers into 'input' - // (the OIS driver and event source.) We do this through an - // EventList which dispatches the event to multiple handlers for - // us. - { - EventList *lst = new EventList; - input.setEvent(EventPtr(lst)); - lst->add(mouse,Event::EV_MouseMove); - lst->add(disp,Event::EV_KeyDown); - lst->add(guiEvents,Event::EV_ALL); - } - - mControlSwitch["playercontrols"] = true; - mControlSwitch["playerfighting"] = true; - mControlSwitch["playerjumping"] = true; - mControlSwitch["playerlooking"] = true; - mControlSwitch["playermagic"] = true; - mControlSwitch["playerviewswitch"] = true; - mControlSwitch["vanitymode"] = true; - - changeInputMode(false); - - /********************************** - Key binding section - - The rest of this function has hard coded key bindings, and is - intended to be replaced by user defined bindings later. - **********************************/ - - // Key bindings for keypress events - // NOTE: These keys do not require constant polling - use in conjuction with variables in loops. - - disp->bind(A_Quit, KC_Q); - disp->bind(A_GameMenu, KC_ESCAPE); - disp->bind(A_Screenshot, KC_SYSRQ); - disp->bind(A_Inventory, KC_I); - disp->bind(A_Console, KC_F1); - disp->bind(A_Journal, KC_J); - disp->bind(A_Activate, KC_SPACE); - disp->bind(A_AutoMove, KC_Z); - disp->bind(A_ToggleSneak, KC_X); - disp->bind(A_ToggleWalk, KC_C); - disp->bind(A_ToggleWeapon,KC_F); - disp->bind(A_ToggleSpell,KC_R); - - // Key bindings for polled keys - // NOTE: These keys are constantly being polled. Only add keys that must be checked each frame. - - // Arrow keys - poller.bind(A_MoveLeft, KC_LEFT); - poller.bind(A_MoveRight, KC_RIGHT); - poller.bind(A_MoveForward, KC_UP); - poller.bind(A_MoveBackward, KC_DOWN); - - // WASD keys - poller.bind(A_MoveLeft, KC_A); - poller.bind(A_MoveRight, KC_D); - poller.bind(A_MoveForward, KC_W); - poller.bind(A_MoveBackward, KC_S); - - poller.bind(A_Jump, KC_E); - poller.bind(A_Crouch, KC_LCONTROL); - } - - void setDragDrop(bool dragDrop) - { - mDragDrop = dragDrop; - } - - //NOTE: Used to check for movement keys - void update () + void InputManager::update(float dt) { // Tell OIS to handle all input events - input.capture(); + mKeyboard->capture(); + mMouse->capture(); + + // update values of channels (as a result of pressed keys) + mInputCtrl->update(dt); // Update windows/gui as a result of input events // For instance this could mean opening a new window/dialog, @@ -359,150 +202,314 @@ private: // ensure that window/gui changes appear quickly while // avoiding that window/gui changes does not happen in // event callbacks (which may crash) - windows.update(); + mWindows.update(); // Disable movement in Gui mode + if (mWindows.isGuiMode()) return; - if (windows.isGuiMode()) return; // Configure player movement according to keyboard input. Actual movement will // be done in the physics system. - if (mControlSwitch["playercontrols"]) { - if (poller.isDown(A_MoveLeft)) + if (mControlSwitch["playercontrols"]) + { + if (actionIsActive(A_MoveLeft)) { - player.setAutoMove (false); - player.setLeftRight (1); + mPlayer.setAutoMove (false); + mPlayer.setLeftRight (1); } - else if (poller.isDown(A_MoveRight)) + else if (actionIsActive(A_MoveRight)) { - player.setAutoMove (false); - player.setLeftRight (-1); + mPlayer.setAutoMove (false); + mPlayer.setLeftRight (-1); } else - player.setLeftRight (0); + mPlayer.setLeftRight (0); - if (poller.isDown(A_MoveForward)) + if (actionIsActive(A_MoveForward)) { - player.setAutoMove (false); - player.setForwardBackward (1); + mPlayer.setAutoMove (false); + mPlayer.setForwardBackward (1); } - else if (poller.isDown(A_MoveBackward)) + else if (actionIsActive(A_MoveBackward)) { - player.setAutoMove (false); - player.setForwardBackward (-1); + mPlayer.setAutoMove (false); + mPlayer.setForwardBackward (-1); } else - player.setForwardBackward (0); + mPlayer.setForwardBackward (0); - if (poller.isDown(A_Jump) && mControlSwitch["playerjumping"]) - player.setUpDown (1); - else if (poller.isDown(A_Crouch)) - player.setUpDown (-1); + if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) + mPlayer.setUpDown (1); + else if (actionIsActive(A_Crouch)) + mPlayer.setUpDown (-1); else - player.setUpDown (0); + mPlayer.setUpDown (0); } + } - // Switch between gui modes. Besides controlling the Gui windows - // this also makes sure input is directed to the right place - void changeInputMode(bool guiMode) + void InputManager::setDragDrop(bool dragDrop) { - // Are we in GUI mode now? - if(guiMode) - { - // Disable mouse look - mouse->disable(); + mDragDrop = dragDrop; + } - // Enable GUI events - guiEvents->enabled = true; + void InputManager::changeInputMode(bool guiMode) + { + // Are we in GUI mode now? + if(guiMode) + { + // Disable mouse look + mMouseLookEnabled = false; + + // Enable GUI events + mGuiCursorEnabled = true; } - else + else { // Start mouse-looking again if allowed. if (mControlSwitch["playerlooking"]) { - mouse->enable(); + mMouseLookEnabled = true; } - // Disable GUI events - guiEvents->enabled = false; + // Disable GUI events + mGuiCursorEnabled = false; } } - void toggleControlSwitch(std::string sw, bool value) + void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) + { + bool changeRes = false; + for (Settings::CategorySettingVector::const_iterator it = changed.begin(); + it != changed.end(); ++it) + { + if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y")) + changeRes = true; + } + + if (changeRes) + adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); + } + + void InputManager::toggleControlSwitch (const std::string& sw, bool value) { if (mControlSwitch[sw] == value) { return; } /// \note 7 switches at all, if-else is relevant if (sw == "playercontrols" && !value) { - player.setLeftRight(0); - player.setForwardBackward(0); - player.setAutoMove(false); - player.setUpDown(0); + mPlayer.setLeftRight(0); + mPlayer.setForwardBackward(0); + mPlayer.setAutoMove(false); + mPlayer.setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time - player.setUpDown(0); + mPlayer.setUpDown(0); } else if (sw == "playerlooking") { if (value) { - mouse->enable(); + mMouseLookEnabled = true; } else { - mouse->disable(); + mMouseLookEnabled = false; } } mControlSwitch[sw] = value; } - }; - - /***CONSTRUCTOR***/ - MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre, - MWWorld::Player &player, - MWBase::WindowManager &windows, - bool debug, - OMW::Engine& engine) - { - impl = new InputImpl(ogre,player,windows,debug, engine); - } - - /***DESTRUCTOR***/ - MWInputManager::~MWInputManager() - { - delete impl; - } - - void MWInputManager::update() - { - impl->update(); - } - - void MWInputManager::setDragDrop(bool dragDrop) - { - impl->setDragDrop(dragDrop); - } - - void MWInputManager::changeInputMode(bool guiMode) - { - impl->changeInputMode(guiMode); - } - - void MWInputManager::processChangedSettings(const Settings::CategorySettingVector& changed) - { - bool changeRes = false; - for (Settings::CategorySettingVector::const_iterator it = changed.begin(); - it != changed.end(); ++it) - { - if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) - changeRes = true; - } - - if (changeRes) - impl->adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); - } - - void MWInputManager::toggleControlSwitch (const std::string& sw, bool value) + void InputManager::adjustMouseRegion(int width, int height) { - impl->toggleControlSwitch(sw, value); + const OIS::MouseState &ms = mMouse->getMouseState(); + ms.width = width; + ms.height = height; } + + bool InputManager::keyPressed( const OIS::KeyEvent &arg ) + { + mInputCtrl->keyPressed (arg); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + + return true; + } + + bool InputManager::keyReleased( const OIS::KeyEvent &arg ) + { + mInputCtrl->keyReleased (arg); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); + + return true; + } + + bool InputManager::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + mInputCtrl->mousePressed (arg, id); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + + return true; + } + + bool InputManager::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + mInputCtrl->mouseReleased (arg, id); + + if (mGuiCursorEnabled) + MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + + return true; + } + + bool InputManager::mouseMoved( const OIS::MouseEvent &arg ) + { + mInputCtrl->mouseMoved (arg); + + if (mGuiCursorEnabled) + { + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // We keep track of our own mouse position, so that moving the mouse while in + // game mode does not move the position of the GUI cursor + mMouseX += arg.state.X.rel; + mMouseY += arg.state.Y.rel; + mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); + mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); + + MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, arg.state.Z.abs); + } + + if (mMouseLookEnabled) + { + float x = arg.state.X.rel * 0.2; + float y = arg.state.Y.rel * 0.2; + + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + } + + return true; + } + + void InputManager::toggleMainMenu() + { + if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) + mWindows.popGuiMode(); + else + mWindows.pushGuiMode (MWGui::GM_MainMenu); + } + + void InputManager::toggleSpell() + { + if (mWindows.isGuiMode()) return; + + MWMechanics::DrawState_ state = mPlayer.getDrawState(); + if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) + { + mPlayer.setDrawState(MWMechanics::DrawState_Spell); + std::cout << "Player has now readied his hands for spellcasting!\n" << std::endl; + } + else + { + mPlayer.setDrawState(MWMechanics::DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; + } + } + + void InputManager::toggleWeapon() + { + if (mWindows.isGuiMode()) return; + + MWMechanics::DrawState_ state = mPlayer.getDrawState(); + if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) + { + mPlayer.setDrawState(MWMechanics::DrawState_Weapon); + std::cout << "Player is now drawing his weapon.\n" << std::endl; + } + else + { + mPlayer.setDrawState(MWMechanics::DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n" << std::endl; + } + } + + void InputManager::screenshot() + { + mEngine.screenshot(); + + std::vector empty; + mWindows.messageBox ("Screenshot saved", empty); + } + + void InputManager::toggleInventory() + { + bool gameMode = !mWindows.isGuiMode(); + + // Toggle between game mode and inventory mode + if(gameMode) + mWindows.pushGuiMode(MWGui::GM_Inventory); + else if(mWindows.getMode() == MWGui::GM_Inventory) + mWindows.popGuiMode(); + + // .. but don't touch any other mode. + } + + void InputManager::toggleConsole() + { + bool gameMode = !mWindows.isGuiMode(); + + // Switch to console mode no matter what mode we are currently + // in, except of course if we are already in console mode + if (!gameMode) + { + if (mWindows.getMode() == MWGui::GM_Console) + mWindows.popGuiMode(); + else + mWindows.pushGuiMode(MWGui::GM_Console); + } + else + mWindows.pushGuiMode(MWGui::GM_Console); + } + + void InputManager::toggleJournal() + { + // Toggle between game mode and journal mode + bool gameMode = !mWindows.isGuiMode(); + + if(gameMode) + mWindows.pushGuiMode(MWGui::GM_Journal); + else if(mWindows.getMode() == MWGui::GM_Journal) + mWindows.popGuiMode(); + // .. but don't touch any other mode. + } + + void InputManager::activate() + { + mEngine.activate(); + } + + void InputManager::toggleAutoMove() + { + if (mWindows.isGuiMode()) return; + mPlayer.setAutoMove (!mPlayer.getAutoMove()); + } + + void InputManager::toggleWalking() + { + if (mWindows.isGuiMode()) return; + mPlayer.toggleRunning(); + } + + // Exit program now button (which is disabled in GUI mode) + void InputManager::exitNow() + { + if(!mWindows.isGuiMode()) + Ogre::Root::getSingleton().queueEndRendering (); + } + + bool InputManager::actionIsActive (int id) + { + return mInputCtrl->getChannel (id)->getValue () == 1; + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 5092198da..ba42327ee 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -9,20 +9,20 @@ namespace OEngine { - namespace Render - { - class OgreRenderer; - } + namespace Render + { + class OgreRenderer; + } } namespace MWWorld { - class Player; + class Player; } namespace MWBase { - class WindowManager; + class WindowManager; } namespace OMW @@ -30,37 +30,154 @@ namespace OMW class Engine; } +namespace ICS +{ + class InputControlSystem; +} + +namespace OIS +{ + class Keyboard; + class Mouse; + class InputManager; +} + +#include +#include + +#include + namespace MWInput { - // Forward declaration of the real implementation. - class InputImpl; - /* Class that handles all input and key bindings for OpenMW. + /** + * @brief Class that handles all input and key bindings for OpenMW. + */ + class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener + { + public: + InputManager(OEngine::Render::OgreRenderer &_ogre, + MWWorld::Player&_player, + MWBase::WindowManager &_windows, + bool debug, + OMW::Engine& engine, + const std::string& defaultFile, + const std::string& userFile, bool userFileExists); - This class is just an interface. All the messy details are in - inputmanager.cpp. - */ - struct MWInputManager : public MWBase::InputManager - { - InputImpl *impl; + virtual ~InputManager(); - public: - MWInputManager(OEngine::Render::OgreRenderer &_ogre, - MWWorld::Player&_player, - MWBase::WindowManager &_windows, - bool debug, - OMW::Engine& engine); - virtual ~MWInputManager(); + virtual void update(float dt); - virtual void update(); + virtual void changeInputMode(bool guiMode); - virtual void changeInputMode(bool guiMode); + virtual void processChangedSettings(const Settings::CategorySettingVector& changed); - virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void setDragDrop(bool dragDrop); - virtual void setDragDrop(bool dragDrop); + virtual void toggleControlSwitch (const std::string& sw, bool value); - virtual void toggleControlSwitch (const std::string& sw, bool value); - }; + + public: + virtual bool keyPressed( const OIS::KeyEvent &arg ); + virtual bool keyReleased( const OIS::KeyEvent &arg ); + + virtual bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); + virtual bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ); + virtual bool mouseMoved( const OIS::MouseEvent &arg ); + + virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); + + private: + OEngine::Render::OgreRenderer &mOgre; + MWWorld::Player &mPlayer; + MWBase::WindowManager &mWindows; + OMW::Engine& mEngine; + + ICS::InputControlSystem* mInputCtrl; + + OIS::Keyboard* mKeyboard; + OIS::Mouse* mMouse; + OIS::InputManager* mInputManager; + + std::string mUserFile; + + bool mDragDrop; + + bool mMouseLookEnabled; + bool mGuiCursorEnabled; + + int mMouseX; + int mMouseY; + + std::map mControlSwitch; + + + private: + void adjustMouseRegion(int width, int height); + + private: + void toggleMainMenu(); + void toggleSpell(); + void toggleWeapon(); + void toggleInventory(); + void toggleConsole(); + void screenshot(); + void toggleJournal(); + void activate(); + void toggleWalking(); + void toggleAutoMove(); + void exitNow(); + + bool actionIsActive (int id); + + private: + enum Actions + { + // please add new actions at the bottom, in order to preserve the channel IDs in the key configuration files + + A_GameMenu, + + A_Quit, // Exit the program + + A_Screenshot, // Take a screenshot + + A_Inventory, // Toggle inventory screen + + A_Console, // Toggle console screen + + A_MoveLeft, // Move player left / right + A_MoveRight, + A_MoveForward, // Forward / Backward + A_MoveBackward, + + A_Activate, + + A_Use, //Use weapon, spell, etc. + A_Jump, + A_AutoMove, //Toggle Auto-move forward + A_Rest, //Rest + A_Journal, //Journal + A_Weapon, //Draw/Sheath weapon + A_Spell, //Ready/Unready Casting + A_AlwaysRun, //Toggle Always Run + A_CycleSpellLeft, //cycling through spells + A_CycleSpellRight, + A_CycleWeaponLeft,//Cycling through weapons + A_CycleWeaponRight, + A_ToggleSneak, //Toggles Sneak, add Push-Sneak later + A_ToggleWalk, //Toggle Walking/Running + A_Crouch, + + A_QuickSave, + A_QuickLoad, + A_QuickMenu, + A_ToggleWeapon, + A_ToggleSpell, + + A_LAST // Marker for the last item + }; + + + }; } #endif diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp deleted file mode 100644 index f318ce666..000000000 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "mouselookevent.hpp" - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -#include "../mwworld/player.hpp" - -#include -#include -#include - -using namespace OIS; -using namespace MWInput; - -void MouseLookEvent::event(Type type, int index, const void *p) -{ - if (type != EV_MouseMove || mDisabled) { - return; - } - - MouseEvent *arg = (MouseEvent*)(p); - - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; - - MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); -} diff --git a/apps/openmw/mwinput/mouselookevent.hpp b/apps/openmw/mwinput/mouselookevent.hpp deleted file mode 100644 index af996643f..000000000 --- a/apps/openmw/mwinput/mouselookevent.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _MWINPUT_MOUSELOOKEVENT_H -#define _MWINPUT_MOUSELOOKEVENT_H - -/* - A mouse-look class for Ogre. Accepts input events from Mangle::Input - and translates them. - - You can adjust the mouse sensibility and switch to a different - camera. The mouselook class also has an optional wrap protection - that keeps the camera from flipping upside down. - - You can disable the mouse looker at any time by calling - setCamera(NULL), and reenable it by setting the camera back. - - NOTE: The current implementation will ONLY work for native OIS - events. - */ - -#include - -namespace MWInput -{ - class MouseLookEvent : public Mangle::Input::Event - { - float sensX, sensY; // Mouse sensibility - bool flipProt; // Flip protection - bool mDisabled; - - public: - MouseLookEvent(float sX = 0.2, float sY = 0.2, bool prot=true) - : sensX(sX), sensY(sY), flipProt(prot) - {} - - void setSens(float sX, float sY) { - sensX = sX; - sensY = sY; - } - - void setProt(bool p) { - flipProt = p; - } - - void disable() { - mDisabled = true; - } - - void enable() { - mDisabled = false; - } - - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr MouseLookEventPtr; -} - -#endif diff --git a/extern/oics/CMakeLists.txt b/extern/oics/CMakeLists.txt new file mode 100644 index 000000000..7c14387a4 --- /dev/null +++ b/extern/oics/CMakeLists.txt @@ -0,0 +1,20 @@ +set(OICS_LIBRARY "oics") + +# Sources + +set(OICS_SOURCE_FILES + ICSChannel.cpp + ICSControl.cpp + ICSInputControlSystem.cpp + ICSInputControlSystem_keyboard.cpp + ICSInputControlSystem_mouse.cpp + ICSInputControlSystem_joystick.cpp + tinyxml.cpp + tinyxmlparser.cpp + tinyxmlerror.cpp + tinystr.cpp +) + +add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES}) + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/extern/oics/ICSChannel.cpp b/extern/oics/ICSChannel.cpp new file mode 100644 index 000000000..703f2207c --- /dev/null +++ b/extern/oics/ICSChannel.cpp @@ -0,0 +1,258 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +#define B1(t) (t*t) +#define B2(t) (2*t*(1-t)) +#define B3(t) ((1-t)*(1-t)) + +namespace ICS +{ + Channel::Channel(int number, float initialValue + , float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep) + : mNumber(number) + , mValue(initialValue) + , mSymmetricAt(symmetricAt) + , mBezierStep(bezierStep) + { + mBezierMidPoint.x = bezierMidPointX; + mBezierMidPoint.y = bezierMidPointY; + + setBezierFunction(bezierMidPointY, bezierMidPointX, symmetricAt, bezierStep); + } + + float Channel::getValue() + { + if(mValue == 0 || mValue == 1) + { + return mValue; + } + + BezierFunction::iterator it = mBezierFunction.begin(); + //size_t size_minus_1 = mBezierFunction.size() - 1; + BezierFunction::iterator last = mBezierFunction.end(); + last--; + for ( ; it != last ; ) + { + BezierPoint left = (*it); + BezierPoint right = (*(++it)); + + if( (left.x <= mValue) && (right.x > mValue) ) + { + float val = left.y - (left.x - mValue) * (left.y - right.y) / (left.x - right.x); + + return std::max(0.0,std::min(1.0, val)); + } + } + + return -1; + } + + void Channel::setValue(float value) + { + float previousValue = this->getValue(); + + mValue = value; + + if(previousValue != value) + { + notifyListeners(previousValue); + } + } + + void Channel::notifyListeners(float previousValue) + { + std::list::iterator pos = mListeners.begin(); + while (pos != mListeners.end()) + { + ((ChannelListener* )(*pos))->channelChanged((Channel*)this, this->getValue(), previousValue); + ++pos; + } + } + + void Channel::addControl(Control* control, Channel::ChannelDirection dir, float percentage) + { + ControlChannelBinderItem ccBinderItem; + ccBinderItem.control = control; + ccBinderItem.direction = dir; + ccBinderItem.percentage = percentage; + + mAttachedControls.push_back(ccBinderItem); + } + + Channel::ControlChannelBinderItem Channel::getAttachedControlBinding(Control* control) + { + for(std::vector::iterator it = mAttachedControls.begin() ; + it != mAttachedControls.end() ; it++) + { + if((*it).control == control) + { + return (*it); + } + } + + ControlChannelBinderItem nullBinderItem; + nullBinderItem.control = NULL; + nullBinderItem.direction = Channel/*::ChannelDirection*/::DIRECT; + nullBinderItem.percentage = 0; + return nullBinderItem; + } + + void Channel::update() + { + if(this->getControlsCount() == 1) + { + ControlChannelBinderItem ccBinderItem = mAttachedControls.back(); + float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue(); + + if(ccBinderItem.direction == ICS::Channel::DIRECT) + { + this->setValue(ccBinderItem.control->getInitialValue() + (ccBinderItem.percentage * diff)); + } + else + { + this->setValue(ccBinderItem.control->getInitialValue() - (ccBinderItem.percentage * diff)); + } + } + else + { + float val = 0; + std::vector::const_iterator it; + for(it=mAttachedControls.begin(); it!=mAttachedControls.end(); ++it) + { + ControlChannelBinderItem ccBinderItem = (*it); + float diff = ccBinderItem.control->getValue() - ccBinderItem.control->getInitialValue(); + + if(ccBinderItem.direction == ICS::Channel::DIRECT) + { + val += (ccBinderItem.percentage * diff); + } + else + { + val -= (ccBinderItem.percentage * diff); + } + } + + if(mAttachedControls.size() > 0) + { + this->setValue(mAttachedControls.begin()->control->getInitialValue() + val); + } + } + } + + void Channel::setBezierFunction(float bezierMidPointY, float bezierMidPointX, float symmetricAt, float bezierStep) + { + mBezierMidPoint.x = bezierMidPointX; + mBezierMidPoint.y = bezierMidPointY; + mBezierStep = bezierStep; + mSymmetricAt = symmetricAt; + + mBezierFunction.clear(); + + BezierPoint start; + start.x = 0; + start.y = 0; + + BezierPoint end; + end.x = 1; + end.y = 1; + mBezierFunction.push_front(end); + + FilterInterval interval; + interval.startX = start.x; + interval.startY = start.y; + interval.midX = mBezierMidPoint.x; + interval.midY = mBezierMidPoint.y; + interval.endX = end.x; + interval.endY = end.y; + interval.step = bezierStep; + mIntervals.push_back(interval); + + if(!(mBezierMidPoint.x == 0.5 && mBezierMidPoint.y == 0.5)) + { + float t = mBezierStep; + while(t < 1) + { + BezierPoint p; + p.x = start.x * B1(t) + mBezierMidPoint.x * B2(t) + end.x * B3(t); + p.y = start.y * B1(t) + mBezierMidPoint.y * B2(t) + end.y * B3(t); + mBezierFunction.push_front(p); + + t += mBezierStep; + } + } + + mBezierFunction.push_front(start); + } + + void Channel::addBezierInterval(float startX, float startY, float midX, float midY + , float endX, float endY, float step) + { + FilterInterval interval; + interval.startX = startX; + interval.startY = startY; + interval.midX = midX; + interval.midY = midY; + interval.endX = endX; + interval.endY = endY; + interval.step = step; + mIntervals.push_back(interval); + + float t = 0; + while(t <= 1) + { + BezierPoint p; + p.x = startX * B1(t) + midX * B2(t) + endX * B3(t); + p.y = startY * B1(t) + midY * B2(t) + endY * B3(t); + + BezierFunction::iterator it = mBezierFunction.begin(); + while( it != mBezierFunction.end() ) + { + BezierPoint left = (*it); + BezierPoint right; + ++it; + if( it != mBezierFunction.end() ) + { + right = (*it); + } + else + { + right.x = endX; + right.y = endY; + } + + if(p.x > left.x && p.x < right.x) + { + mBezierFunction.insert(it, p); + break; + } + } + + t += 1.0f / ((endX-startX)/step); + } + } +} \ No newline at end of file diff --git a/extern/oics/ICSChannel.h b/extern/oics/ICSChannel.h new file mode 100644 index 000000000..f98f0d94d --- /dev/null +++ b/extern/oics/ICSChannel.h @@ -0,0 +1,122 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _Channel_H_ +#define _Channel_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannelListener.h" + +namespace ICS +{ + struct FilterInterval{ + //std::string type; //! @todo uncomment when more types implemented + float startX; + float startY; + float midX; + float midY; + float endX; + float endY; + float step; + }; + + typedef std::list IntervalList; + + class DllExport Channel + { + public: + enum ChannelDirection + { + INVERSE = -1, DIRECT = 1 + }; + + typedef struct { + ChannelDirection direction; + float percentage; + Control* control; + } ControlChannelBinderItem; + + + Channel(int number, float initialValue = 0.5 + , float bezierMidPointY = 0.5, float bezierMidPointX = 0.5 + , float symmetricAt = 0, float bezierStep = 0.2); //! @todo implement symetry + ~Channel(){}; + + void setValue(float value); + float getValue(); + + inline int getNumber(){ return mNumber; }; + + void addControl(Control* control, Channel::ChannelDirection dir, float percentage); + inline size_t getControlsCount(){ return mAttachedControls.size(); }; + std::vector getAttachedControls(){ return mAttachedControls; }; + ControlChannelBinderItem getAttachedControlBinding(Control* control); + + void addListener(ChannelListener* ob){ mListeners.push_back(ob); }; + void removeListener(ChannelListener* ob){ mListeners.remove(ob); }; + + void update(); + + void setBezierFunction(float bezierMidPointY, float bezierMidPointX = 0.5 + , float symmetricAt = 0, float bezierStep = 0.2); + + void addBezierInterval(float startX, float startY, float midX, float midY + , float endX, float endY, float step = 0.1); + + IntervalList& getIntervals(){ return mIntervals; }; + + protected: + + int mNumber; + float mValue; + + struct BezierPoint{ + float x; + float y; + bool operator < (const BezierPoint& other){ return x < other.x; } + }; + + typedef std::list BezierFunction; + + BezierPoint mBezierMidPoint; + BezierFunction mBezierFunction; + float mSymmetricAt; + float mBezierStep; + + IntervalList mIntervals; + + std::vector mAttachedControls; + + std::list mListeners; + void notifyListeners(float previousValue); + + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSChannelListener.h b/extern/oics/ICSChannelListener.h new file mode 100644 index 000000000..d520b3bce --- /dev/null +++ b/extern/oics/ICSChannelListener.h @@ -0,0 +1,46 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _ChannelListener_H_ +#define _ChannelListener_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannel.h" + +namespace ICS +{ + + class DllExport ChannelListener + { + public: + virtual void channelChanged(Channel* channel, float currentValue, float previousValue) = 0; + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSControl.cpp b/extern/oics/ICSControl.cpp new file mode 100644 index 000000000..d43733727 --- /dev/null +++ b/extern/oics/ICSControl.cpp @@ -0,0 +1,161 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +#include "ICSControl.h" + +namespace ICS +{ + Control::Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop, bool autoReverseToInitialValue + , float initialValue, float stepSize, float stepsPerSeconds, bool axisBindable) + : mName(name) + , mValue(initialValue) + , mInitialValue(initialValue) + , mStepSize(stepSize) + , mStepsPerSeconds(stepsPerSeconds) + , mAutoReverseToInitialValue(autoReverseToInitialValue) + , mIgnoreAutoReverse(false) + , mAutoChangeDirectionOnLimitsAfterStop(autoChangeDirectionOnLimitsAfterStop) + , mAxisBindable(axisBindable) + , currentChangingDirection(STOP) + { + + } + + Control::~Control() + { + mAttachedChannels.clear(); + } + + void Control::setValue(float value) + { + float previousValue = mValue; + + mValue = std::max(0.0,std::min(1.0,value)); + + if(mValue != previousValue) + { + updateChannels(); + + notifyListeners(previousValue); + } + } + + void Control::attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage) + { + mAttachedChannels.push_back(channel); + channel->addControl(this, direction, percentage); + } + + void Control::updateChannels() + { + std::list::iterator pos = mAttachedChannels.begin(); + while (pos != mAttachedChannels.end()) + { + ((Channel* )(*pos))->update(); + ++pos; + } + } + + void Control::notifyListeners(float previousValue) + { + std::list::iterator pos = mListeners.begin(); + while (pos != mListeners.end()) + { + ((ControlListener* )(*pos))->controlChanged((Control*)this, this->getValue(), previousValue); + ++pos; + } + } + + void Control::setChangingDirection(ControlChangingDirection direction) + { + currentChangingDirection = direction; + mPendingActions.push_back(direction); + } + + void Control::update(float timeSinceLastFrame) + { + if(mPendingActions.size() > 0) + { + size_t timedActionsCount = 0; + + std::list::iterator cached_end = mPendingActions.end(); + for(std::list::iterator it = mPendingActions.begin() ; + it != cached_end ; it++) + { + if( (*it) != Control::STOP ) + { + timedActionsCount++; + } + } + + float timeSinceLastFramePart = timeSinceLastFrame / std::max(1, timedActionsCount); + for(std::list::iterator it = mPendingActions.begin() ; + it != cached_end ; it++) + { + if( (*it) != Control::STOP ) + { + this->setValue(mValue + + (((int)(*it)) * mStepSize * mStepsPerSeconds * (timeSinceLastFramePart))); + } + else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue ) + { + + if(mValue > mInitialValue) + { + this->setValue( std::max( mInitialValue, + mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)))); + } + else if(mValue < mInitialValue) + { + this->setValue( std::min( mInitialValue, + mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFramePart)))); + } + } + } + mPendingActions.clear(); + } + else if( currentChangingDirection != Control::STOP ) + { + this->setValue(mValue + + (((int)currentChangingDirection) * mStepSize * mStepsPerSeconds * (timeSinceLastFrame))); + } + else if(mAutoReverseToInitialValue && !mIgnoreAutoReverse && mValue != mInitialValue ) + { + if(mValue > mInitialValue) + { + this->setValue( std::max( mInitialValue, + mValue - (mStepSize * mStepsPerSeconds * (timeSinceLastFrame)))); + } + else if(mValue < mInitialValue) + { + this->setValue( std::min( mInitialValue, + mValue + (mStepSize * mStepsPerSeconds * (timeSinceLastFrame)))); + } + } + } +} diff --git a/extern/oics/ICSControl.h b/extern/oics/ICSControl.h new file mode 100644 index 000000000..73f1d5494 --- /dev/null +++ b/extern/oics/ICSControl.h @@ -0,0 +1,107 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _Control_H_ +#define _Control_H_ + +#include "ICSPrerequisites.h" + +#include "ICSChannel.h" +#include "ICSControlListener.h" + +namespace ICS +{ + + class DllExport Control + { + public: + + enum ControlChangingDirection + { + DECREASE = -1, STOP = 0, INCREASE = 1 + }; + + Control(const std::string name, bool autoChangeDirectionOnLimitsAfterStop = false, bool autoReverseToInitialValue = false, float initialValue = 0.5, float stepSize = 0.1, float stepsPerSeconds = 2.0, bool axisBindable = true); + ~Control(); + + void setChangingDirection(ControlChangingDirection direction); + inline ControlChangingDirection getChangingDirection(){ return currentChangingDirection; }; + + void setValue(float value); + inline float getValue(){ return mValue; }; + inline float getInitialValue(){ return mInitialValue; }; + + void attachChannel(Channel* channel, Channel::ChannelDirection direction, float percentage = 1.0); + std::list getAttachedChannels(){ return mAttachedChannels; }; + + inline float getStepSize(){ return mStepSize; }; + inline float getStepsPerSeconds(){ return mStepsPerSeconds; }; + + inline void setIgnoreAutoReverse(bool value){ mIgnoreAutoReverse = value; }; // mouse disable autoreverse + inline bool isAutoReverseIgnored(){ return mIgnoreAutoReverse; }; + inline bool getAutoReverse(){ return mAutoReverseToInitialValue; }; + + inline bool getAutoChangeDirectionOnLimitsAfterStop(){ return mAutoChangeDirectionOnLimitsAfterStop; }; + + inline std::string getName(){ return mName; }; + + inline bool isAxisBindable(){ return mAxisBindable; }; + inline void setAxisBindable(bool value){ mAxisBindable = value; }; + + inline void addListener(ControlListener* ob){ mListeners.push_back(ob); }; + inline void removeListener(ControlListener* ob){ mListeners.remove(ob); }; + + void update(float timeSinceLastFrame); + + protected: + float mValue; + float mInitialValue; + std::string mName; + float mStepSize; + float mStepsPerSeconds; + bool mAutoReverseToInitialValue; + bool mIgnoreAutoReverse; + bool mAutoChangeDirectionOnLimitsAfterStop; + bool mAxisBindable; + + Control::ControlChangingDirection currentChangingDirection; + std::list mAttachedChannels; + + std::list mListeners; + + std::list mPendingActions; + + protected: + + void updateChannels(); + void notifyListeners(float previousValue); + + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSControlListener.h b/extern/oics/ICSControlListener.h new file mode 100644 index 000000000..067b2d6f2 --- /dev/null +++ b/extern/oics/ICSControlListener.h @@ -0,0 +1,46 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _ControlListener_H_ +#define _ControlListener_H_ + +#include "ICSPrerequisites.h" + +#include "ICSControl.h" + +namespace ICS +{ + + class DllExport ControlListener + { + public: + virtual void controlChanged(Control* control, float currentValue, float previousValue) = 0; + }; + +} + + +#endif \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp new file mode 100644 index 000000000..1702c853e --- /dev/null +++ b/extern/oics/ICSInputControlSystem.cpp @@ -0,0 +1,929 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + InputControlSystem::InputControlSystem(std::string file, bool active + , DetectingBindingListener* detectingBindingListener + , InputControlSystemLog* log, size_t channelCount) + : mFileName(file) + , mDetectingBindingListener(detectingBindingListener) + , mDetectingBindingControl(NULL) + , mLog(log) + , mXmouseAxisBinded(false), mYmouseAxisBinded(false) + { + ICS_LOG(" - Creating InputControlSystem - "); + + this->mActive = active; + + this->fillOISKeysMap(); + + ICS_LOG("Channel count = " + ToString(channelCount) ); + for(size_t i=0;iLoadFile(); + + if(xmlDoc->Error()) + { + std::ostringstream message; + message << "TinyXml reported an error reading \""+ file + "\". Row " << + (int)xmlDoc->ErrorRow() << ", Col " << (int)xmlDoc->ErrorCol() << ": " << + xmlDoc->ErrorDesc() ; + ICS_LOG(message.str()); + + delete xmlDoc; + return; + } + + xmlRoot = xmlDoc->RootElement(); + if(std::string(xmlRoot->Value()) != "Controller") { + ICS_LOG("Error: Invalid Controller file. Missing element."); + delete xmlDoc; + return; + } + + TiXmlElement* xmlControl = xmlRoot->FirstChildElement("Control"); + + size_t controlChannelCount = 0; + while(xmlControl) + { + TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel"); + while(xmlChannel) + { + controlChannelCount = std::max(channelCount, FromString(xmlChannel->Attribute("number"))); + + xmlChannel = xmlChannel->NextSiblingElement("Channel"); + } + + xmlControl = xmlControl->NextSiblingElement("Control"); + } + + if(controlChannelCount > channelCount) + { + size_t dif = controlChannelCount - channelCount; + ICS_LOG("Warning: default channel count exceeded. Adding " + ToString(dif) + " channels" ); + for(size_t i = channelCount ; i < controlChannelCount ; i++) + { + mChannels.push_back(new Channel((int)i)); + } + } + + ICS_LOG("Applying filters to channels"); + // + // + // + // + + TiXmlElement* xmlChannelFilter = xmlRoot->FirstChildElement("ChannelFilter"); + while(xmlChannelFilter) + { + int ch = FromString(xmlChannelFilter->Attribute("number")); + + TiXmlElement* xmlInterval = xmlChannelFilter->FirstChildElement("Interval"); + while(xmlInterval) + { + std::string type = xmlInterval->Attribute("type"); + + if(type == "bezier") + { + float step = 0.1; + + float startX = FromString(xmlInterval->Attribute("startX")); + float startY = FromString(xmlInterval->Attribute("startY")); + float midX = FromString(xmlInterval->Attribute("midX")); + float midY = FromString(xmlInterval->Attribute("midY")); + float endX = FromString(xmlInterval->Attribute("endX")); + float endY = FromString(xmlInterval->Attribute("endY")); + + step = FromString(xmlInterval->Attribute("step")); + + ICS_LOG("Applying Bezier filter to channel [number=" + + ToString(ch) + ", startX=" + + ToString(startX) + ", startY=" + + ToString(startY) + ", midX=" + + ToString(midX) + ", midY=" + + ToString(midY) + ", endX=" + + ToString(endX) + ", endY=" + + ToString(endY) + ", step=" + + ToString(step) + "]"); + + mChannels.at(ch)->addBezierInterval(startX, startY, midX, midY, endX, endY, step); + } + + xmlInterval = xmlInterval->NextSiblingElement("Interval"); + } + + + xmlChannelFilter = xmlChannelFilter->NextSiblingElement("ChannelFilter"); + } + + xmlControl = xmlRoot->FirstChildElement("Control"); + while(xmlControl) + { + bool axisBindable = true; + if(xmlControl->Attribute("axisBindable")) + { + axisBindable = (std::string( xmlControl->Attribute("axisBindable") ) == "true"); + } + + ICS_LOG("Adding Control [name=" + + std::string( xmlControl->Attribute("name") ) + ", autoChangeDirectionOnLimitsAfterStop=" + + std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) + ", autoReverseToInitialValue=" + + std::string( xmlControl->Attribute("autoReverseToInitialValue") ) + ", initialValue=" + + std::string( xmlControl->Attribute("initialValue") ) + ", stepSize=" + + std::string( xmlControl->Attribute("stepSize") ) + ", stepsPerSeconds=" + + std::string( xmlControl->Attribute("stepsPerSeconds") ) + ", axisBindable=" + + std::string( (axisBindable)? "true" : "false" ) + "]"); + + float _stepSize = 0; + if(xmlControl->Attribute("stepSize")) + { + std::string value(xmlControl->Attribute("stepSize")); + if(value == "MAX") + { + _stepSize = ICS_MAX; + } + else + { + _stepSize = FromString(value.c_str()); + } + } + else + { + ICS_LOG("Warning: no stepSize value found. Default value is 0."); + } + + float _stepsPerSeconds = 0; + if(xmlControl->Attribute("stepsPerSeconds")) + { + std::string value(xmlControl->Attribute("stepsPerSeconds")); + if(value == "MAX") + { + _stepsPerSeconds = ICS_MAX; + } + else + { + _stepsPerSeconds = FromString(value.c_str()); + } + } + else + { + ICS_LOG("Warning: no stepSize value found. Default value is 0."); + } + + addControl( new Control(xmlControl->Attribute("name") + , std::string( xmlControl->Attribute("autoChangeDirectionOnLimitsAfterStop") ) == "true" + , std::string( xmlControl->Attribute("autoReverseToInitialValue") ) == "true" + , FromString(xmlControl->Attribute("initialValue")) + , _stepSize + , _stepsPerSeconds + , axisBindable) ); + + loadKeyBinders(xmlControl); + + loadMouseAxisBinders(xmlControl); + + loadMouseButtonBinders(xmlControl); + + loadJoystickAxisBinders(xmlControl); + + loadJoystickButtonBinders(xmlControl); + + loadJoystickPOVBinders(xmlControl); + + loadJoystickSliderBinders(xmlControl); + + // Attach controls to channels + TiXmlElement* xmlChannel = xmlControl->FirstChildElement("Channel"); + while(xmlChannel) + { + ICS_LOG("\tAttaching control to channel [number=" + + std::string( xmlChannel->Attribute("number") ) + ", direction=" + + std::string( xmlChannel->Attribute("direction") ) + "]"); + + float percentage = 1; + if(xmlChannel->Attribute("percentage")) + { + if(StringIsNumber(xmlChannel->Attribute("percentage"))) + { + float val = FromString(xmlChannel->Attribute("percentage")); + if(val > 1 || val < 0) + { + ICS_LOG("ERROR: attaching percentage value range is [0,1]"); + } + else + { + percentage = val; + } + } + else + { + ICS_LOG("ERROR: attaching percentage value range is [0,1]"); + } + } + + int chNumber = FromString(xmlChannel->Attribute("number")); + if(std::string(xmlChannel->Attribute("direction")) == "DIRECT") + { + mControls.back()->attachChannel(mChannels[ chNumber ],Channel::DIRECT, percentage); + } + else if(std::string(xmlChannel->Attribute("direction")) == "INVERSE") + { + mControls.back()->attachChannel(mChannels[ chNumber ],Channel::INVERSE, percentage); + } + + xmlChannel = xmlChannel->NextSiblingElement("Channel"); + } + + xmlControl = xmlControl->NextSiblingElement("Control"); + } + + std::vector::const_iterator o; + for(o = mChannels.begin(); o != mChannels.end(); ++o) + { + (*o)->update(); + } + + delete xmlDoc; + } + + ICS_LOG(" - InputControlSystem Created - "); + } + + InputControlSystem::~InputControlSystem() + { + ICS_LOG(" - Deleting InputControlSystem (" + mFileName + ") - "); + + mJoystickIDList.clear(); + + std::vector::const_iterator o; + for(o = mChannels.begin(); o != mChannels.end(); ++o) + { + delete (*o); + } + mChannels.clear(); + + std::vector::const_iterator o2; + for(o2 = mControls.begin(); o2 != mControls.end(); ++o2) + { + delete (*o2); + } + mControls.clear(); + + mControlsKeyBinderMap.clear(); + mControlsMouseButtonBinderMap.clear(); + mControlsJoystickButtonBinderMap.clear(); + + mKeys.clear(); + mKeyCodes.clear(); + + ICS_LOG(" - InputControlSystem deleted - "); + } + + std::string InputControlSystem::getBaseFileName() + { + size_t found = mFileName.find_last_of("/\\"); + std::string file = mFileName.substr(found+1); + + return file.substr(0, file.find_last_of(".")); + } + + bool InputControlSystem::save(std::string fileName) + { + if(fileName != "") + { + mFileName = fileName; + } + + TiXmlDocument doc( mFileName.c_str() ); + + TiXmlDeclaration dec; + dec.Parse( "", 0, TIXML_ENCODING_UNKNOWN ); + doc.InsertEndChild(dec); + + TiXmlElement Controller( "Controller" ); + + for(std::vector::const_iterator o = mChannels.begin() ; o != mChannels.end(); o++) + { + ICS::IntervalList intervals = (*o)->getIntervals(); + + if(intervals.size() > 1) // all channels have a default linear filter + { + TiXmlElement ChannelFilter( "ChannelFilter" ); + + ChannelFilter.SetAttribute("number", ToString((*o)->getNumber()).c_str()); + + ICS::IntervalList::const_iterator interval = intervals.begin(); + while( interval != intervals.end() ) + { + // if not default linear filter + if(!( interval->step == 0.2f + && interval->startX == 0.0f + && interval->startY == 0.0f + && interval->midX == 0.5f + && interval->midY == 0.5f + && interval->endX == 1.0f + && interval->endY == 1.0f )) + { + TiXmlElement XMLInterval( "Interval" ); + + XMLInterval.SetAttribute("type", "bezier"); + XMLInterval.SetAttribute("step", ToString(interval->step).c_str()); + + XMLInterval.SetAttribute("startX", ToString(interval->startX).c_str()); + XMLInterval.SetAttribute("startY", ToString(interval->startY).c_str()); + XMLInterval.SetAttribute("midX", ToString(interval->midX).c_str()); + XMLInterval.SetAttribute("midY", ToString(interval->midY).c_str()); + XMLInterval.SetAttribute("endX", ToString(interval->endX).c_str()); + XMLInterval.SetAttribute("endY", ToString(interval->endY).c_str()); + + ChannelFilter.InsertEndChild(XMLInterval); + } + + interval++; + } + + Controller.InsertEndChild(ChannelFilter); + } + } + + for(std::vector::const_iterator o = mControls.begin() ; o != mControls.end(); o++) + { + TiXmlElement control( "Control" ); + + control.SetAttribute( "name", (*o)->getName().c_str() ); + if((*o)->getAutoChangeDirectionOnLimitsAfterStop()) + { + control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "true" ); + } + else + { + control.SetAttribute( "autoChangeDirectionOnLimitsAfterStop", "false" ); + } + if((*o)->getAutoReverse()) + { + control.SetAttribute( "autoReverseToInitialValue", "true" ); + } + else + { + control.SetAttribute( "autoReverseToInitialValue", "false" ); + } + control.SetAttribute( "initialValue", ToString((*o)->getInitialValue()).c_str() ); + + if((*o)->getStepSize() == ICS_MAX) + { + control.SetAttribute( "stepSize", "MAX" ); + } + else + { + control.SetAttribute( "stepSize", ToString((*o)->getStepSize()).c_str() ); + } + + if((*o)->getStepsPerSeconds() == ICS_MAX) + { + control.SetAttribute( "stepsPerSeconds", "MAX" ); + } + else + { + control.SetAttribute( "stepsPerSeconds", ToString((*o)->getStepsPerSeconds()).c_str() ); + } + + if(!(*o)->isAxisBindable()) + { + control.SetAttribute( "axisBindable", "false" ); + } + + if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) != OIS::KC_UNASSIGNED) + { + TiXmlElement keyBinder( "KeyBinder" ); + + keyBinder.SetAttribute( "key", keyCodeToString( + getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + keyBinder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(keyBinder); + } + + if(getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) != OIS::KC_UNASSIGNED) + { + TiXmlElement keyBinder( "KeyBinder" ); + + keyBinder.SetAttribute( "key", keyCodeToString( + getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + keyBinder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(keyBinder); + } + + if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) + != InputControlSystem/*::NamedAxis*/::UNASSIGNED) + { + TiXmlElement binder( "MouseBinder" ); + + InputControlSystem::NamedAxis axis = + getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::INCREASE); + if(axis == InputControlSystem/*::NamedAxis*/::X) + { + binder.SetAttribute( "axis", "X" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Y) + { + binder.SetAttribute( "axis", "Y" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Z) + { + binder.SetAttribute( "axis", "Z" ); + } + + binder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) + != InputControlSystem/*::NamedAxis*/::UNASSIGNED) + { + TiXmlElement binder( "MouseBinder" ); + + InputControlSystem::NamedAxis axis = + getMouseAxisBinding(*o, Control/*::ControlChangingDirection*/::DECREASE); + if(axis == InputControlSystem/*::NamedAxis*/::X) + { + binder.SetAttribute( "axis", "X" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Y) + { + binder.SetAttribute( "axis", "Y" ); + } + else if(axis == InputControlSystem/*::NamedAxis*/::Z) + { + binder.SetAttribute( "axis", "Z" ); + } + + binder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "MouseButtonBinder" ); + + unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::INCREASE); + if(button == OIS::/*MouseButtonID::*/MB_Left) + { + binder.SetAttribute( "button", "LEFT" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Middle) + { + binder.SetAttribute( "button", "MIDDLE" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Right) + { + binder.SetAttribute( "button", "RIGHT" ); + } + else + { + binder.SetAttribute( "button", ToString(button).c_str() ); + } + binder.SetAttribute( "direction", "INCREASE" ); + control.InsertEndChild(binder); + } + + if(getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "MouseButtonBinder" ); + + unsigned int button = getMouseButtonBinding(*o, Control/*::ControlChangingDirection*/::DECREASE); + if(button == OIS::/*MouseButtonID::*/MB_Left) + { + binder.SetAttribute( "button", "LEFT" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Middle) + { + binder.SetAttribute( "button", "MIDDLE" ); + } + else if(button == OIS::/*MouseButtonID::*/MB_Right) + { + binder.SetAttribute( "button", "RIGHT" ); + } + else + { + binder.SetAttribute( "button", ToString(button).c_str() ); + } + binder.SetAttribute( "direction", "DECREASE" ); + control.InsertEndChild(binder); + } + + JoystickIDList::const_iterator it = mJoystickIDList.begin(); + while(it != mJoystickIDList.end()) + { + int deviceId = *it; + + if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickAxisBinder" ); + + binder.SetAttribute( "axis", ToString( + getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickAxisBinder" ); + + binder.SetAttribute( "axis", ToString( + getJoystickAxisBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "JoystickButtonBinder" ); + + binder.SetAttribute( "button", ToString( + getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickButtonBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != ICS_MAX_DEVICE_BUTTONS) + { + TiXmlElement binder( "JoystickButtonBinder" ); + + binder.SetAttribute( "button", ToString( + getJoystickButtonBinding(*o, *it, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE).index >= 0) + { + TiXmlElement binder( "JoystickPOVBinder" ); + + POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE); + + binder.SetAttribute( "pov", ToString(POVPair.index).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + if(POVPair.axis == ICS::InputControlSystem::EastWest) + { + binder.SetAttribute( "axis", "EastWest" ); + } + else + { + binder.SetAttribute( "axis", "NorthSouth" ); + } + + control.InsertEndChild(binder); + } + + if(getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE).index >= 0) + { + TiXmlElement binder( "JoystickPOVBinder" ); + + POVBindingPair POVPair = getJoystickPOVBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE); + + binder.SetAttribute( "pov", ToString(POVPair.index).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + if(POVPair.axis == ICS::InputControlSystem::EastWest) + { + binder.SetAttribute( "axis", "EastWest" ); + } + else + { + binder.SetAttribute( "axis", "NorthSouth" ); + } + + control.InsertEndChild(binder); + } + + if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickSliderBinder" ); + + binder.SetAttribute( "slider", ToString( + getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); + + binder.SetAttribute( "direction", "INCREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + if(getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE) + != /*NamedAxis::*/UNASSIGNED) + { + TiXmlElement binder( "JoystickSliderBinder" ); + + binder.SetAttribute( "slider", ToString( + getJoystickSliderBinding(*o, deviceId, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); + + binder.SetAttribute( "direction", "DECREASE" ); + + binder.SetAttribute( "deviceId", ToString(deviceId).c_str() ); + + control.InsertEndChild(binder); + } + + it++; + } + + + std::list channels = (*o)->getAttachedChannels(); + for(std::list::iterator it = channels.begin() ; + it != channels.end() ; it++) + { + TiXmlElement binder( "Channel" ); + + binder.SetAttribute( "number", ToString((*it)->getNumber()).c_str() ); + + Channel::ChannelDirection direction = (*it)->getAttachedControlBinding(*o).direction; + if(direction == Channel/*::ChannelDirection*/::DIRECT) + { + binder.SetAttribute( "direction", "DIRECT" ); + } + else + { + binder.SetAttribute( "direction", "INVERSE" ); + } + + float percentage = (*it)->getAttachedControlBinding(*o).percentage; + binder.SetAttribute( "percentage", ToString(percentage).c_str() ); + + control.InsertEndChild(binder); + } + + Controller.InsertEndChild(control); + } + + doc.InsertEndChild(Controller); + return doc.SaveFile(); + } + + void InputControlSystem::update(float lTimeSinceLastFrame) + { + if(mActive) + { + std::vector::const_iterator it; + for(it=mControls.begin(); it!=mControls.end(); ++it) + { + (*it)->update(lTimeSinceLastFrame); + } + } + + //! @todo Future versions should consider channel exponentials and mixtures, so + // after updating Controls, Channels should be updated according to their values + } + + float InputControlSystem::getChannelValue(int i) + { + return std::max(0.0,std::min(1.0,mChannels[i]->getValue())); + } + + float InputControlSystem::getControlValue(int i) + { + return mControls[i]->getValue(); + } + + void InputControlSystem::addJoystick(int deviceId) + { + ICS_LOG("Adding joystick (device id: " + ToString(deviceId) + ")"); + + for(int j = 0 ; j < ICS_MAX_JOYSTICK_AXIS ; j++) + { + if(mControlsJoystickAxisBinderMap[deviceId].find(j) == mControlsJoystickAxisBinderMap[deviceId].end()) + { + ControlAxisBinderItem controlJoystickBinderItem; + controlJoystickBinderItem.direction = Control::STOP; + controlJoystickBinderItem.control = NULL; + mControlsJoystickAxisBinderMap[deviceId][j] = controlJoystickBinderItem; + } + } + + mJoystickIDList.push_back(deviceId); + } + + Control* InputControlSystem::findControl(std::string name) + { + if(mActive) + { + std::vector::const_iterator it; + for(it = mControls.begin(); it != mControls.end(); ++it) + { + if( ((Control*)(*it))->getName() == name) + { + return (Control*)(*it); + } + } + } + + return NULL; + } + + void InputControlSystem::enableDetectingBindingState(Control* control + , Control::ControlChangingDirection direction) + { + mDetectingBindingControl = control; + mDetectingBindingDirection = direction; + + mMouseAxisBindingInitialValues[0] = ICS_MOUSE_AXIS_BINDING_NULL_VALUE; + } + + void InputControlSystem::cancelDetectingBindingState() + { + mDetectingBindingControl = NULL; + } + + void InputControlSystem::fillOISKeysMap() + { + mKeys["UNASSIGNED"]= OIS::KC_UNASSIGNED; + mKeys["ESCAPE"]= OIS::KC_ESCAPE; + mKeys["1"]= OIS::KC_1; + mKeys["2"]= OIS::KC_2; + mKeys["3"]= OIS::KC_3; + mKeys["4"]= OIS::KC_4; + mKeys["5"]= OIS::KC_5; + mKeys["6"]= OIS::KC_6; + mKeys["7"]= OIS::KC_7; + mKeys["8"]= OIS::KC_8; + mKeys["9"]= OIS::KC_9; + mKeys["0"]= OIS::KC_0; + mKeys["MINUS"]= OIS::KC_MINUS; + mKeys["EQUALS"]= OIS::KC_EQUALS; + mKeys["BACK"]= OIS::KC_BACK; + mKeys["TAB"]= OIS::KC_TAB; + mKeys["Q"]= OIS::KC_Q; + mKeys["W"]= OIS::KC_W; + mKeys["E"]= OIS::KC_E; + mKeys["R"]= OIS::KC_R; + mKeys["T"]= OIS::KC_T; + mKeys["Y"]= OIS::KC_Y; + mKeys["U"]= OIS::KC_U; + mKeys["I"]= OIS::KC_I; + mKeys["O"]= OIS::KC_O; + mKeys["P"]= OIS::KC_P; + mKeys["LBRACKET"]= OIS::KC_LBRACKET; + mKeys["RBRACKET"]= OIS::KC_RBRACKET; + mKeys["RETURN"]= OIS::KC_RETURN; + mKeys["LCONTROL"]= OIS::KC_LCONTROL; + mKeys["A"]= OIS::KC_A; + mKeys["S"]= OIS::KC_S; + mKeys["D"]= OIS::KC_D; + mKeys["F"]= OIS::KC_F; + mKeys["G"]= OIS::KC_G; + mKeys["H"]= OIS::KC_H; + mKeys["J"]= OIS::KC_J; + mKeys["K"]= OIS::KC_K; + mKeys["L"]= OIS::KC_L; + mKeys["SEMICOLON"]= OIS::KC_SEMICOLON; + mKeys["APOSTROPHE"]= OIS::KC_APOSTROPHE; + mKeys["GRAVE"]= OIS::KC_GRAVE; + mKeys["LSHIFT"]= OIS::KC_LSHIFT; + mKeys["BACKSLASH"]= OIS::KC_BACKSLASH; + mKeys["Z"]= OIS::KC_Z; + mKeys["X"]= OIS::KC_X; + mKeys["C"]= OIS::KC_C; + mKeys["V"]= OIS::KC_V; + mKeys["B"]= OIS::KC_B; + mKeys["N"]= OIS::KC_N; + mKeys["M"]= OIS::KC_M; + mKeys["COMMA"]= OIS::KC_COMMA; + mKeys["PERIOD"]= OIS::KC_PERIOD; + mKeys["SLASH"]= OIS::KC_SLASH; + mKeys["RSHIFT"]= OIS::KC_RSHIFT; + mKeys["MULTIPLY"]= OIS::KC_MULTIPLY; + mKeys["LMENU"]= OIS::KC_LMENU; + mKeys["SPACE"]= OIS::KC_SPACE; + mKeys["CAPITAL"]= OIS::KC_CAPITAL; + mKeys["F1"]= OIS::KC_F1; + mKeys["F2"]= OIS::KC_F2; + mKeys["F3"]= OIS::KC_F3; + mKeys["F4"]= OIS::KC_F4; + mKeys["F5"]= OIS::KC_F5; + mKeys["F6"]= OIS::KC_F6; + mKeys["F7"]= OIS::KC_F7; + mKeys["F8"]= OIS::KC_F8; + mKeys["F9"]= OIS::KC_F9; + mKeys["F10"]= OIS::KC_F10; + mKeys["F11"]= OIS::KC_F11; + mKeys["F12"]= OIS::KC_F12; + mKeys["NUMLOCK"]= OIS::KC_NUMLOCK; + mKeys["SCROLL"]= OIS::KC_SCROLL; + mKeys["NUMPAD7"]= OIS::KC_NUMPAD7; + mKeys["NUMPAD8"]= OIS::KC_NUMPAD8; + mKeys["NUMPAD9"]= OIS::KC_NUMPAD9; + mKeys["SUBTRACT"]= OIS::KC_SUBTRACT; + mKeys["NUMPAD4"]= OIS::KC_NUMPAD4; + mKeys["NUMPAD5"]= OIS::KC_NUMPAD5; + mKeys["NUMPAD6"]= OIS::KC_NUMPAD6; + mKeys["ADD"]= OIS::KC_ADD; + mKeys["NUMPAD1"]= OIS::KC_NUMPAD1; + mKeys["NUMPAD2"]= OIS::KC_NUMPAD2; + mKeys["NUMPAD3"]= OIS::KC_NUMPAD3; + mKeys["NUMPAD0"]= OIS::KC_NUMPAD0; + mKeys["DECIMAL"]= OIS::KC_DECIMAL; + mKeys["RCONTROL"]= OIS::KC_RCONTROL; + mKeys["DIVIDE"]= OIS::KC_DIVIDE; + mKeys["SYSRQ"]= OIS::KC_SYSRQ; + mKeys["RMENU"]= OIS::KC_RMENU; + mKeys["PAUSE"]= OIS::KC_PAUSE; + mKeys["HOME"]= OIS::KC_HOME; + mKeys["UP"]= OIS::KC_UP; + mKeys["PGUP"]= OIS::KC_PGUP; + mKeys["LEFT"]= OIS::KC_LEFT; + mKeys["RIGHT"]= OIS::KC_RIGHT; + mKeys["END"]= OIS::KC_END; + mKeys["DOWN"]= OIS::KC_DOWN; + mKeys["PGDOWN"]= OIS::KC_PGDOWN; + mKeys["INSERT"]= OIS::KC_INSERT; + mKeys["DELETE"]= OIS::KC_DELETE; + mKeys["LWIN"]= OIS::KC_LWIN; + mKeys["RWIN"]= OIS::KC_RWIN; + mKeys["APPS"]= OIS::KC_APPS; + + mKeys["NUMPADENTER"]= OIS::KC_NUMPADENTER; + + for(std::map::iterator it = mKeys.begin() + ; it != mKeys.end() ; it++) + { + mKeyCodes[ it->second ] = it->first; + } + } + + std::string InputControlSystem::keyCodeToString(OIS::KeyCode key) + { + return mKeyCodes[key]; + } + + OIS::KeyCode InputControlSystem::stringToKeyCode(std::string key) + { + return mKeys[key]; + } +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h new file mode 100644 index 000000000..f1c12d3b5 --- /dev/null +++ b/extern/oics/ICSInputControlSystem.h @@ -0,0 +1,256 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#ifndef _InputControlSystem_H_ +#define _InputControlSystem_H_ + +#include "ICSPrerequisites.h" + +#include "ICSControl.h" +#include "ICSChannel.h" + +#define ICS_LOG(text) if(mLog) mLog->logMessage( ("ICS: " + std::string(text)).c_str() ); +#define ICS_MAX_JOYSTICK_AXIS 16 +#define ICS_MOUSE_BINDING_MARGIN 30 +#define ICS_JOYSTICK_AXIS_BINDING_MARGIN 10000 +#define ICS_JOYSTICK_SLIDER_BINDING_MARGIN 10000 +#define ICS_MOUSE_AXIS_BINDING_NULL_VALUE std::numeric_limits::max() + +namespace ICS +{ + class DllExport InputControlSystemLog + { + public: + virtual void logMessage(const char* text) = 0; + }; + + class DllExport InputControlSystem : + public OIS::MouseListener, + public OIS::KeyListener, + public OIS::JoyStickListener + { + + public: + + enum NamedAxis { X = -1, Y = -2, Z = -3, UNASSIGNED = -4 }; + enum POVAxis { NorthSouth = 0, EastWest = 1 }; + + typedef NamedAxis MouseAxis; // MouseAxis is deprecated. It will be removed in future versions + + typedef std::list JoystickIDList; + + typedef struct + { + int index; + POVAxis axis; + } POVBindingPair; + + InputControlSystem(std::string file = "", bool active = true + , DetectingBindingListener* detectingBindingListener = NULL + , InputControlSystemLog* log = NULL, size_t channelCount = 16); + ~InputControlSystem(); + + std::string getFileName(){ return mFileName; }; + std::string getBaseFileName(); + + void setDetectingBindingListener(DetectingBindingListener* detectingBindingListener){ mDetectingBindingListener = detectingBindingListener; }; + DetectingBindingListener* getDetectingBindingListener(){ return mDetectingBindingListener; }; + + // in seconds + void update(float timeSinceLastFrame); + + inline Channel* getChannel(int i){ return mChannels[i]; }; + float getChannelValue(int i); + inline int getChannelCount(){ return (int)mChannels.size(); }; + + inline Control* getControl(int i){ return mControls[i]; }; + float getControlValue(int i); + inline int getControlCount(){ return (int)mControls.size(); }; + inline void addControl(Control* control){ mControls.push_back(control); }; + + Control* findControl(std::string name); + + inline void activate(){ this->mActive = true; }; + inline void deactivate(){ this->mActive = false; }; + + void addJoystick(int deviceId); + JoystickIDList& getJoystickIdList(){ return mJoystickIDList; }; + + // MouseListener + bool mouseMoved(const OIS::MouseEvent &evt); + bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID); + bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID); + + // KeyListener + bool keyPressed(const OIS::KeyEvent &evt); + bool keyReleased(const OIS::KeyEvent &evt); + + // JoyStickListener + bool buttonPressed(const OIS::JoyStickEvent &evt, int button); + bool buttonReleased(const OIS::JoyStickEvent &evt, int button); + bool axisMoved(const OIS::JoyStickEvent &evt, int axis); + bool povMoved(const OIS::JoyStickEvent &evt, int index); + bool sliderMoved(const OIS::JoyStickEvent &evt, int index); + + void addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction); + void addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction); + void addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction); + void addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction); + void addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction); + void addJoystickPOVBinding(Control* control, int deviceId, int index, POVAxis axis, Control::ControlChangingDirection direction); + void addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction); + void removeKeyBinding(OIS::KeyCode key); + void removeMouseAxisBinding(NamedAxis axis); + void removeMouseButtonBinding(unsigned int button); + void removeJoystickAxisBinding(int deviceId, int axis); + void removeJoystickButtonBinding(int deviceId, unsigned int button); + void removeJoystickPOVBinding(int deviceId, int index, POVAxis axis); + void removeJoystickSliderBinding(int deviceId, int index); + + OIS::KeyCode getKeyBinding(Control* control, ICS::Control::ControlChangingDirection direction); + NamedAxis getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction); + unsigned int getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction); + int getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + unsigned int getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + POVBindingPair getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + int getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction); + + std::string keyCodeToString(OIS::KeyCode key); + OIS::KeyCode stringToKeyCode(std::string key); + + void enableDetectingBindingState(Control* control, Control::ControlChangingDirection direction); + void cancelDetectingBindingState(); + + bool save(std::string fileName = ""); + + protected: + + void loadKeyBinders(TiXmlElement* xmlControlNode); + void loadMouseAxisBinders(TiXmlElement* xmlControlNode); + void loadMouseButtonBinders(TiXmlElement* xmlControlNode); + void loadJoystickAxisBinders(TiXmlElement* xmlControlNode); + void loadJoystickButtonBinders(TiXmlElement* xmlControlNode); + void loadJoystickPOVBinders(TiXmlElement* xmlControlNode); + void loadJoystickSliderBinders(TiXmlElement* xmlControlNode); + + void addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction); + void removeMouseAxisBinding_(int axis); + + protected: + + typedef struct { + Control::ControlChangingDirection direction; + Control* control; + } ControlKeyBinderItem; + + typedef ControlKeyBinderItem ControlAxisBinderItem; + typedef ControlKeyBinderItem ControlButtonBinderItem; + typedef ControlKeyBinderItem ControlPOVBinderItem; + typedef ControlKeyBinderItem ControlSliderBinderItem; + + typedef struct { + Control* control; + Control::ControlChangingDirection direction; + } PendingActionItem; + + std::list mPendingActions; + + std::string mFileName; + + typedef std::map ControlsKeyBinderMapType; // + typedef std::map ControlsAxisBinderMapType; // + typedef std::map ControlsButtonBinderMapType; // + typedef std::map ControlsPOVBinderMapType; // + typedef std::map ControlsSliderBinderMapType; // + + typedef std::map JoystickAxisBinderMapType; // > + typedef std::map JoystickButtonBinderMapType; // > + typedef std::map > JoystickPOVBinderMapType; // > > + typedef std::map JoystickSliderBinderMapType; // > + + ControlsAxisBinderMapType mControlsMouseAxisBinderMap; // + ControlsButtonBinderMapType mControlsMouseButtonBinderMap; // + JoystickAxisBinderMapType mControlsJoystickAxisBinderMap; // > + JoystickButtonBinderMapType mControlsJoystickButtonBinderMap; // > + JoystickPOVBinderMapType mControlsJoystickPOVBinderMap; // > > + JoystickSliderBinderMapType mControlsJoystickSliderBinderMap; // > + + std::vector mControls; + std::vector mChannels; + + ControlsKeyBinderMapType mControlsKeyBinderMap; + std::map mKeys; + std::map mKeyCodes; + + bool mActive; + InputControlSystemLog* mLog; + + DetectingBindingListener* mDetectingBindingListener; + Control* mDetectingBindingControl; + Control::ControlChangingDirection mDetectingBindingDirection; + + bool mXmouseAxisBinded; + bool mYmouseAxisBinded; + + JoystickIDList mJoystickIDList; + + int mMouseAxisBindingInitialValues[3]; + + private: + + void fillOISKeysMap(); + }; + + class DllExport DetectingBindingListener + { + public: + virtual void keyBindingDetected(InputControlSystem* ICS, Control* control + , OIS::KeyCode key, Control::ControlChangingDirection direction); + + virtual void mouseAxisBindingDetected(InputControlSystem* ICS, Control* control + , InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction); + + virtual void mouseButtonBindingDetected(InputControlSystem* ICS, Control* control + , unsigned int button, Control::ControlChangingDirection direction); + + virtual void joystickAxisBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int axis, Control::ControlChangingDirection direction); + + virtual void joystickButtonBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, unsigned int button, Control::ControlChangingDirection direction); + + virtual void joystickPOVBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction); + + virtual void joystickSliderBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int slider, Control::ControlChangingDirection direction); + }; + + static const float ICS_MAX = std::numeric_limits::max(); +} + + +#endif diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp new file mode 100644 index 000000000..1e66599ea --- /dev/null +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -0,0 +1,665 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + // load xml + void InputControlSystem::loadJoystickAxisBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickBinder = xmlControlNode->FirstChildElement("JoystickAxisBinder"); + while(xmlJoystickBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickAxisBinding(mControls.back(), FromString(xmlJoystickBinder->Attribute("deviceId")) + , FromString(xmlJoystickBinder->Attribute("axis")), dir); + + xmlJoystickBinder = xmlJoystickBinder->NextSiblingElement("JoystickAxisBinder"); + } + } + + void InputControlSystem::loadJoystickButtonBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickButtonBinder = xmlControlNode->FirstChildElement("JoystickButtonBinder"); + while(xmlJoystickButtonBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickButtonBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickButtonBinding(mControls.back(), FromString(xmlJoystickButtonBinder->Attribute("deviceId")) + , FromString(xmlJoystickButtonBinder->Attribute("button")), dir); + + xmlJoystickButtonBinder = xmlJoystickButtonBinder->NextSiblingElement("JoystickButtonBinder"); + } + } + + void InputControlSystem::loadJoystickPOVBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickPOVBinder = xmlControlNode->FirstChildElement("JoystickPOVBinder"); + while(xmlJoystickPOVBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickPOVBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + InputControlSystem::POVAxis axis = /*POVAxis::*/NorthSouth; + if(std::string(xmlJoystickPOVBinder->Attribute("axis")) == "EastWest") + { + axis = /*POVAxis::*/EastWest; + } + + addJoystickPOVBinding(mControls.back(), FromString(xmlJoystickPOVBinder->Attribute("deviceId")) + , FromString(xmlJoystickPOVBinder->Attribute("pov")), axis, dir); + + xmlJoystickPOVBinder = xmlJoystickPOVBinder->NextSiblingElement("JoystickPOVBinder"); + } + } + + void InputControlSystem::loadJoystickSliderBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlJoystickSliderBinder = xmlControlNode->FirstChildElement("JoystickSliderBinder"); + while(xmlJoystickSliderBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlJoystickSliderBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addJoystickSliderBinding(mControls.back(), FromString(xmlJoystickSliderBinder->Attribute("deviceId")) + , FromString(xmlJoystickSliderBinder->Attribute("slider")), dir); + + xmlJoystickSliderBinder = xmlJoystickSliderBinder->NextSiblingElement("JoystickSliderBinder"); + } + } + + // add bindings + void InputControlSystem::addJoystickAxisBinding(Control* control, int deviceId, int axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding AxisBinder [deviceid=" + + ToString(deviceId) + ", axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlAxisBinderItem controlAxisBinderItem; + controlAxisBinderItem.control = control; + controlAxisBinderItem.direction = direction; + mControlsJoystickAxisBinderMap[ deviceId ][ axis ] = controlAxisBinderItem; + } + + void InputControlSystem::addJoystickButtonBinding(Control* control, int deviceId, unsigned int button, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickButtonBinder [deviceId=" + + ToString(deviceId) + ", button=" + + ToString(button) + ", direction=" + + ToString(direction) + "]"); + + ControlButtonBinderItem controlJoystickButtonBinderItem; + controlJoystickButtonBinderItem.direction = direction; + controlJoystickButtonBinderItem.control = control; + mControlsJoystickButtonBinderMap[ deviceId ][ button ] = controlJoystickButtonBinderItem; + } + + void InputControlSystem::addJoystickPOVBinding(Control* control, int deviceId, int index, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickPOVBinder [deviceId=" + + ToString(deviceId) + ", pov=" + + ToString(index) + ", axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlPOVBinderItem ControlPOVBinderItem; + ControlPOVBinderItem.direction = direction; + ControlPOVBinderItem.control = control; + mControlsJoystickPOVBinderMap[ deviceId ][ index ][ axis ] = ControlPOVBinderItem; + } + + void InputControlSystem::addJoystickSliderBinding(Control* control, int deviceId, int index, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding JoystickSliderBinder [deviceId=" + + ToString(deviceId) + ", direction=" + + ToString(index) + ", direction=" + + ToString(direction) + "]"); + + ControlSliderBinderItem ControlSliderBinderItem; + ControlSliderBinderItem.direction = direction; + ControlSliderBinderItem.control = control; + mControlsJoystickSliderBinderMap[ deviceId ][ index ] = ControlSliderBinderItem; + } + + // get bindings + int InputControlSystem::getJoystickAxisBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end()) + { + ControlsAxisBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].begin(); + while(it != mControlsJoystickAxisBinderMap[deviceId].end()) + { + if(it->first >= 0 && it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return /*NamedAxis::*/UNASSIGNED; + } + + unsigned int InputControlSystem::getJoystickButtonBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].begin(); + while(it != mControlsJoystickButtonBinderMap[deviceId].end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return ICS_MAX_DEVICE_BUTTONS; + } + + InputControlSystem::POVBindingPair InputControlSystem::getJoystickPOVBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + POVBindingPair result; + result.index = -1; + + if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end()) + { + //ControlsAxisBinderMapType::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin(); + std::map::iterator it = mControlsJoystickPOVBinderMap[deviceId].begin(); + while(it != mControlsJoystickPOVBinderMap[deviceId].end()) + { + ControlsPOVBinderMapType::const_iterator it2 = it->second.begin(); + while(it2 != it->second.end()) + { + if(it2->second.control == control && it2->second.direction == direction) + { + result.index = it->first; + result.axis = (POVAxis)it2->first; + return result; + } + it2++; + } + + it++; + } + } + + return result; + } + + int InputControlSystem::getJoystickSliderBinding(Control* control, int deviceId, ICS::Control::ControlChangingDirection direction) + { + if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].begin(); + while(it != mControlsJoystickSliderBinderMap[deviceId].end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + } + + return /*NamedAxis::*/UNASSIGNED; + } + + // remove bindings + void InputControlSystem::removeJoystickAxisBinding(int deviceId, int axis) + { + if(mControlsJoystickAxisBinderMap.find(deviceId) != mControlsJoystickAxisBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickAxisBinderMap[deviceId].find(axis); + if(it != mControlsJoystickAxisBinderMap[deviceId].end()) + { + mControlsJoystickAxisBinderMap[deviceId].erase(it); + } + } + } + + void InputControlSystem::removeJoystickButtonBinding(int deviceId, unsigned int button) + { + if(mControlsJoystickButtonBinderMap.find(deviceId) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickButtonBinderMap[deviceId].find(button); + if(it != mControlsJoystickButtonBinderMap[deviceId].end()) + { + mControlsJoystickButtonBinderMap[deviceId].erase(it); + } + } + } + + void InputControlSystem::removeJoystickPOVBinding(int deviceId, int index, POVAxis axis) + { + if(mControlsJoystickPOVBinderMap.find(deviceId) != mControlsJoystickPOVBinderMap.end()) + { + std::map::iterator it = mControlsJoystickPOVBinderMap[deviceId].find(index); + if(it != mControlsJoystickPOVBinderMap[deviceId].end()) + { + if(it->second.find(axis) != it->second.end()) + { + mControlsJoystickPOVBinderMap[deviceId].find(index)->second.erase( it->second.find(axis) ); + } + } + } + } + + void InputControlSystem::removeJoystickSliderBinding(int deviceId, int index) + { + if(mControlsJoystickSliderBinderMap.find(deviceId) != mControlsJoystickSliderBinderMap.end()) + { + ControlsButtonBinderMapType::iterator it = mControlsJoystickSliderBinderMap[deviceId].find(index); + if(it != mControlsJoystickSliderBinderMap[deviceId].end()) + { + mControlsJoystickSliderBinderMap[deviceId].erase(it); + } + } + } + + // joyStick listeners + bool InputControlSystem::buttonPressed(const OIS::JoyStickEvent &evt, int button) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->joystickButtonBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), button, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::buttonReleased(const OIS::JoyStickEvent &evt, int button) + { + if(mActive) + { + if(mControlsJoystickButtonBinderMap.find(evt.device->getID()) != mControlsJoystickButtonBinderMap.end()) + { + ControlsButtonBinderMapType::const_iterator it = mControlsJoystickButtonBinderMap[evt.device->getID()].find(button); + if(it != mControlsJoystickButtonBinderMap[evt.device->getID()].end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + } + return true; + } + + bool InputControlSystem::axisMoved(const OIS::JoyStickEvent &evt, int axis) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickAxisBinderMap.find(evt.device->getID()) != mControlsJoystickAxisBinderMap.end()) + { + ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + Control* ctrl = joystickBinderItem.control; + if(ctrl) + { + ctrl->setIgnoreAutoReverse(true); + if(joystickBinderItem.direction == Control::INCREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)( evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( valDisplaced / axisRange ); + } + else if(joystickBinderItem.direction == Control::DECREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)(evt.state.mAxes[axis].abs - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( 1 - ( valDisplaced / axisRange ) ); + } + } + } + } + else if(mDetectingBindingListener) + { + //ControlAxisBinderItem joystickBinderItem = mControlsJoystickAxisBinderMap[ evt.device->getID() ][ axis ]; // joystic axis start at 0 index + //Control* ctrl = joystickBinderItem.control; + //if(ctrl && ctrl->isAxisBindable()) + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if( abs( evt.state.mAxes[axis].abs ) > ICS_JOYSTICK_AXIS_BINDING_MARGIN) + { + mDetectingBindingListener->joystickAxisBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), axis, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::povMoved(const OIS::JoyStickEvent &evt, int index) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickPOVBinderMap.find(evt.device->getID()) != mControlsJoystickPOVBinderMap.end()) + { + std::map::const_iterator i = mControlsJoystickPOVBinderMap[ evt.device->getID() ].find(index); + if(i != mControlsJoystickPOVBinderMap[ evt.device->getID() ].end()) + { + if(evt.state.mPOV[index].direction != OIS::Pov::West + && evt.state.mPOV[index].direction != OIS::Pov::East + && evt.state.mPOV[index].direction != OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); + if(it != i->second.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::North + || evt.state.mPOV[index].direction == OIS::Pov::NorthWest + || evt.state.mPOV[index].direction == OIS::Pov::NorthEast) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction)); + } + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + + if(evt.state.mPOV[index].direction != OIS::Pov::North + && evt.state.mPOV[index].direction != OIS::Pov::South + && evt.state.mPOV[index].direction != OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/EastWest ); + if(it != i->second.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::East + || evt.state.mPOV[index].direction == OIS::Pov::NorthEast + || evt.state.mPOV[index].direction == OIS::Pov::SouthEast) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + it->second.control->setChangingDirection((Control::ControlChangingDirection)(-1 * it->second.direction)); + } + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + + if(evt.state.mPOV[index].direction == OIS::Pov::Centered) + { + ControlsPOVBinderMapType::const_iterator it = i->second.find( /*POVAxis::*/NorthSouth ); + if(it != i->second.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + + it = i->second.find( /*POVAxis::*/EastWest ); + if(it != i->second.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + } + } + } + else if(mDetectingBindingListener) + { + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if(evt.state.mPOV[index].direction == OIS::Pov::West + || evt.state.mPOV[index].direction == OIS::Pov::East + || evt.state.mPOV[index].direction == OIS::Pov::North + || evt.state.mPOV[index].direction == OIS::Pov::South) + { + POVAxis povAxis = NorthSouth; + if(evt.state.mPOV[index].direction == OIS::Pov::West + || evt.state.mPOV[index].direction == OIS::Pov::East) + { + povAxis = EastWest; + } + + mDetectingBindingListener->joystickPOVBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), index, povAxis, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mControlsJoystickSliderBinderMap.find(evt.device->getID()) != mControlsJoystickSliderBinderMap.end()) + { + ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; + Control* ctrl = joystickBinderItem.control; + if(ctrl) + { + ctrl->setIgnoreAutoReverse(true); + if(joystickBinderItem.direction == Control::INCREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)( evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( valDisplaced / axisRange ); + } + else if(joystickBinderItem.direction == Control::DECREASE) + { + float axisRange = OIS::JoyStick::MAX_AXIS - OIS::JoyStick::MIN_AXIS; + float valDisplaced = (float)(evt.state.mSliders[index].abX - OIS::JoyStick::MIN_AXIS); + + ctrl->setValue( 1 - ( valDisplaced / axisRange ) ); + } + } + } + } + else if(mDetectingBindingListener) + { + /*ControlSliderBinderItem joystickBinderItem = mControlsJoystickSliderBinderMap[ evt.device->getID() ][ index ]; + Control* ctrl = joystickBinderItem.control; + if(ctrl && ctrl->isAxisBindable()) + {*/ + if(mDetectingBindingControl && mDetectingBindingControl->isAxisBindable()) + { + if( abs( evt.state.mSliders[index].abX ) > ICS_JOYSTICK_SLIDER_BINDING_MARGIN) + { + mDetectingBindingListener->joystickSliderBindingDetected(this, + mDetectingBindingControl, evt.device->getID(), index, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + // joystick auto bindings + void DetectingBindingListener::joystickAxisBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int axis, Control::ControlChangingDirection direction) + { + // if the joystick axis is used by another control, remove it + ICS->removeJoystickAxisBinding(deviceId, axis); + + // if the control has an axis assigned, remove it + int oldAxis = ICS->getJoystickAxisBinding(control, deviceId, direction); + if(oldAxis != InputControlSystem::UNASSIGNED) + { + ICS->removeJoystickAxisBinding(deviceId, oldAxis); + } + + ICS->addJoystickAxisBinding(control, deviceId, axis, direction); + ICS->cancelDetectingBindingState(); + } + void DetectingBindingListener::joystickButtonBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, unsigned int button, Control::ControlChangingDirection direction) + { + // if the joystick button is used by another control, remove it + ICS->removeJoystickButtonBinding(deviceId, button); + + // if the control has a joystick button assigned, remove it + unsigned int oldButton = ICS->getJoystickButtonBinding(control, deviceId, direction); + if(oldButton != ICS_MAX_DEVICE_BUTTONS) + { + ICS->removeJoystickButtonBinding(deviceId, oldButton); + } + + ICS->addJoystickButtonBinding(control, deviceId, button, direction); + ICS->cancelDetectingBindingState(); + } + + + void DetectingBindingListener::joystickPOVBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int pov, InputControlSystem::POVAxis axis, Control::ControlChangingDirection direction) + { + // if the joystick slider is used by another control, remove it + ICS->removeJoystickPOVBinding(deviceId, pov, axis); + + // if the control has a joystick button assigned, remove it + ICS::InputControlSystem::POVBindingPair oldPOV = ICS->getJoystickPOVBinding(control, deviceId, direction); + if(oldPOV.index >= 0 && oldPOV.axis == axis) + { + ICS->removeJoystickPOVBinding(deviceId, oldPOV.index, oldPOV.axis); + } + + ICS->addJoystickPOVBinding(control, deviceId, pov, axis, direction); + ICS->cancelDetectingBindingState(); + } + + void DetectingBindingListener::joystickSliderBindingDetected(InputControlSystem* ICS, Control* control + , int deviceId, int slider, Control::ControlChangingDirection direction) + { + // if the joystick slider is used by another control, remove it + ICS->removeJoystickSliderBinding(deviceId, slider); + + // if the control has a joystick slider assigned, remove it + int oldSlider = ICS->getJoystickSliderBinding(control, deviceId, direction); + if(oldSlider != InputControlSystem::/*NamedAxis::*/UNASSIGNED) + { + ICS->removeJoystickSliderBinding(deviceId, oldSlider); + } + + ICS->addJoystickSliderBinding(control, deviceId, slider, direction); + ICS->cancelDetectingBindingState(); + } +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp new file mode 100644 index 000000000..8ef81d979 --- /dev/null +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -0,0 +1,156 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + void InputControlSystem::loadKeyBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlKeyBinder = xmlControlNode->FirstChildElement("KeyBinder"); + while(xmlKeyBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlKeyBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlKeyBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + addKeyBinding(mControls.back(), mKeys[xmlKeyBinder->Attribute("key")], dir); + + xmlKeyBinder = xmlKeyBinder->NextSiblingElement("KeyBinder"); + } + } + + void InputControlSystem::addKeyBinding(Control* control, OIS::KeyCode key, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding KeyBinder [key=" + + keyCodeToString(key) + ", direction=" + + ToString(direction) + "]"); + + ControlKeyBinderItem controlKeyBinderItem; + controlKeyBinderItem.control = control; + controlKeyBinderItem.direction = direction; + mControlsKeyBinderMap[ key ] = controlKeyBinderItem; + } + + void InputControlSystem::removeKeyBinding(OIS::KeyCode key) + { + ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.find(key); + if(it != mControlsKeyBinderMap.end()) + { + mControlsKeyBinderMap.erase(it); + } + } + + OIS::KeyCode InputControlSystem::getKeyBinding(Control* control + , ICS::Control::ControlChangingDirection direction) + { + ControlsKeyBinderMapType::iterator it = mControlsKeyBinderMap.begin(); + while(it != mControlsKeyBinderMap.end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + + return OIS::KC_UNASSIGNED; + } + bool InputControlSystem::keyPressed(const OIS::KeyEvent &evt) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + if(it != mControlsKeyBinderMap.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->keyBindingDetected(this, + mDetectingBindingControl, evt.key, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::keyReleased(const OIS::KeyEvent &evt) + { + if(mActive) + { + ControlsKeyBinderMapType::const_iterator it = mControlsKeyBinderMap.find(evt.key); + if(it != mControlsKeyBinderMap.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + + return true; + } + + void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control + , OIS::KeyCode key, Control::ControlChangingDirection direction) + { + // if the key is used by another control, remove it + ICS->removeKeyBinding(key); + + // if the control has a key assigned, remove it + OIS::KeyCode oldKey = ICS->getKeyBinding(control, direction); + if(oldKey != OIS::KC_UNASSIGNED) + { + ICS->removeKeyBinding(oldKey); + } + + ICS->addKeyBinding(control, key, direction); + ICS->cancelDetectingBindingState(); + } + +} \ No newline at end of file diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp new file mode 100644 index 000000000..c62f1765e --- /dev/null +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -0,0 +1,397 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSInputControlSystem.h" + +namespace ICS +{ + // load xml + void InputControlSystem::loadMouseAxisBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlMouseBinder = xmlControlNode->FirstChildElement("MouseBinder"); + while(xmlMouseBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlMouseBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlMouseBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + NamedAxis axis = /*NamedAxis::*/ X; + if((*xmlMouseBinder->Attribute("axis")) == 'Y') + { + axis = /*NamedAxis::*/ Y; + } + else if((*xmlMouseBinder->Attribute("axis")) == 'Z') + { + axis = /*NamedAxis::*/ Z; + } + + addMouseAxisBinding(mControls.back(), axis, dir); + + xmlMouseBinder = xmlMouseBinder->NextSiblingElement("MouseBinder"); + } + } + + void InputControlSystem::loadMouseButtonBinders(TiXmlElement* xmlControlNode) + { + TiXmlElement* xmlMouseButtonBinder = xmlControlNode->FirstChildElement("MouseButtonBinder"); + while(xmlMouseButtonBinder) + { + Control::ControlChangingDirection dir = Control::STOP; + if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "INCREASE") + { + dir = Control::INCREASE; + } + else if(std::string(xmlMouseButtonBinder->Attribute("direction")) == "DECREASE") + { + dir = Control::DECREASE; + } + + int button = 0; + if(std::string(xmlMouseButtonBinder->Attribute("button")) == "LEFT") + { + button = OIS::/*MouseButtonID::*/MB_Left; + } + else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "RIGHT") + { + button = OIS::/*MouseButtonID::*/MB_Right; + } + else if(std::string(xmlMouseButtonBinder->Attribute("button")) == "MIDDLE") + { + button = OIS::/*MouseButtonID::*/MB_Middle; + } + else + { + button = FromString(xmlMouseButtonBinder->Attribute("button")); + } + + addMouseButtonBinding(mControls.back(), button, dir); + + xmlMouseButtonBinder = xmlMouseButtonBinder->NextSiblingElement("MouseButtonBinder"); + } + } + + + // add bindings + void InputControlSystem::addMouseAxisBinding(Control* control, NamedAxis axis, Control::ControlChangingDirection direction) + { + if(axis == /*NamedAxis::*/X) + { + mXmouseAxisBinded = true; + } + else if(axis == /*NamedAxis::*/Y) + { + mYmouseAxisBinded = true; + } + + addMouseAxisBinding_(control, axis, direction); + } + + /*protected*/ void InputControlSystem::addMouseAxisBinding_(Control* control, int axis, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding AxisBinder [axis=" + + ToString(axis) + ", direction=" + + ToString(direction) + "]"); + + ControlAxisBinderItem controlAxisBinderItem; + controlAxisBinderItem.control = control; + controlAxisBinderItem.direction = direction; + mControlsMouseAxisBinderMap[ axis ] = controlAxisBinderItem; + } + + void InputControlSystem::addMouseButtonBinding(Control* control, unsigned int button, Control::ControlChangingDirection direction) + { + ICS_LOG("\tAdding MouseButtonBinder [button=" + + ToString(button) + ", direction=" + + ToString(direction) + "]"); + + ControlButtonBinderItem controlMouseButtonBinderItem; + controlMouseButtonBinderItem.direction = direction; + controlMouseButtonBinderItem.control = control; + mControlsMouseButtonBinderMap[ button ] = controlMouseButtonBinderItem; + } + + // get bindings + InputControlSystem::NamedAxis InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction) + { + ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin(); + while(it != mControlsMouseAxisBinderMap.end()) + { + if(it->first < 0 && it->second.control == control && it->second.direction == direction) + { + return (InputControlSystem::NamedAxis)(it->first); + } + it++; + } + + return /*NamedAxis::*/UNASSIGNED; + } + + //int InputControlSystem::getMouseAxisBinding(Control* control, ICS::Control::ControlChangingDirection direction) + //{ + // ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.begin(); + // while(it != mControlsMouseAxisBinderMap.end()) + // { + // if(it->first >= 0 && it->second.control == control && it->second.direction == direction) + // { + // return it->first; + // } + // it++; + // } + + // return /*NamedAxis::*/UNASSIGNED; + //} + + unsigned int InputControlSystem::getMouseButtonBinding(Control* control, ICS::Control::ControlChangingDirection direction) + { + ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.begin(); + while(it != mControlsMouseButtonBinderMap.end()) + { + if(it->second.control == control && it->second.direction == direction) + { + return it->first; + } + it++; + } + + return ICS_MAX_DEVICE_BUTTONS; + } + + // remove bindings + void InputControlSystem::removeMouseAxisBinding(NamedAxis axis) + { + if(axis == /*NamedAxis::*/X) + { + mXmouseAxisBinded = false; + } + else if(axis == /*NamedAxis::*/Y) + { + mYmouseAxisBinded = false; + } + + removeMouseAxisBinding_(axis); + } + /*protected*/ void InputControlSystem::removeMouseAxisBinding_(int axis) + { + ControlsAxisBinderMapType::iterator it = mControlsMouseAxisBinderMap.find(axis); + if(it != mControlsMouseAxisBinderMap.end()) + { + mControlsMouseAxisBinderMap.erase(it); + } + } + + + void InputControlSystem::removeMouseButtonBinding(unsigned int button) + { + ControlsButtonBinderMapType::iterator it = mControlsMouseButtonBinderMap.find(button); + if(it != mControlsMouseButtonBinderMap.end()) + { + mControlsMouseButtonBinderMap.erase(it); + } + } + + // mouse Listeners + bool InputControlSystem::mouseMoved(const OIS::MouseEvent &evt) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + if(mXmouseAxisBinded && evt.state.X.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/X ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.X.abs) / float(evt.state.width) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( 1 - float( evt.state.X.abs / float(evt.state.width) ) ); + } + } + + if(mYmouseAxisBinded && evt.state.Y.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsMouseAxisBinderMap[ /*NamedAxis::*/Y ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.Y.abs) / float(evt.state.height) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( 1 - float( evt.state.Y.abs / float(evt.state.height) ) ); + } + } + + //! @todo Whats the range of the Z axis? + /*if(evt.state.Z.rel) + { + ControlAxisBinderItem mouseBinderItem = mControlsAxisBinderMap[ NamedAxis::Z ]; + Control* ctrl = mouseBinderItem.control; + ctrl->setIgnoreAutoReverse(true); + if(mouseBinderItem.direction == Control::INCREASE) + { + ctrl->setValue( float( (evt.state.Z.abs) / float(evt.state.¿width?) ) ); + } + else if(mouseBinderItem.direction == Control::DECREASE) + { + ctrl->setValue( float( (1 - evt.state.Z.abs) / float(evt.state.¿width?) ) ); + } + }*/ + } + else if(mDetectingBindingListener) + { + if(mDetectingBindingControl->isAxisBindable()) + { + if(mMouseAxisBindingInitialValues[0] == ICS_MOUSE_AXIS_BINDING_NULL_VALUE) + { + mMouseAxisBindingInitialValues[0] = 0; + mMouseAxisBindingInitialValues[1] = 0; + mMouseAxisBindingInitialValues[2] = 0; + } + + mMouseAxisBindingInitialValues[0] += evt.state.X.rel; + mMouseAxisBindingInitialValues[1] += evt.state.Y.rel; + mMouseAxisBindingInitialValues[2] += evt.state.Z.rel; + + if( abs(mMouseAxisBindingInitialValues[0]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, X, mDetectingBindingDirection); + } + else if( abs(mMouseAxisBindingInitialValues[1]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, Y, mDetectingBindingDirection); + } + else if( abs(mMouseAxisBindingInitialValues[2]) > ICS_MOUSE_BINDING_MARGIN ) + { + mDetectingBindingListener->mouseAxisBindingDetected(this, + mDetectingBindingControl, Z, mDetectingBindingDirection); + } + } + } + } + + return true; + } + + bool InputControlSystem::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + { + if(mActive) + { + if(!mDetectingBindingControl) + { + ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn); + if(it != mControlsMouseButtonBinderMap.end()) + { + it->second.control->setIgnoreAutoReverse(false); + if(!it->second.control->getAutoChangeDirectionOnLimitsAfterStop()) + { + it->second.control->setChangingDirection(it->second.direction); + } + else + { + if(it->second.control->getValue() == 1) + { + it->second.control->setChangingDirection(Control::DECREASE); + } + else if(it->second.control->getValue() == 0) + { + it->second.control->setChangingDirection(Control::INCREASE); + } + } + } + } + else if(mDetectingBindingListener) + { + mDetectingBindingListener->mouseButtonBindingDetected(this, + mDetectingBindingControl, btn, mDetectingBindingDirection); + } + } + + return true; + } + + bool InputControlSystem::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID btn) + { + if(mActive) + { + ControlsButtonBinderMapType::const_iterator it = mControlsMouseButtonBinderMap.find((int)btn); + if(it != mControlsMouseButtonBinderMap.end()) + { + it->second.control->setChangingDirection(Control::STOP); + } + } + + return true; + } + + // mouse auto bindings + void DetectingBindingListener::mouseAxisBindingDetected(InputControlSystem* ICS, Control* control + , InputControlSystem::NamedAxis axis, Control::ControlChangingDirection direction) + { + // if the mouse axis is used by another control, remove it + ICS->removeMouseAxisBinding(axis); + + // if the control has an axis assigned, remove it + InputControlSystem::NamedAxis oldAxis = ICS->getMouseAxisBinding(control, direction); + if(oldAxis != InputControlSystem::UNASSIGNED) + { + ICS->removeMouseAxisBinding(oldAxis); + } + + ICS->addMouseAxisBinding(control, axis, direction); + ICS->cancelDetectingBindingState(); + } + + void DetectingBindingListener::mouseButtonBindingDetected(InputControlSystem* ICS, Control* control + , unsigned int button, Control::ControlChangingDirection direction) + { + // if the mouse button is used by another control, remove it + ICS->removeMouseButtonBinding(button); + + // if the control has a mouse button assigned, remove it + unsigned int oldButton = ICS->getMouseButtonBinding(control, direction); + if(oldButton != ICS_MAX_DEVICE_BUTTONS) + { + ICS->removeMouseButtonBinding(oldButton); + } + + ICS->addMouseButtonBinding(control, button, direction); + ICS->cancelDetectingBindingState(); + } + +} \ No newline at end of file diff --git a/extern/oics/ICSPrerequisites.cpp b/extern/oics/ICSPrerequisites.cpp new file mode 100644 index 000000000..2824950ed --- /dev/null +++ b/extern/oics/ICSPrerequisites.cpp @@ -0,0 +1,27 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +#include "ICSPrerequisites.h" diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h new file mode 100644 index 000000000..864dad15f --- /dev/null +++ b/extern/oics/ICSPrerequisites.h @@ -0,0 +1,111 @@ +/* ------------------------------------------------------- +Copyright (c) 2011 Alberto G. Salguero (alberto.salguero (at) uca.es) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------- */ + +//! @todo add mouse wheel support + +#ifndef _InputControlSystem_Prerequisites_H_ +#define _InputControlSystem_Prerequisites_H_ + +/// Include external headers +#include +#include +#include +#include +#include +#include + +#include "tinyxml.h" + +#define OIS_DYNAMIC_LIB +#include +#include +#include +#include +#include + +/// Define the dll export qualifier if compiling for Windows + +/* +#ifdef ICS_PLATFORM_WIN32 + #ifdef ICS_LIB + #define DllExport __declspec (dllexport) + #else + #define DllExport __declspec (dllimport) + #endif +#else + #define DllExport +#endif +*/ +#define DllExport + +// Define some macros +#define ICS_DEPRECATED __declspec(deprecated("Deprecated. It will be removed in future versions.")) + +/// Version defines +#define ICS_VERSION_MAJOR 0 +#define ICS_VERSION_MINOR 3 +#define ICS_VERSION_PATCH 1 + +#define ICS_MAX_DEVICE_BUTTONS 30 + +namespace ICS +{ + template + bool StringIsNumber ( const std::string &Text ) + { + std::stringstream ss(Text); + T result; + return ss >> result ? true : false; + } + + // from http://www.cplusplus.com/forum/articles/9645/ + template + std::string ToString ( T value ) + { + std::stringstream ss; + ss << value; + return ss.str(); + } + + // from http://www.cplusplus.com/forum/articles/9645/ + template + T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a + { //character array as argument + std::stringstream ss(Text); + T result; + return ss >> result ? result : 0; + } + + class InputControlSystem; + class Channel; + class ChannelListener; + class Control; + class ControlListener; + class DetectingBindingListener; + class InputControlSystemLog; +} + +#endif diff --git a/extern/oics/tinystr.cpp b/extern/oics/tinystr.cpp new file mode 100644 index 000000000..681250714 --- /dev/null +++ b/extern/oics/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/extern/oics/tinystr.h b/extern/oics/tinystr.h new file mode 100644 index 000000000..419e647e1 --- /dev/null +++ b/extern/oics/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/extern/oics/tinyxml.cpp b/extern/oics/tinyxml.cpp new file mode 100644 index 000000000..841a41cd3 --- /dev/null +++ b/extern/oics/tinyxml.cpp @@ -0,0 +1,1888 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/extern/oics/tinyxml.h b/extern/oics/tinyxml.h new file mode 100644 index 000000000..e69913b59 --- /dev/null +++ b/extern/oics/tinyxml.h @@ -0,0 +1,1802 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +//#define TIXML_USE_STL + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/mangle/.gitignore b/libs/mangle/.gitignore deleted file mode 100644 index cd24d7897..000000000 --- a/libs/mangle/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -upload_docs.sh -docs -*~ diff --git a/libs/mangle/Doxyfile b/libs/mangle/Doxyfile deleted file mode 100644 index f3e018002..000000000 --- a/libs/mangle/Doxyfile +++ /dev/null @@ -1,1510 +0,0 @@ -# Doxyfile 1.5.8 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = Mangle - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 1 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, -# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, -# Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = YES - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = sound stream vfs input - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.h - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */tests/* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = docs - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -#
Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to FRAME, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. Other possible values -# for this tag are: HIERARCHIES, which will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list; -# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which -# disables this behavior completely. For backwards compatibility with previous -# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE -# respectively. - -GENERATE_TREEVIEW = NONE - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = YES - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Options related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/libs/mangle/LICENSE.txt b/libs/mangle/LICENSE.txt deleted file mode 100644 index ccfcc9f22..000000000 --- a/libs/mangle/LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -Minimal Abstraction Game Layer (Mangle) is licensed under the -'zlib/libpng' license: - ----- - -Copyright (c) 2009 Nicolay Korslund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. - diff --git a/libs/mangle/README.txt b/libs/mangle/README.txt deleted file mode 100644 index f4849bebd..000000000 --- a/libs/mangle/README.txt +++ /dev/null @@ -1,129 +0,0 @@ -Welcome to Mangle v0.1 ----------------------- - -Written by: Nicolay Korslund (korslund@gmail.com) -License: zlib/png (see LICENSE.txt) -WWW: http://asm-soft.com/mangle/ -Documentation: http://asm-soft.com/mangle/docs - - - -Mangle is the project name for a small set of generic interfaces for -various game middleware libraries, such as sound, input, graphics, and -so on. You can imagine that it stands for "Minimal Abstraction Game -Layer", if you like. It will consist of several more or less -independent modules, one for each of these areas. These may be used -together to build an entire game engine, or they can be used -individually as separate libraries. - -However, Mangle does NOT actually implement a game engine, or any new -fundamental functionality. More on that below. - -Currently there's modules for sound and streams / archives (virtual -file systems.) More will come in the future (including input, 2D/3D -graphics, GUI, physics, and more.) - - -Main idea ---------- - -The idea behind Mangle is to provide a uniform, consistent interface -to other game libraries. The library does not provide ANY -functionality on its own. Instead it connects to a backend -implementation of your choice (or of your making.) - -The Sound module, for example, currently has backends for OpenAL -(output only), FFmpeg (input only) and for Audiere. Hopefully we'll -add IrrKlang, FMod, DirectSound, Miles and more in the future. It can -combine libraries to get more complete functionality (like using -OpenAL for output and FFmpeg to decode sound files), and it's also -easy to write your own backend if you're using a different (or -home-brewed) sound system. - -Regardless of what backend you use, the front-end interfaces (found -eg. in sound/output.h) is identical, and as a library user you -shouldn't notice much difference at all if you swap one backend for -another at a later point. It should Just Work. - -The interfaces themselves are also quite simple. Setting up a sound -stream from FFmpeg or other decoder into OpenAL can be quite hairy - -but with Mangle the hairy parts have already been written for you. You -just plug the parts together. - -The goal in the long run is to support a wide variety of game-related -libraries, and as many backend libraries (free and commercial) as -possible, so that you the user will have to write as little code as -possible. - - - -What is it good for -------------------- - -The main point of Mangle, as we said above, is that it connects to any -library of your choice "behind the scenes" but provides the same, -super-simple interface front-end for all of them. There can benefit -you in many ways: - -- If you want to use a new library that Mangle support. You don't have - to scour the net for tutorials and usage examples, since much of the - common usage code is already included in the implementation classes. - -- If you don't want to pollute your code with library-specific code. - The Mangle interfaces can help you keep your code clean, and its - user interface is often simpler than the exteral library one. - -- If you want to quickly connect different libraries together, it - really helps if they speak a common language. The Mangle interfaces - are exactly that - a common language between libraries. Do you need - Audiere to load sounds from a weird archive format only implemented - for PhysFS, all channeled through the OGRE resource system? No - problem! - -- If you are creating a library that depends on a specific feature - (such as sound), but you don't want to lock your users into any - specific sound library. Mangle works as an abstraction that lets - your users select their own implementation. - -- If you want to support multiple backends for your game/app, or want - to make it possible to easily switch backends later. You can select - backends at compile time or even at runtime. For example you might - want to switch to to a commercial sound library at a later stage in - development, or you may want to use a different input library on - console platforms than on PC. - -The Mangle implementations are extremely light-weight - often just one -or two cpp/h pairs per module. You can plug them directly into your -program, there's no separate library building step required. - -Since the library aims to be very modularly put together, you can -also, in many cases, just copy-and-paste the parts you need and ignore -the rest. Or modify stuff without fearing that the whole 'system' will -come crashing down, because there is no big 'system' to speak of. - - -Past and future ---------------- - -Mangle started out as (and still is) a spin-off from OpenMW, another -project I am personally working on ( http://openmw.com/ ). OpenMW is -an attempt to recreate the engine behind the commercial game -Morrowind, using only open source software. - -The projects are still tightly interlinked, and they will continue to -be until OpenMW is finished. Most near-future work on Mangle will be -focused chiefly on OpenMW at the moment. However I will gladly include -external contributions and suggestions that are not OpenMW-related if -someone sends them to me. - - -Conclusion ----------- - -As you might have guessed, Mangle is more a concept in development -than a finished library right now. - -All feedback, ideas, concepts, questions and code are very -welcome. Send them to: korslund@gmail.com - -I will put up a forum later as well if there's enough interest. diff --git a/libs/mangle/input/clients/ogre_input_capture.hpp b/libs/mangle/input/clients/ogre_input_capture.hpp deleted file mode 100644 index 2e77dc10b..000000000 --- a/libs/mangle/input/clients/ogre_input_capture.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MANGLE_INPUT_OGREINPUTFRAME_H -#define MANGLE_INPUT_OGREINPUTFRAME_H - -/* - This Ogre FrameListener calls capture() on an input driver every frame. - */ - -#include -#include "../driver.hpp" - -namespace Mangle { -namespace Input { - - struct OgreInputCapture : Ogre::FrameListener - { - Mangle::Input::Driver &driver; - - OgreInputCapture(Mangle::Input::Driver &drv) - : driver(drv) {} - - bool frameStarted(const Ogre::FrameEvent &evt) - { - driver.capture(); - return true; - } - }; -}} - -#endif diff --git a/libs/mangle/input/driver.hpp b/libs/mangle/input/driver.hpp deleted file mode 100644 index f4ba159c5..000000000 --- a/libs/mangle/input/driver.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MANGLE_INPUT_DRIVER_H -#define MANGLE_INPUT_DRIVER_H - -#include "event.hpp" - -namespace Mangle -{ - namespace Input - { - /** Input::Driver is the main interface to any input system that - handles keyboard and/or mouse input, along with any other - input source like joysticks. - - It is really a generalized event system, and could also be - used for non-input related events. The definition of the event - codes and structures are entirely dependent on the - implementation. - - A system-independent key code list will be found in keys.hpp, - and input drivers should privide optional translations to/from - this list for full compatibility. - */ - struct Driver - { - Driver() {} - virtual ~Driver() {} - - /** Captures input and produces the relevant events from it. An - event callback must be set with setEvent(), or all events - will be ignored. - */ - virtual void capture() = 0; - - /** Check the state of a given key or button. The key/button - definitions depends on the driver. - */ - virtual bool isDown(int index) = 0; - - /** Show or hide system mouse cursor - */ - virtual void showMouse(bool show) = 0; - - /** Set the event handler for input events. The evt->event() - function is called for each event. The meaning of the index - and *p parameters will be specific to each driver and to - each input system. - */ - void setEvent(EventPtr evt) - { event = evt; } - - /** Instigate an event. Is used internally for all events, but - can also be called from the outside to "fake" events from - this driver. - */ - void makeEvent(Event::Type type, int index, const void *p=NULL) - { - if(event) - event->event(type,index,p); - } - - private: - /// Holds the event callback set byt setEvent() - EventPtr event; - }; - - typedef boost::shared_ptr DriverPtr; - } -} -#endif diff --git a/libs/mangle/input/event.hpp b/libs/mangle/input/event.hpp deleted file mode 100644 index dc7b47088..000000000 --- a/libs/mangle/input/event.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef MANGLE_INPUT_EVENT_H -#define MANGLE_INPUT_EVENT_H - -#include "../tools/shared_ptr.hpp" - -namespace Mangle -{ - namespace Input - { - /** Generic callback for input events. The meaning of the - parameters depend on the system producing the events. - */ - struct Event - { - /// Event types - enum Type - { - EV_Unknown = 1, // Unknown event type - EV_KeyDown = 2, // Keyboard button was pressed - EV_KeyUp = 4, // Keyboard button was released - EV_Keyboard = 6, // All keyboard events - - EV_MouseMove = 8, // Mouse movement - EV_MouseDown = 16, // Mouse button pressed - EV_MouseUp = 32, // Mouse button released - EV_Mouse = 56, // All mouse events - - EV_ALL = 63 // All events - }; - - /** - Called upon all events. The first parameter give the event - type, the second gives additional data (usually the local - keysym or button index as defined by the driver), and the - pointer points to the full custom event structure provided by - the driver (the type may vary depending on the EventType, - this is defined in the Driver documentation.) - */ - virtual void event(Type type, int index, const void *p) = 0; - virtual ~Event() {} - }; - - typedef boost::shared_ptr EventPtr; - } -} -#endif diff --git a/libs/mangle/input/filters/eventlist.hpp b/libs/mangle/input/filters/eventlist.hpp deleted file mode 100644 index b3e2ff8f2..000000000 --- a/libs/mangle/input/filters/eventlist.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef MANGLE_INPUT_EVENTLIST_H -#define MANGLE_INPUT_EVENTLIST_H - -#include "../event.hpp" -#include - -namespace Mangle -{ - namespace Input - { - /** And Event handler that distributes each event to a list of - other handlers. Supports filtering events by their Type - parameter. - */ - struct EventList : Event - { - struct Filter - { - EventPtr evt; - int flags; - }; - std::vector list; - - void add(EventPtr e, int flags = EV_ALL) - { - Filter f; - f.evt = e; - f.flags = flags; - list.push_back(f); - } - - virtual void event(Type type, int index, const void *p) - { - std::vector::iterator it; - - for(it=list.begin(); it!=list.end(); it++) - { - if(type & it->flags) - it->evt->event(type,index,p); - } - } - }; - - typedef boost::shared_ptr EventListPtr; - } -} -#endif diff --git a/libs/mangle/input/servers/ois_driver.cpp b/libs/mangle/input/servers/ois_driver.cpp deleted file mode 100644 index 07ba3e83a..000000000 --- a/libs/mangle/input/servers/ois_driver.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "ois_driver.hpp" - -#include -#include -#include -#include - -#ifdef __APPLE_CC__ -#include -#endif - -using namespace Mangle::Input; -using namespace OIS; - -struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener -{ - OISDriver &drv; - - OISListener(OISDriver &driver) - : drv(driver) {} - - bool keyPressed( const OIS::KeyEvent &arg ) - { - drv.makeEvent(Event::EV_KeyDown, arg.key, &arg); - return true; - } - - bool keyReleased( const OIS::KeyEvent &arg ) - { - drv.makeEvent(Event::EV_KeyUp, arg.key, &arg); - return true; - } - - bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) - { - // Mouse button events are handled as key events - // TODO: Translate mouse buttons into pseudo-keysyms - drv.makeEvent(Event::EV_MouseDown, id, &arg); - return true; - } - - bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) - { - // TODO: ditto - drv.makeEvent(Event::EV_MouseUp, id, &arg); - return true; - } - - bool mouseMoved( const OIS::MouseEvent &arg ) - { - drv.makeEvent(Event::EV_MouseMove, -1, &arg); - return true; - } -}; - -OISDriver::OISDriver(Ogre::RenderWindow *window, bool exclusive) -{ - assert(window); - - size_t windowHnd; - - window->getCustomAttribute("WINDOW", &windowHnd); - - std::ostringstream windowHndStr; - ParamList pl; - - windowHndStr << windowHnd; - pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); - - // Set non-exclusive mouse and keyboard input if the user requested - // it. - if(!exclusive) - { -#if defined OIS_WIN32_PLATFORM - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_FOREGROUND" ))); - pl.insert(std::make_pair(std::string("w32_mouse"), - std::string("DISCL_NONEXCLUSIVE"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_FOREGROUND"))); - pl.insert(std::make_pair(std::string("w32_keyboard"), - std::string("DISCL_NONEXCLUSIVE"))); -#elif defined OIS_LINUX_PLATFORM - pl.insert(std::make_pair(std::string("x11_mouse_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_mouse_hide"), - std::string("false"))); - pl.insert(std::make_pair(std::string("x11_keyboard_grab"), - std::string("false"))); - pl.insert(std::make_pair(std::string("XAutoRepeatOn"), - std::string("true"))); -#endif - } - -#ifdef __APPLE_CC__ - // Give the application window focus to receive input events - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - SetFrontProcess(&psn); -#endif - - inputMgr = InputManager::createInputSystem( pl ); - - // Create all devices - keyboard = static_cast(inputMgr->createInputObject - ( OISKeyboard, true )); - mouse = static_cast(inputMgr->createInputObject - ( OISMouse, true )); - - // Set mouse region - const MouseState &ms = mouse->getMouseState(); - ms.width = window->getWidth(); - ms.height = window->getHeight(); - - // Set up the input listener - listener = new OISListener(*this); - keyboard-> setEventCallback(listener); - mouse-> setEventCallback(listener); -} - -OISDriver::~OISDriver() -{ - // Delete the listener object - delete listener; - - if(inputMgr == NULL) return; - - // Kill the input systems. This will reset input options such as key - // repeat rate. - inputMgr->destroyInputObject(keyboard); - inputMgr->destroyInputObject(mouse); - InputManager::destroyInputSystem(inputMgr); - inputMgr = NULL; -} - -void OISDriver::capture() -{ - // Capture keyboard and mouse events - keyboard->capture(); - mouse->capture(); -} - -bool OISDriver::isDown(int index) -{ - // TODO: Extend to mouse buttons as well - return keyboard->isKeyDown((OIS::KeyCode)index); -} - -void OISDriver::adjustMouseClippingSize(int width, int height) -{ - const OIS::MouseState &ms = mouse->getMouseState(); - ms.width = width; - ms.height = height; -} diff --git a/libs/mangle/input/servers/ois_driver.hpp b/libs/mangle/input/servers/ois_driver.hpp deleted file mode 100644 index 81633542f..000000000 --- a/libs/mangle/input/servers/ois_driver.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef MANGLE_INPUT_OIS_DRIVER_H -#define MANGLE_INPUT_OIS_DRIVER_H - -#include "../driver.hpp" - -namespace OIS -{ - class InputManager; - class Mouse; - class Keyboard; -} - -namespace Ogre -{ - class RenderWindow; -} - -namespace Mangle -{ - namespace Input - { - struct OISListener; - - /** Input driver for OIS, the input manager typically used with - Ogre. - */ - struct OISDriver : Driver - { - /// If exclusive=true, then we capture mouse and keyboard from - /// the OS. - OISDriver(Ogre::RenderWindow *window, bool exclusive=true); - ~OISDriver(); - - void adjustMouseClippingSize(int width, int height); - - void capture(); - bool isDown(int index); - /// Not currently supported. - void showMouse(bool) {} - - private: - OIS::InputManager *inputMgr; - OIS::Mouse *mouse; - OIS::Keyboard *keyboard; - - OISListener *listener; - }; - } -} -#endif diff --git a/libs/mangle/input/servers/sdl_driver.cpp b/libs/mangle/input/servers/sdl_driver.cpp deleted file mode 100644 index 93884a6e6..000000000 --- a/libs/mangle/input/servers/sdl_driver.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "sdl_driver.hpp" - -#include - -using namespace Mangle::Input; - -void SDLDriver::capture() -{ - // Poll for events - SDL_Event evt; - while(SDL_PollEvent(&evt)) - { - Event::Type type = Event::EV_Unknown; - int index = -1; - - switch(evt.type) - { - // For key events, send the keysym as the index. - case SDL_KEYDOWN: - type = Event::EV_KeyDown; - index = evt.key.keysym.sym; - break; - case SDL_KEYUP: - type = Event::EV_KeyUp; - index = evt.key.keysym.sym; - break; - case SDL_MOUSEMOTION: - type = Event::EV_MouseMove; - break; - // Add more event types later - } - - // Pass the event along, using -1 as index for unidentified - // event types. - makeEvent(type, index, &evt); - } -} - -bool SDLDriver::isDown(int index) -{ - int num; - Uint8 *keys = SDL_GetKeyState(&num); - assert(index >= 0 && index < num); - - // The returned array from GetKeyState is indexed by the - // SDLK_KEYNAME enums and is just a list of bools. If the indexed - // value is true, the button is down. - return keys[index]; -} - -void SDLDriver::showMouse(bool show) -{ - SDL_ShowCursor(show?SDL_ENABLE:SDL_DISABLE); -} diff --git a/libs/mangle/input/servers/sdl_driver.hpp b/libs/mangle/input/servers/sdl_driver.hpp deleted file mode 100644 index b71346cba..000000000 --- a/libs/mangle/input/servers/sdl_driver.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MANGLE_INPUT_SDL_DRIVER_H -#define MANGLE_INPUT_SDL_DRIVER_H - -#include "../driver.hpp" - -namespace Mangle -{ - namespace Input - { - /** Input driver for SDL. As the input system of SDL is seldomly - used alone (most often along with the video system), it is - assumed that you do your own initialization and cleanup of SDL - before and after using this driver. - - The Event.event() calls will be given the proper EV_ type, the - key index (for key up/down events), and a pointer to the full - SDL_Event structure. - */ - struct SDLDriver : Driver - { - void capture(); - bool isDown(int index); - void showMouse(bool); - }; - } -} -#endif diff --git a/libs/mangle/input/tests/.gitignore b/libs/mangle/input/tests/.gitignore deleted file mode 100644 index 460c76f00..000000000 --- a/libs/mangle/input/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*_test -ogre.cfg diff --git a/libs/mangle/input/tests/Makefile b/libs/mangle/input/tests/Makefile deleted file mode 100644 index 8760adfe7..000000000 --- a/libs/mangle/input/tests/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -GCC=g++ -Wall - -all: sdl_driver_test ois_driver_test evtlist_test - -sdl_driver_test: sdl_driver_test.cpp - $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL - -ois_driver_test: ois_driver_test.cpp - $(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem - -evtlist_test: evtlist_test.cpp ../filters/eventlist.hpp ../event.hpp - $(GCC) $< -o $@ - -clean: - rm *_test diff --git a/libs/mangle/input/tests/common.cpp b/libs/mangle/input/tests/common.cpp deleted file mode 100644 index 0c7c76466..000000000 --- a/libs/mangle/input/tests/common.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include "../driver.hpp" -#include -using namespace std; -using namespace Mangle::Input; - -Driver *input; - -struct MyCB : Event -{ - void event(Event::Type type, int i, const void *p) - { - cout << "got event: type=" << type << " index=" << i << endl; - } -}; - -void mainLoop(int argc, int quitKey) -{ - cout << "Hold the Q key to quit:\n"; - input->setEvent(EventPtr(new MyCB)); - while(!input->isDown(quitKey)) - { - input->capture(); - usleep(20000); - - if(argc == 1) - { - cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; - break; - } - } - - delete input; - cout << "\nBye bye!\n"; -} diff --git a/libs/mangle/input/tests/evtlist_test.cpp b/libs/mangle/input/tests/evtlist_test.cpp deleted file mode 100644 index fbd980cbd..000000000 --- a/libs/mangle/input/tests/evtlist_test.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include "../filters/eventlist.hpp" - -using namespace std; -using namespace Mangle::Input; - -struct MyEvent : Event -{ - int ii; - MyEvent(int i) : ii(i) {} - - void event(Event::Type type, int i, const void *p) - { - cout << " #" << ii << " got event: type=" << type << " index=" << i << endl; - } -}; - -EventList lst; - -int iii=1; -void make(int flags) -{ - lst.add(EventPtr(new MyEvent(iii++)), flags); -} - -void send(Event::Type type) -{ - cout << "Sending type " << type << endl; - lst.event(type,0,NULL); -} - -int main() -{ - make(Event::EV_ALL); - make(Event::EV_KeyDown); - make(Event::EV_KeyUp | Event::EV_MouseDown); - - send(Event::EV_Unknown); - send(Event::EV_KeyDown); - send(Event::EV_KeyUp); - send(Event::EV_MouseDown); - - cout << "Enough of that\n"; - return 0; -} diff --git a/libs/mangle/input/tests/ois_driver_test.cpp b/libs/mangle/input/tests/ois_driver_test.cpp deleted file mode 100644 index 386f24055..000000000 --- a/libs/mangle/input/tests/ois_driver_test.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "common.cpp" - -#include "../servers/ois_driver.hpp" -#include -#include -#include - -bool isFile(const char *name) -{ - boost::filesystem::path cfg_file_path(name); - return boost::filesystem::exists(cfg_file_path); -} - -using namespace Ogre; -using namespace OIS; - -Root *root; -RenderWindow *window; - -void setupOgre() -{ - // Disable logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - bool useConfig = isFile("ogre.cfg"); - - // Set up Root - root = new Root("plugins.cfg", "ogre.cfg", ""); - - // Configure - if(!useConfig) - root->showConfigDialog(); - else - root->restoreConfig(); - - // Initialize OGRE window - window = root->initialise(true, "test", ""); -} - -int main(int argc, char** argv) -{ - setupOgre(); - input = new OISDriver(window); - - mainLoop(argc, KC_Q); - - delete root; - return 0; -} diff --git a/libs/mangle/input/tests/output/evtlist_test.out b/libs/mangle/input/tests/output/evtlist_test.out deleted file mode 100644 index 180dcc58a..000000000 --- a/libs/mangle/input/tests/output/evtlist_test.out +++ /dev/null @@ -1,12 +0,0 @@ -Sending type 1 - #1 got event: type=1 index=0 -Sending type 2 - #1 got event: type=2 index=0 - #2 got event: type=2 index=0 -Sending type 4 - #1 got event: type=4 index=0 - #3 got event: type=4 index=0 -Sending type 16 - #1 got event: type=16 index=0 - #3 got event: type=16 index=0 -Enough of that diff --git a/libs/mangle/input/tests/output/ois_driver_test.out b/libs/mangle/input/tests/output/ois_driver_test.out deleted file mode 100644 index 7d273fd46..000000000 --- a/libs/mangle/input/tests/output/ois_driver_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Hold the Q key to quit: -got event: type=8 index=-1 -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/mangle/input/tests/output/sdl_driver_test.out b/libs/mangle/input/tests/output/sdl_driver_test.out deleted file mode 100644 index 2df2e4014..000000000 --- a/libs/mangle/input/tests/output/sdl_driver_test.out +++ /dev/null @@ -1,5 +0,0 @@ -Hold the Q key to quit: -got event: type=1 index=-1 -You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly - -Bye bye! diff --git a/libs/mangle/input/tests/plugins.cfg b/libs/mangle/input/tests/plugins.cfg deleted file mode 100644 index 57ec54e1a..000000000 --- a/libs/mangle/input/tests/plugins.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=/usr/local/lib/OGRE/ - -# Define plugins -Plugin=RenderSystem_GL -Plugin=Plugin_ParticleFX -Plugin=Plugin_OctreeSceneManager -# Plugin=Plugin_CgProgramManager - - diff --git a/libs/mangle/input/tests/sdl_driver_test.cpp b/libs/mangle/input/tests/sdl_driver_test.cpp deleted file mode 100644 index 5db6dbba8..000000000 --- a/libs/mangle/input/tests/sdl_driver_test.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "common.cpp" - -#include "../servers/sdl_driver.hpp" -#include - -int main(int argc, char** argv) -{ - SDL_Init(SDL_INIT_VIDEO); - SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); - input = new SDLDriver(); - - mainLoop(argc, SDLK_q); - - SDL_Quit(); - return 0; -} diff --git a/libs/mangle/input/tests/test.sh b/libs/mangle/input/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/libs/mangle/input/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/rend2d/driver.hpp b/libs/mangle/rend2d/driver.hpp deleted file mode 100644 index 08a15b0ae..000000000 --- a/libs/mangle/rend2d/driver.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef MANGLE_REND2D_DRIVER_H -#define MANGLE_REND2D_DRIVER_H - -#include -#include "sprite.hpp" - -namespace Mangle -{ - namespace Rend2D - { - /** - The driver is the connection to the backend system that powers - 2D sprite rendering. For example the backend could be SDL or - any other 2D-capable graphics library. - */ - struct Driver - { - /// Get the screen sprite - virtual Sprite *getScreen() = 0; - - /// Sets the video mode. - virtual void setVideoMode(int width, int height, int bpp=32, bool fullscreen=false) = 0; - - /** Update the screen. Until this function is called, none of - the changes written to the screen sprite will be visible. - */ - virtual void update() = 0; - - /// Set the window title, as well as the title of the window - /// when "iconified" - virtual void setWindowTitle(const std::string &title, - const std::string &icon) = 0; - - /// Set the window title - void setWindowTitle(const std::string &title) { setWindowTitle(title,title); } - - /// Load sprite from an image file. Thows an exception on - /// failure. - virtual Sprite* loadImage(const std::string &file) = 0; - - /// Load a sprite from an image file stored in memory. Throws - /// exception on failure. - virtual Sprite* loadImage(const void* data, size_t size) = 0; - - /** @brief Set gamma value for all colors. - - Note: Setting this in windowed mode will affect the ENTIRE - SCREEN! - */ - virtual void setGamma(float gamma) = 0; - - /// Set gamma individually for red, green, blue - virtual void setGamma(float red, float green, float blue) = 0; - - /// Get screen width - virtual int width() = 0; - - /// Get screen height - virtual int height() = 0; - }; - } -} -#endif diff --git a/libs/mangle/rend2d/servers/sdl_driver.cpp b/libs/mangle/rend2d/servers/sdl_driver.cpp deleted file mode 100644 index 84a17933f..000000000 --- a/libs/mangle/rend2d/servers/sdl_driver.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "sdl_driver.hpp" - -#include -#include -#include -#include - -using namespace Mangle::Rend2D; - -const SpriteData *SDL_Sprite::lock() -{ - // Make sure we aren't already locked - assert(!data.pixels); - - // Lock the surface and set up the data structure - SDL_LockSurface(surface); - - data.pixels = surface->pixels; - data.w = surface->w; - data.h = surface->h; - data.pitch = surface->pitch; - data.bypp = surface->format->BytesPerPixel; - - return &data; -} - -void SDL_Sprite::unlock() -{ - if(data.pixels) - { - SDL_UnlockSurface(surface); - data.pixels = NULL; - } -} - -// This is a really crappy and slow implementation, only intended for -// testing purposes. Use lock/unlock for faster pixel drawing. -void SDL_Sprite::pixel(int x, int y, int color) -{ - SDL_LockSurface(surface); - - int bpp = surface->format->BytesPerPixel; - char *p = (char*)surface->pixels + y*surface->pitch + x*bpp; - - switch(bpp) - { - case 1: *p = color; break; - case 3: - if(SDL_BYTEORDER == SDL_BIG_ENDIAN) - { - p[0] = (color >> 16) & 0xff; - p[1] = (color >> 8) & 0xff; - p[2] = color & 0xff; - } - else - { - p[0] = color & 0xff; - p[1] = (color >> 8) & 0xff; - p[2] = (color >> 16) & 0xff; - } - break; - case 4: - *(int*)p = color; - break; - } - SDL_UnlockSurface(surface); -} - -void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite - int x, int y, // Destination position - int sx, int sy, // Source position - int w, int h // Amount to draw. -1 means remainder. - ) -{ - // Get source surface - SDL_Sprite *other = dynamic_cast(s); - assert(other != NULL); - SDL_Surface *img = other->getSurface(); - - // Check coordinate validity - assert(sx <= img->w && sy <= img->h); - assert(x <= surface->w && y <= surface->h); - assert(sx >= 0 && sy >= 0); - - // Compute width and height if necessary - if(w == -1) w = img->w - sx; - if(h == -1) h = img->h - sy; - - // Check them if they're valid - assert(w >= 0 && w <= img->w); - assert(h >= 0 && h <= img->h); - - SDL_Rect dest; - dest.x = x; - dest.y = y; - dest.w = w; - dest.h = h; - - SDL_Rect src; - src.x = sx; - src.y = sy; - src.w = w; - src.h = h; - - // Do the Blitman - SDL_BlitSurface(img, &src, surface, &dest); -} - -SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete) - : surface(s), autoDel(autoDelete) -{ - assert(surface != NULL); - data.pixels = NULL; -} - -SDL_Sprite::~SDL_Sprite() -{ - if(autoDel) - SDL_FreeSurface(surface); -} - -void SDL_Sprite::fill(int value) -{ - SDL_FillRect(surface, NULL, value); -} - -int SDL_Sprite::width() { return surface->w; } -int SDL_Sprite::height() { return surface->h; } - -SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false) -{ - if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw std::runtime_error("Error initializing SDL video"); -} -SDLDriver::~SDLDriver() -{ - if(display) delete display; - SDL_Quit(); -} - -void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) -{ - unsigned int flags; - - if(display) delete display; - - if (fullscreen) - // Assume fullscreen mode allows a double-bufferd hardware - // mode. We need more test code for this to be safe though. - flags = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; - else - flags = SDL_SWSURFACE; - - // Create the surface and check it - realDisp = SDL_SetVideoMode(width, height, bpp, flags); - if(realDisp == NULL) - throw std::runtime_error("Failed setting SDL video mode"); - - // Code for software double buffering. I haven't found this to be - // any speed advantage at all in windowed mode (it's slower, as one - // would expect.) Not properly tested in fullscreen mode with - // hardware buffers, but it will probably only be an improvement if - // we do excessive writing (ie. write each pixel on average more - // than once) or try to read from the display buffer. - if(softDouble) - { - // Make a new surface with the same attributes as the real - // display surface. - SDL_Surface *back = SDL_DisplayFormat(realDisp); - assert(back != NULL); - - // Create a sprite representing the double buffer - display = new SDL_Sprite(back); - } - else - { - // Create a sprite directly representing the display surface. - // The 'false' parameter means do not autodelete the screen - // surface upon exit (since SDL manages it) - display = new SDL_Sprite(realDisp, false); - } -} - -/// Update the screen -void SDLDriver::update() -{ - // Blit the soft double buffer onto the real display buffer - if(softDouble) - SDL_BlitSurface(display->getSurface(), NULL, realDisp, NULL ); - - if(realDisp) - SDL_Flip(realDisp); -} - -/// Set the window title, as well as the title of the window when -/// "iconified" -void SDLDriver::setWindowTitle(const std::string &title, - const std::string &icon) -{ - SDL_WM_SetCaption( title.c_str(), icon.c_str() ); -} - -// Convert the given surface to display format. -static SDL_Surface* convertImage(SDL_Surface* surf) -{ - if(surf != NULL) - { - // Convert the image to the display buffer format, for faster - // blitting - SDL_Surface *surf2 = SDL_DisplayFormat(surf); - SDL_FreeSurface(surf); - surf = surf2; - } - return surf; -} - -/// Load sprite from an image file, using SDL_image. -Sprite* SDLDriver::loadImage(const std::string &file) -{ - SDL_Surface *surf = IMG_Load(file.c_str()); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image file '" + file + "'"); - return spriteFromSDL(surf); -} - -/// Load sprite from an SDL_RWops structure. autoFree determines -/// whether the RWops struct should be closed/freed after use. -Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree) -{ - SDL_Surface *surf = IMG_Load_RW(src, autoFree); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image"); - return spriteFromSDL(surf); -} - -/// Load a sprite from an image file stored in memory. Uses -/// SDL_image. -Sprite* SDLDriver::loadImage(const void* data, size_t size) -{ - SDL_RWops *rw = SDL_RWFromConstMem(data, size); - return loadImage(rw, true); -} - -void SDLDriver::setGamma(float red, float green, float blue) -{ - SDL_SetGamma(red,green,blue); -} - -/// Convert an existing SDL surface into a sprite -Sprite* SDLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) -{ - assert(surf); - return new SDL_Sprite(surf, autoFree); -} - -void SDLDriver::sleep(int ms) { SDL_Delay(ms); } -unsigned int SDLDriver::ticks() { return SDL_GetTicks(); } diff --git a/libs/mangle/rend2d/servers/sdl_driver.hpp b/libs/mangle/rend2d/servers/sdl_driver.hpp deleted file mode 100644 index 0f205ba34..000000000 --- a/libs/mangle/rend2d/servers/sdl_driver.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef MANGLE_DRAW2D_SDL_H -#define MANGLE_DRAW2D_SDL_H - -#include "../driver.hpp" - -// Predeclarations keep the streets safe at night -struct SDL_Surface; -struct SDL_RWops; - -namespace Mangle -{ - namespace Rend2D - { - /// SDL-implementation of Sprite - struct SDL_Sprite : Sprite - { - /** Draw a sprite in the given position. Can only draw other SDL - sprites. - */ - void draw(Sprite *s, // Must be SDL_Sprite - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ); - - SDL_Sprite(SDL_Surface *s, bool autoDelete=true); - ~SDL_Sprite(); - - // Information retrieval - int width(); - int height(); - SDL_Surface *getSurface() { return surface; } - - // Fill with a given pixel value - void fill(int value); - - // Set one pixel - void pixel(int x, int y, int value); - - const SpriteData *lock(); - void unlock(); - - private: - // The SDL surface - SDL_Surface* surface; - - // Used for locking - SpriteData data; - - // If true, delete this surface when the canvas is destructed - bool autoDel; - }; - - class SDLDriver : public Driver - { - // The main display surface - SDL_Sprite *display; - - // The actual display surface. May or may not be the same - // surface pointed to by 'display' above, depending on the - // softDouble flag. - SDL_Surface *realDisp; - - // If true, we do software double buffering. - bool softDouble; - - public: - SDLDriver(); - ~SDLDriver(); - - /// Sets the video mode. Will create the window if it is not - /// already set up. Note that for SDL, bpp=0 means use current - /// bpp. - void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); - - /// Update the screen - void update(); - - /// Set the window title, as well as the title of the window - /// when "iconified" - void setWindowTitle(const std::string &title, - const std::string &icon); - - // Include overloads from our Glorious parent - using Driver::setWindowTitle; - - /// Load sprite from an image file, using SDL_image. - Sprite* loadImage(const std::string &file); - - /// Load sprite from an SDL_RWops structure. autoFree determines - /// whether the RWops struct should be closed/freed after use. - Sprite* loadImage(SDL_RWops *src, bool autoFree=false); - - /// Load a sprite from an image file stored in memory. Uses - /// SDL_image. - Sprite* loadImage(const void* data, size_t size); - - /// Set gamma value - void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } - - /// Set gamma individually for red, green, blue - void setGamma(float red, float green, float blue); - - /// Convert an existing SDL surface into a sprite - Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); - - // Get width and height - int width() { return display ? display->width() : 0; } - int height() { return display ? display->height() : 0; } - - /// Get the screen sprite - Sprite *getScreen() { return display; } - - /// Not really a graphic-related function, but very - /// handly. Sleeps the given number of milliseconds using - /// SDL_Delay(). - void sleep(int ms); - - /// Get the number of ticks since SDL initialization, using - /// SDL_GetTicks(). - unsigned int ticks(); - }; - } -} -#endif diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp b/libs/mangle/rend2d/servers/sdl_gl_driver.cpp deleted file mode 100644 index db519e091..000000000 --- a/libs/mangle/rend2d/servers/sdl_gl_driver.cpp +++ /dev/null @@ -1,311 +0,0 @@ -#include "sdl_gl_driver.hpp" - -#include -#include -#include -#include -#include - -using namespace Mangle::Rend2D; - -void SDLGL_Sprite::draw(Sprite *s, // Must be SDLGL_Sprite - int x, int y, // Destination position - int sx, int sy, // Source position - int w, int h // Amount to draw. -1 means remainder. - ) -{ - // Get source surface - SDLGL_Sprite *other = dynamic_cast(s); - assert(other != NULL); - SDL_Surface *img = other->getSurface(); - - // Check coordinate validity - assert(sx <= img->w && sy <= img->h); - assert(x <= surface->w && y <= surface->h); - assert(sx >= 0 && sy >= 0); - - // Compute width and height if necessary - if(w == -1) w = img->w - sx; - if(h == -1) h = img->h - sy; - - // Check them if they're valid - assert(w >= 0 && w <= img->w); - assert(h >= 0 && h <= img->h); - - SDL_Rect dest; - dest.x = x; - dest.y = y; - dest.w = w; - dest.h = h; - - SDL_Rect src; - src.x = sx; - src.y = sy; - src.w = w; - src.h = h; - - // Do the Blitman - SDL_BlitSurface(img, &src, surface, &dest); -} - -SDLGL_Sprite::SDLGL_Sprite(SDL_Surface *s, bool autoDelete) - : surface(s), autoDel(autoDelete) -{ - assert(surface != NULL); -} - -SDLGL_Sprite::~SDLGL_Sprite() -{ - if(autoDel) - SDL_FreeSurface(surface); -} - -void SDLGL_Sprite::fill(int value) -{ - SDL_FillRect(surface, NULL, value); -} - -int SDLGL_Sprite::width() { return surface->w; } -int SDLGL_Sprite::height() { return surface->h; } - -SDLGLDriver::SDLGLDriver() : display(NULL), realDisp(NULL) -{ - if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw std::runtime_error("Error initializing SDL video"); -} -SDLGLDriver::~SDLGLDriver() -{ - if(display) delete display; - SDL_Quit(); -} - -// Surface used for the screen. Since OpenGL surfaces must have sizes -// that are powers of 2, we have to "fake" the returned display size -// to match the screen, not the surface itself. If we don't use this, -// the client program will get confused about the actual size of our -// screen, thinking it is bigger than it is. -struct FakeSizeSprite : SDLGL_Sprite -{ - int fakeW, fakeH; - - FakeSizeSprite(SDL_Surface *s, int fw, int fh) - : SDLGL_Sprite(s), fakeW(fw), fakeH(fh) - {} - - int width() { return fakeW; } - int height() { return fakeH; } -}; - -static int makePow2(int num) -{ - assert(num); - if((num & (num-1)) != 0) - { - int cnt = 0; - while(num) - { - num >>= 1; - cnt++; - } - num = 1 << cnt; - } - return num; -} - -void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) -{ - unsigned int flags; - - if(display) delete display; - - flags = SDL_OPENGL; - - if (fullscreen) - flags |= SDL_FULLSCREEN; - - SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); - - // Create the surface and check it - screen = SDL_SetVideoMode(width, height, bpp, flags); - if(screen == NULL) - throw std::runtime_error("Failed setting SDL video mode"); - - // Expand width and height to be powers of 2 - int width2 = makePow2(width); - int height2 = makePow2(height); - - // Create a new SDL surface of this size - const SDL_PixelFormat& fmt = *(screen->format); - realDisp = SDL_CreateRGBSurface(SDL_SWSURFACE,width2,height2, - fmt.BitsPerPixel, - fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask); - - // Create a sprite directly representing the display surface. This - // allows the user to blit to it directly. - display = new FakeSizeSprite(realDisp, width, height); - - // Set up the OpenGL format - nOfColors = fmt.BytesPerPixel; - - if(nOfColors == 4) - { - if (fmt.Rmask == 0x000000ff) - texture_format = GL_RGBA; - else - texture_format = GL_BGRA; - } - else if(nOfColors == 3) - { - if (fmt.Rmask == 0x000000ff) - texture_format = GL_RGB; - else - texture_format = GL_BGR; - } - else - assert(0 && "unsupported screen format"); - - glEnable(GL_TEXTURE_2D); - - // Have OpenGL generate a texture object handle for us - glGenTextures( 1, &texture ); - - // Bind the texture object - glBindTexture( GL_TEXTURE_2D, texture ); - - // Set the texture's stretching properties - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); -} - -void SDLGLDriver::updateNoSwap() -{ - if(!realDisp) return; - - // Fist, set up the screen texture: - - // Bind the texture object - glBindTexture( GL_TEXTURE_2D, texture ); - - // Edit the texture object's image data - glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, realDisp->w, realDisp->h, 0, - texture_format, GL_UNSIGNED_BYTE, realDisp->pixels ); - - glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); - - // OpenGL barf. Set up the projection to match our screen - int vPort[4]; - glGetIntegerv(GL_VIEWPORT, vPort); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, vPort[2], 0, vPort[3], -1, 1); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glBegin( GL_QUADS ); - - // Needed to move the screen into the right place - int diff = screen->h - realDisp->h; - - // Bottom-left vertex (corner) - glTexCoord2i( 0, 1 ); - glVertex3f(0,diff,0); - - // Bottom-right vertex (corner) - glTexCoord2i( 1, 1 ); - glVertex3f( realDisp->w, diff, 0.f ); - - // Top-right vertex (corner) - glTexCoord2i( 1, 0 ); - glVertex3f( realDisp->w, screen->h, 0.f ); - - // Top-left vertex (corner) - glTexCoord2i( 0, 0 ); - glVertex3f( 0, screen->h, 0.f ); - glEnd(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); -} - -void SDLGLDriver::swap() -{ - SDL_GL_SwapBuffers(); -} - -void SDLGLDriver::update() -{ - updateNoSwap(); - swap(); -} - -/// Set the window title, as well as the title of the window when -/// "iconified" -void SDLGLDriver::setWindowTitle(const std::string &title, - const std::string &icon) -{ - SDL_WM_SetCaption( title.c_str(), icon.c_str() ); -} - -// Convert the given surface to display format. -static SDL_Surface* convertImage(SDL_Surface* surf) -{ - if(surf != NULL) - { - // Convert the image to the display buffer format, for faster - // blitting - SDL_Surface *surf2 = SDL_DisplayFormat(surf); - SDL_FreeSurface(surf); - surf = surf2; - } - return surf; -} - -/// Load sprite from an image file, using SDL_image. -Sprite* SDLGLDriver::loadImage(const std::string &file) -{ - SDL_Surface *surf = IMG_Load(file.c_str()); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image file '" + file + "'"); - return spriteFromSDL(surf); -} - -/// Load sprite from an SDL_RWops structure. autoFree determines -/// whether the RWops struct should be closed/freed after use. -Sprite* SDLGLDriver::loadImage(SDL_RWops *src, bool autoFree) -{ - SDL_Surface *surf = IMG_Load_RW(src, autoFree); - surf = convertImage(surf); - if(surf == NULL) - throw std::runtime_error("SDL failed to load image"); - return spriteFromSDL(surf); -} - -/// Load a sprite from an image file stored in memory. Uses -/// SDL_image. -Sprite* SDLGLDriver::loadImage(const void* data, size_t size) -{ - SDL_RWops *rw = SDL_RWFromConstMem(data, size); - return loadImage(rw, true); -} - -void SDLGLDriver::setGamma(float red, float green, float blue) -{ - SDL_SetGamma(red,green,blue); -} - -/// Convert an existing SDL surface into a sprite -Sprite* SDLGLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) -{ - assert(surf); - return new SDLGL_Sprite(surf, autoFree); -} - -void SDLGLDriver::sleep(int ms) { SDL_Delay(ms); } -unsigned int SDLGLDriver::ticks() { return SDL_GetTicks(); } diff --git a/libs/mangle/rend2d/servers/sdl_gl_driver.hpp b/libs/mangle/rend2d/servers/sdl_gl_driver.hpp deleted file mode 100644 index d116e3659..000000000 --- a/libs/mangle/rend2d/servers/sdl_gl_driver.hpp +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef MANGLE_DRAW2D_SDLGL_H -#define MANGLE_DRAW2D_SDLGL_H - -/** This driver is similar to SDLDriver, except that it uses SDL on - top of OpenGL. - - I've decided to make it a separate file instead of just adding - optional OpenGL support to the original, so that pure SDL users - don't have to add OpenGL as a dependency. - */ - -#include "../driver.hpp" - -// Predeclarations keep the streets safe at night -struct SDL_Surface; -struct SDL_RWops; - -namespace Mangle -{ - namespace Rend2D - { - /// SDL-implementation of Sprite - struct SDLGL_Sprite : Sprite - { - /** Draw a sprite in the given position. Can only draw other SDL - sprites. - */ - void draw(Sprite *s, // Must be SDLGL_Sprite - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ); - - SDLGL_Sprite(SDL_Surface *s, bool autoDelete=true); - ~SDLGL_Sprite(); - - // Information retrieval - virtual int width(); - virtual int height(); - SDL_Surface *getSurface() { return surface; } - - // Fill with a given pixel value - void fill(int value); - - private: - // The SDL surface - SDL_Surface* surface; - - // If true, delete this surface when the canvas is destructed - bool autoDel; - }; - - class SDLGLDriver : public Driver - { - // The main display surface - SDLGL_Sprite *display; - - // The screen surface. This is completely unused. - SDL_Surface *screen; - - // The display surface and main GL texture. These are used when - // drawing the entire screen as one surface, as a drop-in - // replacement for SDLDriver. - SDL_Surface *realDisp; - unsigned int texture; - int nOfColors, texture_format; - - public: - SDLGLDriver(); - ~SDLGLDriver(); - - /// Sets the video mode. Will create the window if it is not - /// already set up. Note that for SDL, bpp=0 means use current - /// bpp. - void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); - - /// Update the screen - void update(); - - /// Calls SDL_GL_SwapBuffers - void swap(); - - /// Draw surface to screen but do not call SDL_GL_SwapBuffers() - void updateNoSwap(); - - /// Set the window title, as well as the title of the window - /// when "iconified" - void setWindowTitle(const std::string &title, - const std::string &icon); - - // Include overloads from our Glorious parent - using Driver::setWindowTitle; - - /// Load sprite from an image file, using SDL_image. - Sprite* loadImage(const std::string &file); - - /// Load sprite from an SDL_RWops structure. autoFree determines - /// whether the RWops struct should be closed/freed after use. - Sprite* loadImage(SDL_RWops *src, bool autoFree=false); - - /// Load a sprite from an image file stored in memory. Uses - /// SDL_image. - Sprite* loadImage(const void* data, size_t size); - - /// Set gamma value - void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } - - /// Set gamma individually for red, green, blue - void setGamma(float red, float green, float blue); - - /// Convert an existing SDL surface into a sprite - Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); - - // Get width and height - int width() { return display ? display->width() : 0; } - int height() { return display ? display->height() : 0; } - - /// Get the screen sprite - Sprite *getScreen() { return display; } - - /// Not really a graphic-related function, but very - /// handly. Sleeps the given number of milliseconds using - /// SDL_Delay(). - void sleep(int ms); - - /// Get the number of ticks since SDL initialization, using - /// SDL_GetTicks(). - unsigned int ticks(); - }; - } -} -#endif diff --git a/libs/mangle/rend2d/sprite.hpp b/libs/mangle/rend2d/sprite.hpp deleted file mode 100644 index f49da6cb6..000000000 --- a/libs/mangle/rend2d/sprite.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef MANGLE_REND2D_SPRITE_H -#define MANGLE_REND2D_SPRITE_H - -namespace Mangle -{ - namespace Rend2D - { - /** - A pointer to sprite data for direct drawing. Only to be used - while the corresponding sprite is locked. - */ - struct SpriteData - { - void *pixels; // Pixel data - int w, h; // Width and height - int pitch, bypp; // Pitch (bytes) and bytes per pixel - }; - - /** - A Sprite is either a bitmap to be drawn or an output of area - for blitting other bitmaps, or both. They are created by the - Driver. - */ - struct Sprite - { - /// Draw a sprite in the given position - virtual void draw(Sprite *s, // The sprite to draw - int x, int y, // Destination position - int sx=0, int sy=0, // Source position - int w=-1, int h=-1 // Amount to draw. -1 means remainder. - ) = 0; - - virtual ~Sprite() {} - - // Information retrieval - virtual int width() = 0; - virtual int height() = 0; - - /// Fill the sprite with the given pixel value. The pixel format - /// depends on the format of the sprite. - virtual void fill(int value) = 0; - - /// Set one pixel value. The pixel format depends on the sprite - /// format. This is not expected to be fast, and in some - /// implementations may not work at all. - virtual void pixel(int x, int y, int value) {} - - /// Lock sprite for direct drawing, and return a struct - /// containing the necessary pointer. When finished, unlock the - /// sprite with unlock(). May return NULL, if so then direct - /// drawing is not possible. - virtual const SpriteData *lock() { return NULL; } - virtual void unlock() {} - }; - } -} -#endif diff --git a/libs/mangle/rend2d/tests/.gitignore b/libs/mangle/rend2d/tests/.gitignore deleted file mode 100644 index 814490404..000000000 --- a/libs/mangle/rend2d/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/libs/mangle/rend2d/tests/Makefile b/libs/mangle/rend2d/tests/Makefile deleted file mode 100644 index d430f60a9..000000000 --- a/libs/mangle/rend2d/tests/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -GCC=g++ -Wall -Werror - -all: sdl_test sdl_move_test sdlgl_move_test - -sdl_test: sdl_test.cpp - $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image - -sdl_move_test: sdl_move_test.cpp ../servers/sdl_driver.cpp - $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image - -sdlgl_move_test: sdlgl_move_test.cpp ../servers/sdl_gl_driver.cpp - $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image -lGL - -clean: - rm *_test diff --git a/libs/mangle/rend2d/tests/output/sdl_move_test.out b/libs/mangle/rend2d/tests/output/sdl_move_test.out deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/mangle/rend2d/tests/output/sdl_test.out b/libs/mangle/rend2d/tests/output/sdl_test.out deleted file mode 100644 index 4528e1a98..000000000 --- a/libs/mangle/rend2d/tests/output/sdl_test.out +++ /dev/null @@ -1,11 +0,0 @@ -Loading SDL driver. -Creating window. -Current mode: 640x480 -Setting fancy title, cause we like fancy titles. -Loading tile1-blue.png from file. -Loading tile1-yellow.png from memory. -Going bananas. -Taking a breather. -WOW DID YOU SEE THAT!? -Mucking about with the gamma settings -Done. diff --git a/libs/mangle/rend2d/tests/output/sdlgl_move_test.out b/libs/mangle/rend2d/tests/output/sdlgl_move_test.out deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/mangle/rend2d/tests/sdl_move_test.cpp b/libs/mangle/rend2d/tests/sdl_move_test.cpp deleted file mode 100644 index bfbca98fa..000000000 --- a/libs/mangle/rend2d/tests/sdl_move_test.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - SDLDriver sdl; - - sdl.setVideoMode(640,480,0,false); - sdl.setWindowTitle("Testing 123"); - Sprite *screen = sdl.getScreen(); - const char* imgName = "tile1-blue.png"; - Sprite *image = sdl.loadImage(imgName); - - for(int frames=0; frames<170; frames++) - { - screen->fill(0); - for(int j=0; j<10; j++) - for(int i=0; i<25; i++) - screen->draw(image, 2*frames+30*j, 20*i); - sdl.update(); - sdl.sleep(10); - } - return 0; -} diff --git a/libs/mangle/rend2d/tests/sdl_test.cpp b/libs/mangle/rend2d/tests/sdl_test.cpp deleted file mode 100644 index 0355112e6..000000000 --- a/libs/mangle/rend2d/tests/sdl_test.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - cout << "Loading SDL driver.\n"; - SDLDriver sdl; - - cout << "Creating window.\n"; - sdl.setVideoMode(640,480); - cout << "Current mode: " << sdl.width() << "x" << sdl.height() << endl; - - cout << "Setting fancy title, cause we like fancy titles.\n"; - sdl.setWindowTitle("Chief executive window"); - - // Display surface - Sprite *screen = sdl.getScreen(); - - const char* imgName = "tile1-blue.png"; - cout << "Loading " << imgName << " from file.\n"; - Sprite *image = sdl.loadImage(imgName); - - const char* imgName2 = "tile1-yellow.png"; - cout << "Loading " << imgName2 << " from memory.\n"; - Sprite *image2; - { - // This is hard-coded for file sizes below 500 bytes, so obviously - // you shouldn't mess with the image files. - ifstream file(imgName2, ios::binary); - char buf[500]; - file.read(buf, 500); - int size = file.gcount(); - image2 = sdl.loadImage(buf, size); - } - - cout << "Going bananas.\n"; - for(int i=1; i<20; i++) - screen->draw(image, 30*i, 20*i); - - cout << "Taking a breather.\n"; - sdl.update(); - for(int i=1; i<20; i++) - screen->draw(image2, 30*(20-i), 20*i); - sdl.sleep(800); - sdl.update(); - cout << "WOW DID YOU SEE THAT!?\n"; - sdl.sleep(800); - - cout << "Mucking about with the gamma settings\n"; - sdl.setGamma(2.0, 0.1, 0.8); - sdl.sleep(100); - sdl.setGamma(0.6, 2.1, 2.1); - sdl.sleep(100); - sdl.setGamma(1.6); - sdl.sleep(100); - - cout << "Done.\n"; - return 0; -} diff --git a/libs/mangle/rend2d/tests/sdlgl_move_test.cpp b/libs/mangle/rend2d/tests/sdlgl_move_test.cpp deleted file mode 100644 index b769ee837..000000000 --- a/libs/mangle/rend2d/tests/sdlgl_move_test.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include - -using namespace std; - -#include "../servers/sdl_gl_driver.hpp" - -using namespace Mangle::Rend2D; - -int main() -{ - SDLGLDriver sdl; - - sdl.setVideoMode(640,480,0,false); - sdl.setWindowTitle("Testing 123"); - Sprite *screen = sdl.getScreen(); - const char* imgName = "tile1-blue.png"; - Sprite *image = sdl.loadImage(imgName); - - for(int frames=0; frames<170; frames++) - { - screen->fill(0); - for(int j=0; j<10; j++) - for(int i=0; i<25; i++) - screen->draw(image, 2*frames+30*j, 20*i); - sdl.update(); - sdl.sleep(5); - } - - return 0; -} diff --git a/libs/mangle/rend2d/tests/test.sh b/libs/mangle/rend2d/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/libs/mangle/rend2d/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/rend2d/tests/tile1-blue.png b/libs/mangle/rend2d/tests/tile1-blue.png deleted file mode 100644 index 066e6f8eb932e81d0ab391bc16a3b9e0fb063d7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg4HgO>q^~cs6azG);64!_l=ltB< z)VvY~=c3falGGH1^30M91$R&1fbd2>aiF3cPZ!4!jq}MON!e)%tQ!)zB&BycNVusb zWdv<3H_ml_#P;5c#V!`t_IO|Ks0DIaVrPLG@TdWn2J z|KvBBkM=tLPH;V9awjzAOkRtr!tdY5?;n;qvu%fo@uXRM82JyY98GH4whCx3gQu&X J%Q~loCIFm~TL1t6 diff --git a/libs/mangle/rend2d/tests/tile1-yellow.png b/libs/mangle/rend2d/tests/tile1-yellow.png deleted file mode 100644 index 2aaf9015d313d5cd00915ffbb1df2f0262ea2116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEjDo>Htxq)+3JBpk|nMYCC>S|xv6<249-QVi6yBi3gww4 z84B*6z5(HleBwYwd7dtgAsXkCb@CIF6<9YUa9LVOJ4;MTQp*TBJ?;FFMT!6KZ8mro zxg%{yo!B3~hU~W2DM<=~6P7+aaBe@#)q{84nt0F0&sDtcsm}jj_2%Ux)y>}yc6DU# wSS@pBCTF(8x9=wp*PqRuS(uroVp_?}Ab(0lNY${m9cVLyr>mdKI;Vst0EqflTmS$7 diff --git a/libs/mangle/testall.sh b/libs/mangle/testall.sh deleted file mode 100755 index b93fee215..000000000 --- a/libs/mangle/testall.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -function run() -{ - echo "TESTING $1" - cd "$1/tests/" - ./test.sh - cd ../../ -} - -run stream -run vfs -run sound -run input -run rend2d -run . diff --git a/libs/mangle/tests/.gitignore b/libs/mangle/tests/.gitignore deleted file mode 100644 index 814490404..000000000 --- a/libs/mangle/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*_test diff --git a/libs/mangle/tests/Makefile b/libs/mangle/tests/Makefile deleted file mode 100644 index d912c0784..000000000 --- a/libs/mangle/tests/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -GCC=g++ -I../ - -all: ogrevfs_audiere_openal_test - -I_OGRE=$(shell pkg-config --cflags OGRE) -L_OGRE=$(shell pkg-config --libs OGRE) -L_OPENAL=$(shell pkg-config --libs openal) -L_AUDIERE=-laudiere - -ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/sources/audiere_source.cpp ../sound/outputs/openal_out.cpp ../stream/clients/audiere_file.cpp - $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) - -clean: - rm *_test diff --git a/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp b/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp deleted file mode 100644 index 4936538c5..000000000 --- a/libs/mangle/tests/ogrevfs_audiere_openal_test.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - This example combines: - - - the OGRE VFS system (to read from zip) - - Audiere (for decoding sound data) - - OpenAL (for sound playback) - - */ - -#include "sound/filters/openal_audiere.hpp" -#include "vfs/servers/ogre_vfs.hpp" -#include -#include - -using namespace Ogre; -using namespace Mangle; -using namespace std; - -int main() -{ - // Disable Ogre logging - new LogManager; - Log *log = LogManager::getSingleton().createLog(""); - log->setDebugOutputEnabled(false); - - // Set up Root - Root *root = new Root("","",""); - - // Add zip file with a sound in it - root->addResourceLocation("sound.zip", "Zip", "General"); - - // Ogre file system - VFS::OgreVFS vfs; - - // The main sound system - Sound::OpenAL_Audiere_Factory mg; - Sound::SoundPtr snd = mg.load(vfs.open("owl.ogg")); - - cout << "Playing 'owl.ogg' from 'sound.zip'\n"; - snd->play(); - - while(snd->isPlaying()) - { - usleep(10000); - if(mg.needsUpdate) mg.update(); - } - - return 0; -} diff --git a/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out b/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out deleted file mode 100644 index 28ea8a71b..000000000 --- a/libs/mangle/tests/output/ogrevfs_audiere_openal_test.out +++ /dev/null @@ -1 +0,0 @@ -Playing 'owl.ogg' from 'sound.zip' diff --git a/libs/mangle/tests/sound.zip b/libs/mangle/tests/sound.zip deleted file mode 100644 index fd32b35299859ba2d92e702db719367110d8b994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18159 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 diff --git a/libs/mangle/tests/test.sh b/libs/mangle/tests/test.sh deleted file mode 100755 index 2d07708ad..000000000 --- a/libs/mangle/tests/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -make || exit - -mkdir -p output - -PROGS=*_test - -for a in $PROGS; do - if [ -f "output/$a.out" ]; then - echo "Running $a:" - ./$a | diff output/$a.out - - else - echo "Creating $a.out" - ./$a > "output/$a.out" - git add "output/$a.out" - fi -done diff --git a/libs/mangle/tools/shared_ptr.hpp b/libs/mangle/tools/shared_ptr.hpp deleted file mode 100644 index 3d073fc24..000000000 --- a/libs/mangle/tools/shared_ptr.hpp +++ /dev/null @@ -1,3 +0,0 @@ -// This file should include whatever it needs to define the boost/tr1 -// shared_ptr<> and weak_ptr<> templates. -#include diff --git a/libs/openengine/gui/events.cpp b/libs/openengine/gui/events.cpp deleted file mode 100644 index 35b01158b..000000000 --- a/libs/openengine/gui/events.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include - -#include "events.hpp" - -using namespace OIS; -using namespace OEngine::GUI; - -EventInjector::EventInjector(MyGUI::Gui *g) - : gui(g), enabled(true) - , mMouseX(0) - , mMouseY(0) -{ - assert(gui); - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - mMouseX = viewSize.width/2; - mMouseY = viewSize.height/2; -} - -void EventInjector::event(Type type, int index, const void *p) -{ - if(!enabled) return; - - if(type & EV_Keyboard) - { - KeyEvent *key = (KeyEvent*)p; - MyGUI::KeyCode code = MyGUI::KeyCode::Enum(key->key); - if(type == EV_KeyDown) - { - /* - This is just a first approximation. Apparently, OIS is - unable to provide reliable unicode characters on all - platforms. At least that's what I surmise from the amount - of workaround that the MyGUI folks have put in place for - this. See Common/Input/OIS/InputManager.cpp in the MyGUI - sources for details. - - If the work they have done there is indeed necessary (I - haven't tested that it is, although I have had dubious - experinces with OIS events in the past), then we should - probably adapt all that code here. Or even better, - directly into the OIS input manager in Mangle. - - Note that all this only affects the 'text' field, and - should thus only affect typed text in input boxes (which - is still pretty significant.) - */ - MyGUI::Char text = (MyGUI::Char)key->text; - MyGUI::InputManager::getInstance().injectKeyPress(code,text); - } - else - { - MyGUI::InputManager::getInstance().injectKeyRelease(code); - } - } - else if(type & EV_Mouse) - { - MouseEvent *mouse = (MouseEvent*)p; - MyGUI::MouseButton id = MyGUI::MouseButton::Enum(index); - - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // Update mouse position - mMouseX += mouse->state.X.rel; - mMouseY += mouse->state.Y.rel; - mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); - mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); - - if(type == EV_MouseDown) - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, id); - else if(type == EV_MouseUp) - MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, id); - else - MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mouse->state.Z.abs); - } -} diff --git a/libs/openengine/gui/events.hpp b/libs/openengine/gui/events.hpp deleted file mode 100644 index 10c5309bc..000000000 --- a/libs/openengine/gui/events.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef OENGINE_MYGUI_EVENTS_H -#define OENGINE_MYGUI_EVENTS_H - -#include - -namespace MyGUI -{ - class Gui; -} - -namespace OEngine { -namespace GUI -{ - /** Event handler that injects OIS events into MyGUI - */ - class EventInjector : public Mangle::Input::Event - { - MyGUI::Gui *gui; - - int mMouseX; - int mMouseY; - - public: - bool enabled; - - EventInjector(MyGUI::Gui *g); - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr EventInjectorPtr; -}} -#endif diff --git a/libs/openengine/ogre/mouselook.cpp b/libs/openengine/ogre/mouselook.cpp deleted file mode 100644 index 841bab603..000000000 --- a/libs/openengine/ogre/mouselook.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "mouselook.hpp" - -#include -#include -#include - -using namespace OIS; -using namespace Ogre; -using namespace OEngine::Render; - -void MouseLookEvent::event(Type type, int index, const void *p) -{ - if(type != EV_MouseMove || camera == NULL) return; - - MouseEvent *arg = (MouseEvent*)(p); - - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; - - camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x)); - camera->getParentSceneNode()->pitch(Degree(-y)); - if(flipProt) - { - // The camera before pitching - /*Quaternion nopitch = camera->getParentSceneNode()->getOrientation(); - - camera->getParentSceneNode()->pitch(Degree(-y)); - - // Apply some failsafe measures against the camera flipping - // upside down. Is the camera close to pointing straight up or - // down? - if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1) - // If so, undo the last pitch - camera->getParentSceneNode()->setOrientation(nopitch);*/ - //camera->getU - - // Angle of rotation around the X-axis. - float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees()); - - // Just to determine the sign of the angle we pick up above, the - // value itself does not interest us. - float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x; - - // Limit the pitch between -90 degress and +90 degrees, Quake3-style. - if (pitchAngle > 90.0f) - { - if (pitchAngleSign > 0) - // Set orientation to 90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - Ogre::Math::Sqrt(0.5f), 0, 0)); - else if (pitchAngleSign < 0) - // Sets orientation to -90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - -Ogre::Math::Sqrt(0.5f), 0, 0)); - } - } -} diff --git a/libs/openengine/ogre/mouselook.hpp b/libs/openengine/ogre/mouselook.hpp deleted file mode 100644 index 6e09ff4a1..000000000 --- a/libs/openengine/ogre/mouselook.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef OENGINE_OGRE_MOUSELOOK_H -#define OENGINE_OGRE_MOUSELOOK_H - -/* - A mouse-look class for Ogre. Accepts input events from Mangle::Input - and translates them. - - You can adjust the mouse sensibility and switch to a different - camera. The mouselook class also has an optional wrap protection - that keeps the camera from flipping upside down. - - You can disable the mouse looker at any time by calling - setCamera(NULL), and reenable it by setting the camera back. - - NOTE: The current implementation will ONLY work for native OIS - events. - */ - -#include - -namespace Ogre -{ - class Camera; -} - -namespace OEngine { -namespace Render -{ - class MouseLookEvent : public Mangle::Input::Event - { - Ogre::Camera* camera; - float sensX, sensY; // Mouse sensibility - bool flipProt; // Flip protection - - public: - MouseLookEvent(Ogre::Camera *cam=NULL, - float sX=0.2, float sY=0.2, - bool prot=true) - : camera(cam) - , sensX(sX) - , sensY(sY) - , flipProt(prot) - {} - - void setCamera(Ogre::Camera *cam) - { camera = cam; } - void setSens(float sX, float sY) - { sensX = sX; sensY = sY; } - void setProt(bool p) { flipProt = p; } - - void event(Type type, int index, const void *p); - }; - - typedef boost::shared_ptr MouseLookEventPtr; -}} -#endif From 97c45455fd762196a583b6da361caf313b314124 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 21:21:23 +0200 Subject: [PATCH 408/688] fix WindowManager destruction --- apps/openmw/mwbase/environment.cpp | 3 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- libs/openengine/gui/manager.cpp | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 8d786db91..9aaa5af85 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -146,8 +146,7 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; -/// \todo Re-enable (currently throwing an exception) -// delete mWindowManager; + delete mWindowManager; mWindowManager = 0; delete mWorld; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2a6f7ec9b..183c435fd 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -159,7 +159,6 @@ WindowManager::WindowManager( WindowManager::~WindowManager() { - delete mGuiManager; delete mConsole; delete mMessageBoxManager; delete mHud; @@ -182,6 +181,8 @@ WindowManager::~WindowManager() delete mSpellWindow; cleanupGarbage(); + + delete mGuiManager; } void WindowManager::cleanupGarbage() diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 9c6ca37eb..58929ba8b 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -36,6 +36,7 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool void MyGUIManager::shutdown() { + mGui->shutdown (); delete mGui; if(mPlatform) { From 90f1d9c2f2989011878c83852a0779e85f0fdcca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 21:25:13 +0200 Subject: [PATCH 409/688] OSX suggestion by corristo --- apps/openmw/mwinput/inputmanagerimp.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d1c400cea..e1eacae05 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -1,5 +1,9 @@ #include "inputmanagerimp.hpp" +#if defined(__APPLE__) && !defined(__LP64__) +#include +#endif + #include #include @@ -76,12 +80,12 @@ namespace MWInput #endif } - #ifdef __APPLE_CC__ +#if defined(__APPLE__) && !defined(__LP64__) // Give the application window focus to receive input events ProcessSerialNumber psn = { 0, kCurrentProcess }; TransformProcessType(&psn, kProcessTransformToForegroundApplication); SetFrontProcess(&psn); - #endif +#endif mInputManager = OIS::InputManager::createInputSystem( pl ); From 976ad7a301bff286a65a119cc7d7532e61daea25 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Aug 2012 22:59:58 +0200 Subject: [PATCH 410/688] key defaults specified in the code now, required in order to keep the configuration files valid across multiple versions of openmw --- CMakeLists.txt | 3 - apps/openmw/engine.cpp | 20 +---- apps/openmw/mwinput/inputmanagerimp.cpp | 67 ++++++++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +- files/input-default.xml | 100 ------------------------ 5 files changed, 58 insertions(+), 137 deletions(-) delete mode 100644 files/input-default.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 79e33f181..68fdee1e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,9 +285,6 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") -configure_file(${OpenMW_SOURCE_DIR}/files/input-default.xml - "${OpenMW_BINARY_DIR}/input-default.xml") - configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg "${OpenMW_BINARY_DIR}/transparency-overrides.cfg") diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 128d97553..edd0dead1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -271,22 +271,10 @@ void OMW::Engine::go() settings.loadUser(globaldefault); // Get the path for the keybinder xml file - std::string keybinderDefault; - - // load user settings if they exist, otherwise just load the default settings as user settings - const std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - const std::string keybinderDefaultLocal = (mCfgMgr.getLocalPath() / "input-default.xml").string(); - const std::string keybinderDefaultGlobal = (mCfgMgr.getGlobalPath() / "input-default.xml").string(); - - bool keybinderUserExists = boost::filesystem::exists(keybinderUser); - - if (boost::filesystem::exists(keybinderDefaultLocal)) - keybinderDefault = keybinderDefaultLocal; - else if (boost::filesystem::exists(keybinderDefaultGlobal)) - keybinderDefault = keybinderDefaultGlobal; - else - throw std::runtime_error ("No default input settings found! Make sure the file \"input-default.xml\" was properly installed."); + std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + if (!boost::filesystem::exists(keybinderUser)) + keybinderUser = ""; mFpsLevel = settings.getInt("fps", "HUD"); @@ -386,7 +374,7 @@ void OMW::Engine::go() mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderDefault, keybinderUser, keybinderUserExists)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index e1eacae05..031899538 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -7,8 +7,7 @@ #include #include -#include -#include +#include #include @@ -32,8 +31,7 @@ namespace MWInput MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, - const std::string& defaultFile, - const std::string& userFile, bool userFileExists) + const std::string& userFile) : mOgre(ogre) , mPlayer(player) , mWindows(windows) @@ -43,6 +41,7 @@ namespace MWInput , mMouseY(ogre.getWindow()->getHeight ()/2.f) , mUserFile(userFile) , mDragDrop(false) + , mGuiCursorEnabled(false) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -102,15 +101,9 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - std::string configFile; - if (userFileExists) - configFile = userFile; - else - configFile = defaultFile; + mInputCtrl = new ICS::InputControlSystem(userFile, true, NULL, NULL, A_LAST); - std::cout << "Loading input configuration: " << configFile << std::endl; - - mInputCtrl = new ICS::InputControlSystem(configFile, true, NULL, NULL, A_LAST); + loadKeyDefaults(); for (int i = 0; i < A_LAST; ++i) { @@ -339,8 +332,7 @@ namespace MWInput { mInputCtrl->keyReleased (arg); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(arg.key)); return true; } @@ -359,8 +351,7 @@ namespace MWInput { mInputCtrl->mouseReleased (arg, id); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); return true; } @@ -449,11 +440,15 @@ namespace MWInput { bool gameMode = !mWindows.isGuiMode(); + std::cout << "gameMode: " << gameMode << std::endl; + // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); else if(mWindows.getMode() == MWGui::GM_Inventory) mWindows.popGuiMode(); + else + std::cout << "toggleInv didnt do anything!!!" << std::endl; // .. but don't touch any other mode. } @@ -516,4 +511,44 @@ namespace MWInput return mInputCtrl->getChannel (id)->getValue () == 1; } + void InputManager::loadKeyDefaults () + { + // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid + // across different versions of OpenMW (in the case where another input action is added) + std::map defaultKeyBindings; + + defaultKeyBindings[A_Activate] = OIS::KC_SPACE; + defaultKeyBindings[A_MoveBackward] = OIS::KC_S; + defaultKeyBindings[A_MoveForward] = OIS::KC_W; + defaultKeyBindings[A_MoveLeft] = OIS::KC_A; + defaultKeyBindings[A_MoveRight] = OIS::KC_D; + defaultKeyBindings[A_ToggleWeapon] = OIS::KC_F; + defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; + defaultKeyBindings[A_Console] = OIS::KC_F1; + defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL; + defaultKeyBindings[A_AutoMove] = OIS::KC_Q; + defaultKeyBindings[A_Jump] = OIS::KC_E; + defaultKeyBindings[A_Journal] = OIS::KC_J; + defaultKeyBindings[A_Rest] = OIS::KC_T; + defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; + + std::map defaultMouseButtonBindings; + defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; + + for (int i = 0; i < A_LAST; ++i) + { + if (mInputCtrl->getChannel(i)->getControlsCount () == 0) + { + ICS::Control* control1 = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); + mInputCtrl->addControl(control1); + control1->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + + if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) + mInputCtrl->addKeyBinding(control1, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); + else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) + mInputCtrl->addMouseButtonBinding (control1, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + } + } + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index ba42327ee..9fc77aeeb 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -61,8 +61,7 @@ namespace MWInput MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine, - const std::string& defaultFile, - const std::string& userFile, bool userFileExists); + const std::string& userFile); virtual ~InputManager(); @@ -130,6 +129,8 @@ namespace MWInput bool actionIsActive (int id); + void loadKeyDefaults(); + private: enum Actions { diff --git a/files/input-default.xml b/files/input-default.xml deleted file mode 100644 index e44b3455a..000000000 --- a/files/input-default.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From bc6e4feedc6d68958ea72a7689ca73f86b913db1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 01:26:15 +0200 Subject: [PATCH 411/688] hotkey window first version --- apps/openmw/engine.cpp | 6 +- apps/openmw/mwbase/inputmanager.hpp | 6 + apps/openmw/mwbase/windowmanager.hpp | 4 + apps/openmw/mwgui/hud.cpp | 3 + apps/openmw/mwgui/hud.hpp | 2 + apps/openmw/mwgui/settingswindow.cpp | 51 +++++++ apps/openmw/mwgui/settingswindow.hpp | 9 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 24 ++++ apps/openmw/mwgui/windowmanagerimp.hpp | 6 + apps/openmw/mwinput/inputmanagerimp.cpp | 158 ++++++++++++++++++++-- apps/openmw/mwinput/inputmanagerimp.hpp | 36 ++++- files/mygui/openmw_settings_window.layout | 7 + 12 files changed, 291 insertions(+), 21 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index edd0dead1..0bfd97c5d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -272,9 +272,7 @@ void OMW::Engine::go() // Get the path for the keybinder xml file std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - - if (!boost::filesystem::exists(keybinderUser)) - keybinderUser = ""; + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); mFpsLevel = settings.getInt("fps", "HUD"); @@ -374,7 +372,7 @@ void OMW::Engine::go() mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser)); + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); std::cout << "\nPress Q/ESC or close window to exit.\n"; diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 5d73025a7..bf1d8e45f 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -31,6 +31,12 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + + virtual std::string getActionDescription (int action) = 0; + virtual std::string getActionBindingName (int action) = 0; + virtual std::vector getActionSorting () = 0; + virtual int getNumActions() = 0; + virtual void enableDetectingBindingMode (int action) = 0; }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 931353a90..fde965256 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -177,6 +177,10 @@ namespace MWBase virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; + virtual void disallowMouse() = 0; + virtual void allowMouse() = 0; + virtual void notifyInputActionBound() = 0; + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index bdbb316b0..92dc4e495 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -237,6 +237,9 @@ void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible void HUD::onWorldClicked(MyGUI::Widget* _sender) { + if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) + return; + if (mDragAndDrop->mIsOnDragAndDrop) { // drop item into the gameworld diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 485c788fc..baa350a10 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -48,6 +48,8 @@ namespace MWGui MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; + MyGUI::Widget* mDummy; + MyGUI::WidgetPtr fpsbox; MyGUI::TextBox* fpscounter; MyGUI::TextBox* trianglecounter; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index f6597a64e..477ffe0e2 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -115,6 +115,7 @@ namespace MWGui getWidget(mMiscShadows, "MiscShadows"); getWidget(mShadowsDebug, "ShadowsDebug"); getWidget(mUnderwaterButton, "UnderwaterButton"); + getWidget(mControlsBox, "ControlsBox"); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -511,4 +512,54 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); } + + void SettingsWindow::updateControlsBox() + { + while (mControlsBox->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mControlsBox->getChildAt(0)); + + std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); + + const int h = 18; + const int w = mControlsBox->getWidth() - 34; + int curH = 6; + for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) + { + std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); + if (desc == "") + continue; + + std::string binding = MWBase::Environment::get().getInputManager()->getActionBindingName (*it); + + MyGUI::TextBox* leftText = mControlsBox->createWidget("SandText", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + leftText->setCaptionWithReplacing(desc); + + MyGUI::Button* rightText = mControlsBox->createWidget("SandTextButton", MyGUI::IntCoord(0,curH,w,h), MyGUI::Align::Default); + rightText->setCaptionWithReplacing(binding); + rightText->setTextAlign (MyGUI::Align::Right); + rightText->setUserData(*it); // save the action id for callbacks + rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); + curH += h; + } + + mControlsBox->setCanvasSize (mControlsBox->getWidth(), std::max(curH, mControlsBox->getHeight())); + } + + void SettingsWindow::onRebindAction(MyGUI::Widget* _sender) + { + int actionId = *_sender->getUserData(); + + static_cast(_sender)->setCaptionWithReplacing("#{sNone}"); + + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sControlsMenu3}", std::vector()); + MWBase::Environment::get().getWindowManager ()->disallowMouse(); + + MWBase::Environment::get().getInputManager ()->enableDetectingBindingMode (actionId); + + } + + void SettingsWindow::open() + { + updateControlsBox (); + } } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index ca11b6f9c..61614da01 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -15,6 +15,10 @@ namespace MWGui public: SettingsWindow(MWBase::WindowManager& parWindowManager); + virtual void open(); + + void updateControlsBox(); + private: static int const sFovMin = 30; static int const sFovMax = 140; @@ -60,6 +64,9 @@ namespace MWGui MyGUI::ScrollBar* mFootstepsVolumeSlider; MyGUI::ScrollBar* mMusicVolumeSlider; + // controls + MyGUI::ScrollView* mControlsBox; + void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); void onTextureFilteringToggled(MyGUI::Widget* _sender); @@ -72,6 +79,8 @@ namespace MWGui void onShadersToggled(MyGUI::Widget* _sender); void onShadowTextureSize(MyGUI::Widget* _sender); + void onRebindAction(MyGUI::Widget* _sender); + void apply(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 183c435fd..676eb2046 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -134,6 +134,8 @@ WindowManager::WindowManager( mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + // The HUD is always on mHud->setVisible(true); @@ -230,11 +232,16 @@ void WindowManager::updateVisible() bool gameMode = !isGuiMode(); + mInputBlocker->setVisible (gameMode); + if (gameMode) mToolTips->enterGameMode(); else mToolTips->enterGuiMode(); + if (gameMode) + MyGUI::InputManager::getInstance ().setKeyFocusWidget (NULL); + setMinimapVisibility((mAllowed & GW_Map) && !mMap->pinned()); setWeaponVisibility((mAllowed & GW_Inventory) && !mInventoryWindow->pinned()); setSpellVisibility((mAllowed & GW_Magic) && !mSpellWindow->pinned()); @@ -646,6 +653,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mScrollWindow->center(); mBookWindow->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); } } @@ -823,3 +831,19 @@ WindowManager::SkillList WindowManager::getPlayerMajorSkills() { return mPlayerMajorSkills; } + +void WindowManager::disallowMouse() +{ + mInputBlocker->setVisible (true); +} + +void WindowManager::allowMouse() +{ + mInputBlocker->setVisible (!isGuiMode ()); +} + +void WindowManager::notifyInputActionBound () +{ + mSettingsWindow->updateControlsBox (); + allowMouse(); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index eaa6a1683..391305594 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -157,6 +157,10 @@ namespace MWGui virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); + virtual void disallowMouse(); + virtual void allowMouse(); + virtual void notifyInputActionBound(); + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. virtual void messageBox (const std::string& message, const std::vector& buttons); @@ -208,6 +212,8 @@ namespace MWGui CharacterCreation* mCharGen; + MyGUI::Widget* mInputBlocker; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 031899538..09ed50944 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include "../engine.hpp" @@ -31,7 +29,7 @@ namespace MWInput MWBase::WindowManager &windows, bool debug, OMW::Engine& engine, - const std::string& userFile) + const std::string& userFile, bool userFileExists) : mOgre(ogre) , mPlayer(player) , mWindows(windows) @@ -101,11 +99,12 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, mMouse->getMouseState ().Z.abs); - mInputCtrl = new ICS::InputControlSystem(userFile, true, NULL, NULL, A_LAST); + std::string file = userFileExists ? userFile : ""; + mInputCtrl = new ICS::InputControlSystem(file, true, this, NULL, A_Last); loadKeyDefaults(); - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { mInputCtrl->getChannel (i)->addListener (this); } @@ -322,8 +321,7 @@ namespace MWInput { mInputCtrl->keyPressed (arg); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); return true; } @@ -341,8 +339,7 @@ namespace MWInput { mInputCtrl->mousePressed (arg, id); - if (mGuiCursorEnabled) - MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); return true; } @@ -440,15 +437,11 @@ namespace MWInput { bool gameMode = !mWindows.isGuiMode(); - std::cout << "gameMode: " << gameMode << std::endl; - // Toggle between game mode and inventory mode if(gameMode) mWindows.pushGuiMode(MWGui::GM_Inventory); else if(mWindows.getMode() == MWGui::GM_Inventory) mWindows.popGuiMode(); - else - std::cout << "toggleInv didnt do anything!!!" << std::endl; // .. but don't touch any other mode. } @@ -535,7 +528,7 @@ namespace MWInput std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; - for (int i = 0; i < A_LAST; ++i) + for (int i = 0; i < A_Last; ++i) { if (mInputCtrl->getChannel(i)->getControlsCount () == 0) { @@ -551,4 +544,141 @@ namespace MWInput } } + std::string InputManager::getActionDescription (int action) + { + std::map descriptions; + + descriptions[A_Activate] = "sActivate"; + descriptions[A_MoveBackward] = "sBack"; + descriptions[A_MoveForward] = "sForward"; + descriptions[A_MoveLeft] = "sLeft"; + descriptions[A_MoveRight] = "sRight"; + descriptions[A_ToggleWeapon] = "sReady_Weapon"; + descriptions[A_ToggleSpell] = "sReady_Magic"; + descriptions[A_Console] = "sConsoleTitle"; + descriptions[A_Crouch] = "sCrouch_Sneak"; + descriptions[A_AutoMove] = "sAuto_Run"; + descriptions[A_Jump] = "sJump"; + descriptions[A_Journal] = "sJournal"; + descriptions[A_Rest] = "sRestKey"; + descriptions[A_Inventory] = "sInventory"; + + if (action == A_GameMenu) + return "Menu"; // not configurable in morrowind so no GMST + + if (descriptions[action] == "") + return ""; // not configurable + + return "#{" + descriptions[action] + "}"; + } + + std::string InputManager::getActionBindingName (int action) + { + if (mInputCtrl->getChannel (action)->getControlsCount () == 0) + return "#{sNone}"; + + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + if (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + return mInputCtrl->keyCodeToString (mInputCtrl->getKeyBinding (c, ICS::Control::INCREASE)); + else if (mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + return "#{sMouse} " + boost::lexical_cast(mInputCtrl->getMouseButtonBinding (c, ICS::Control::INCREASE)); + else + return "#{sNone}"; + } + + std::vector InputManager::getActionSorting() + { + std::vector ret; + ret.push_back(A_MoveForward); + ret.push_back(A_MoveBackward); + ret.push_back(A_MoveLeft); + ret.push_back(A_MoveRight); + ret.push_back(A_Crouch); + ret.push_back(A_Activate); + ret.push_back(A_ToggleWeapon); + ret.push_back(A_AutoMove); + ret.push_back(A_Jump); + ret.push_back(A_Inventory); + ret.push_back(A_Journal); + ret.push_back(A_Rest); + ret.push_back(A_Console); + ret.push_back(A_GameMenu); + + return ret; + } + + void InputManager::enableDetectingBindingMode (int action) + { + ICS::Control* c = mInputCtrl->getChannel (action)->getAttachedControls ().front().control; + + mInputCtrl->enableDetectingBindingState (c, ICS::Control::INCREASE); + } + + void InputManager::mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction) + { + // we don't want mouse movement bindings + return; + } + + void InputManager::keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::keyBindingDetected (ICS, control, key, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::mouseButtonBindingDetected (ICS, control, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickAxisBindingDetected (ICS, control, deviceId, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickButtonBindingDetected (ICS, control, deviceId, button, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickPOVBindingDetected (ICS, control, deviceId, pov, axis, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction) + { + clearAllBindings(control); + ICS::DetectingBindingListener::joystickSliderBindingDetected (ICS, control, deviceId, slider, direction); + MWBase::Environment::get().getWindowManager ()->notifyInputActionBound (); + } + + void InputManager::clearAllBindings (ICS::Control* control) + { + // right now we don't really need multiple bindings for the same action, so remove all others first + if (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) != OIS::KC_UNASSIGNED) + mInputCtrl->removeKeyBinding (mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE)); + if (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) != ICS_MAX_DEVICE_BUTTONS) + mInputCtrl->removeMouseButtonBinding (mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE)); + + /// \todo add joysticks here once they are added + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 9fc77aeeb..d3d4d1385 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -46,6 +46,7 @@ namespace OIS #include #include +#include namespace MWInput { @@ -53,7 +54,7 @@ namespace MWInput /** * @brief Class that handles all input and key bindings for OpenMW. */ - class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener + class InputManager : public MWBase::InputManager, public OIS::KeyListener, public OIS::MouseListener, public ICS::ChannelListener, public ICS::DetectingBindingListener { public: InputManager(OEngine::Render::OgreRenderer &_ogre, @@ -61,7 +62,7 @@ namespace MWInput MWBase::WindowManager &_windows, bool debug, OMW::Engine& engine, - const std::string& userFile); + const std::string& userFile, bool userFileExists); virtual ~InputManager(); @@ -75,6 +76,12 @@ namespace MWInput virtual void toggleControlSwitch (const std::string& sw, bool value); + virtual std::string getActionDescription (int action); + virtual std::string getActionBindingName (int action); + virtual int getNumActions() { return A_Last; } + virtual std::vector getActionSorting (); + virtual void enableDetectingBindingMode (int action); + public: virtual bool keyPressed( const OIS::KeyEvent &arg ); @@ -86,6 +93,29 @@ namespace MWInput virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); + virtual void mouseAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , ICS::InputControlSystem::NamedAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void keyBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , OIS::KeyCode key, ICS::Control::ControlChangingDirection direction); + + virtual void mouseButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickAxisBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickButtonBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, unsigned int button, ICS::Control::ControlChangingDirection direction); + + virtual void joystickPOVBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int pov,ICS:: InputControlSystem::POVAxis axis, ICS::Control::ControlChangingDirection direction); + + virtual void joystickSliderBindingDetected(ICS::InputControlSystem* ICS, ICS::Control* control + , int deviceId, int slider, ICS::Control::ControlChangingDirection direction); + + void clearAllBindings (ICS::Control* control); + private: OEngine::Render::OgreRenderer &mOgre; MWWorld::Player &mPlayer; @@ -175,7 +205,7 @@ namespace MWInput A_ToggleWeapon, A_ToggleSpell, - A_LAST // Marker for the last item + A_Last // Marker for the last item }; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index a14606ade..ee4855f38 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -82,6 +82,13 @@ + + + + + + + From c7b8787c323701253175d86819a0a12f08f6ab42 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 02:55:22 +0200 Subject: [PATCH 412/688] "reset to defaults" button, invert y axis button --- apps/openmw/mwbase/inputmanager.hpp | 1 + apps/openmw/mwgui/settingswindow.cpp | 38 +++++++++++++++++++++-- apps/openmw/mwgui/settingswindow.hpp | 5 +++ apps/openmw/mwinput/inputmanagerimp.cpp | 37 +++++++++++++++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 5 ++- files/mygui/openmw_settings_window.layout | 9 +++++- files/settings-default.cfg | 5 +++ 7 files changed, 88 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index bf1d8e45f..4c73c72b9 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -37,6 +37,7 @@ namespace MWBase virtual std::vector getActionSorting () = 0; virtual int getNumActions() = 0; virtual void enableDetectingBindingMode (int action) = 0; + virtual void resetToDefaultBindings() = 0; }; } diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 477ffe0e2..5bcec9f70 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -116,7 +116,10 @@ namespace MWGui getWidget(mShadowsDebug, "ShadowsDebug"); getWidget(mUnderwaterButton, "UnderwaterButton"); getWidget(mControlsBox, "ControlsBox"); + getWidget(mResetControlsButton, "ResetControlsButton"); + getWidget(mInvertYButton, "InvertYButton"); + mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -155,6 +158,9 @@ namespace MWGui mOkButton->setCoord(mMainWidget->getWidth()-16-okSize, mOkButton->getTop(), okSize, mOkButton->getHeight()); + mResetControlsButton->setSize (mResetControlsButton->getTextSize ().width + 24, mResetControlsButton->getHeight()); + mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); + // fill resolution list Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues; @@ -220,6 +226,8 @@ namespace MWGui mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); + std::string shaders; if (!Settings::Manager::getBool("shaders", "Objects")) shaders = "off"; @@ -383,6 +391,8 @@ namespace MWGui Settings::Manager::setBool("misc shadows", "Shadows", newState); else if (_sender == mShadowsDebug) Settings::Manager::setBool("debug", "Shadows", newState); + else if (_sender == mInvertYButton) + Settings::Manager::setBool("invert y axis", "Input", newState); apply(); } @@ -521,8 +531,8 @@ namespace MWGui std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); const int h = 18; - const int w = mControlsBox->getWidth() - 34; - int curH = 6; + const int w = mControlsBox->getWidth() - 28; + int curH = 0; for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) { std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); @@ -539,6 +549,7 @@ namespace MWGui rightText->setTextAlign (MyGUI::Align::Right); rightText->setUserData(*it); // save the action id for callbacks rightText->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onRebindAction); + rightText->eventMouseWheel += MyGUI::newDelegate(this, &SettingsWindow::onInputTabMouseWheel); curH += h; } @@ -558,6 +569,29 @@ namespace MWGui } + void SettingsWindow::onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mControlsBox->getViewOffset().top + _rel*0.3 > 0) + mControlsBox->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mControlsBox->setViewOffset(MyGUI::IntPoint(0, mControlsBox->getViewOffset().top + _rel*0.3)); + } + + void SettingsWindow::onResetDefaultBindings(MyGUI::Widget* _sender) + { + ConfirmationDialog* dialog = mWindowManager.getConfirmationDialog(); + dialog->open("#{sNotifyMessage66}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindingsAccept); + dialog->eventCancelClicked.clear(); + } + + void SettingsWindow::onResetDefaultBindingsAccept() + { + MWBase::Environment::get().getInputManager ()->resetToDefaultBindings (); + updateControlsBox (); + } + void SettingsWindow::open() { updateControlsBox (); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 61614da01..aad37826e 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -66,6 +66,8 @@ namespace MWGui // controls MyGUI::ScrollView* mControlsBox; + MyGUI::Button* mResetControlsButton; + MyGUI::Button* mInvertYButton; void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); @@ -80,6 +82,9 @@ namespace MWGui void onShadowTextureSize(MyGUI::Widget* _sender); void onRebindAction(MyGUI::Widget* _sender); + void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel); + void onResetDefaultBindings(MyGUI::Widget* _sender); + void onResetDefaultBindingsAccept (); void apply(); }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 09ed50944..9f757c686 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -40,6 +40,7 @@ namespace MWInput , mUserFile(userFile) , mDragDrop(false) , mGuiCursorEnabled(false) + , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -280,6 +281,9 @@ namespace MWInput { if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y")) changeRes = true; + + if (it->first == "Input" && it->second == "invert y axis") + mInvertY = Settings::Manager::getBool("invert y axis", "Input"); } if (changeRes) @@ -374,7 +378,7 @@ namespace MWInput if (mMouseLookEnabled) { float x = arg.state.X.rel * 0.2; - float y = arg.state.Y.rel * 0.2; + float y = arg.state.Y.rel * 0.2 * (mInvertY ? -1 : 1); MWBase::World *world = MWBase::Environment::get().getWorld(); world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); @@ -504,7 +508,7 @@ namespace MWInput return mInputCtrl->getChannel (id)->getValue () == 1; } - void InputManager::loadKeyDefaults () + void InputManager::loadKeyDefaults (bool force) { // using hardcoded key defaults is inevitable, if we want the configuration files to stay valid // across different versions of OpenMW (in the case where another input action is added) @@ -530,16 +534,27 @@ namespace MWInput for (int i = 0; i < A_Last; ++i) { - if (mInputCtrl->getChannel(i)->getControlsCount () == 0) + ICS::Control* control; + bool controlExists = mInputCtrl->getChannel(i)->getControlsCount () != 0; + if (!controlExists) { - ICS::Control* control1 = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); - mInputCtrl->addControl(control1); - control1->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + control = new ICS::Control(boost::lexical_cast(i), false, true, 0, ICS::ICS_MAX, ICS::ICS_MAX); + mInputCtrl->addControl(control); + control->attachChannel(mInputCtrl->getChannel(i), ICS::Channel::DIRECT); + } + else + { + control = mInputCtrl->getChannel(i)->getAttachedControls ().front().control; + } + + if (!controlExists || force) + { + clearAllBindings (control); if (defaultKeyBindings.find(i) != defaultKeyBindings.end()) - mInputCtrl->addKeyBinding(control1, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); + mInputCtrl->addKeyBinding(control, static_cast(defaultKeyBindings[i]), ICS::Control::INCREASE); else if (defaultMouseButtonBindings.find(i) != defaultMouseButtonBindings.end()) - mInputCtrl->addMouseButtonBinding (control1, defaultMouseButtonBindings[i], ICS::Control::INCREASE); + mInputCtrl->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); } } } @@ -597,6 +612,7 @@ namespace MWInput ret.push_back(A_Crouch); ret.push_back(A_Activate); ret.push_back(A_ToggleWeapon); + ret.push_back(A_ToggleSpell); ret.push_back(A_AutoMove); ret.push_back(A_Jump); ret.push_back(A_Inventory); @@ -681,4 +697,9 @@ namespace MWInput /// \todo add joysticks here once they are added } + void InputManager::resetToDefaultBindings() + { + loadKeyDefaults(true); + } + } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d3d4d1385..42c90a73a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -81,6 +81,7 @@ namespace MWInput virtual int getNumActions() { return A_Last; } virtual std::vector getActionSorting (); virtual void enableDetectingBindingMode (int action); + virtual void resetToDefaultBindings(); public: @@ -132,6 +133,8 @@ namespace MWInput bool mDragDrop; + bool mInvertY; + bool mMouseLookEnabled; bool mGuiCursorEnabled; @@ -159,7 +162,7 @@ namespace MWInput bool actionIsActive (int id); - void loadKeyDefaults(); + void loadKeyDefaults(bool force = false); private: enum Actions diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ee4855f38..5908c16f9 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -84,9 +84,16 @@ - + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index a723e307c..fd0c6e7af 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -144,3 +144,8 @@ sfx volume = 1.0 music volume = 0.4 footsteps volume = 0.6 voice volume = 1.0 + + +[Input] + +invert y axis = false From 67577c61924860e1c67ccb303ef17149e42f9f39 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 18:48:50 +0200 Subject: [PATCH 413/688] UI cursor & camera sensitivity sliders --- apps/openmw/mwgui/settingswindow.cpp | 14 +++++++ apps/openmw/mwgui/settingswindow.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 23 +++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +++ files/mygui/openmw_settings_window.layout | 48 +++++++++++++++++++---- files/settings-default.cfg | 8 ++++ 6 files changed, 85 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 5bcec9f70..4039136a1 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -118,6 +118,8 @@ namespace MWGui getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mInvertYButton, "InvertYButton"); + getWidget(mUISensitivitySlider, "UISensitivitySlider"); + getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); @@ -226,6 +228,14 @@ namespace MWGui mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); mShadowsDebug->setCaptionWithReplacing(Settings::Manager::getBool("debug", "Shadows") ? "#{sOn}" : "#{sOff}"); + float cameraSens = (Settings::Manager::getFloat("camera sensitivity", "Input")-0.2)/(5.0-0.2); + mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1)); + float uiSens = (Settings::Manager::getFloat("ui sensitivity", "Input")-0.2)/(5.0-0.2); + mUISensitivitySlider->setScrollPosition (uiSens * (mUISensitivitySlider->getScrollRange()-1)); + mCameraSensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + mUISensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + + mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); std::string shaders; @@ -510,6 +520,10 @@ namespace MWGui Settings::Manager::setFloat("footsteps volume", "Sound", val); else if (scroller == mMusicVolumeSlider) Settings::Manager::setFloat("music volume", "Sound", val); + else if (scroller == mUISensitivitySlider) + Settings::Manager::setFloat("ui sensitivity", "Input", (1-val) * 0.2 + val * 5.f); + else if (scroller == mCameraSensitivitySlider) + Settings::Manager::setFloat("camera sensitivity", "Input", (1-val) * 0.2 + val * 5.f); apply(); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index aad37826e..159d52bdc 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -68,6 +68,8 @@ namespace MWGui MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; MyGUI::Button* mInvertYButton; + MyGUI::ScrollBar* mUISensitivitySlider; + MyGUI::ScrollBar* mCameraSensitivitySlider; void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9f757c686..febe528c0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -41,6 +41,10 @@ namespace MWInput , mDragDrop(false) , mGuiCursorEnabled(false) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) + , mCameraSensitivity (Settings::Manager::getFloat("camera sensitivity", "Input")) + , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) + , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) + , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -284,6 +288,13 @@ namespace MWInput if (it->first == "Input" && it->second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); + + if (it->first == "Input" && it->second == "camera sensitivity") + mCameraSensitivity = Settings::Manager::getFloat("camera sensitivity", "Input"); + + if (it->first == "Input" && it->second == "ui sensitivity") + mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); + } if (changeRes) @@ -367,8 +378,8 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - mMouseX += arg.state.X.rel; - mMouseY += arg.state.Y.rel; + mMouseX += arg.state.X.rel * mUISensitivity; + mMouseY += arg.state.Y.rel * mUISensitivity * mUIYMultiplier; mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); @@ -377,8 +388,8 @@ namespace MWInput if (mMouseLookEnabled) { - float x = arg.state.X.rel * 0.2; - float y = arg.state.Y.rel * 0.2 * (mInvertY ? -1 : 1); + float x = arg.state.X.rel * mCameraSensitivity * 0.2; + float y = arg.state.Y.rel * mCameraSensitivity * 0.2 * (mInvertY ? -1 : 1) * mUIYMultiplier; MWBase::World *world = MWBase::Environment::get().getWorld(); world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); @@ -578,9 +589,6 @@ namespace MWInput descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; - if (action == A_GameMenu) - return "Menu"; // not configurable in morrowind so no GMST - if (descriptions[action] == "") return ""; // not configurable @@ -619,7 +627,6 @@ namespace MWInput ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); - ret.push_back(A_GameMenu); return ret; } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 42c90a73a..79e5a70fc 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -135,6 +135,11 @@ namespace MWInput bool mInvertY; + float mCameraSensitivity; + float mUISensitivity; + float mCameraYMultiplier; + float mUIYMultiplier; + bool mMouseLookEnabled; bool mGuiCursorEnabled; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 5908c16f9..ad906d2a1 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -83,19 +83,53 @@ - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,11 +169,11 @@ - + - + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index fd0c6e7af..effd9f3da 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -149,3 +149,11 @@ voice volume = 1.0 [Input] invert y axis = false + +camera sensitivity = 1.0 + +ui sensitivity = 1.0 + +camera y multiplier = 1.0 + +ui y multiplier = 1.0 From b373d0ec7bafb013032a05d88e8f183d90348242 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Mon, 13 Aug 2012 13:53:54 -0400 Subject: [PATCH 414/688] Correct struct to class in forward declarations Fixes http://bugs.openmw.org/issues/362 --- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/inventorystore.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 6ab92319d..3c3b0e34b 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -21,7 +21,7 @@ namespace MWRender namespace MWMechanics { - struct CreatureStats; + class CreatureStats; class NpcStats; struct Movement; } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 45b3cab26..e55eca0ae 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -7,7 +7,7 @@ namespace MWMechanics { - struct NpcStats; + class NpcStats; } namespace MWWorld From f9efd543e438cc832ec5b695cc901d3a1f43ab96 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Aug 2012 21:33:53 +0200 Subject: [PATCH 415/688] use float for the mouse position tracking, should be more accurate for sensitivity multipliers != 1 --- apps/openmw/mwinput/inputmanagerimp.cpp | 10 +++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index febe528c0..0c0bb74e6 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -378,12 +378,12 @@ namespace MWInput // We keep track of our own mouse position, so that moving the mouse while in // game mode does not move the position of the GUI cursor - mMouseX += arg.state.X.rel * mUISensitivity; - mMouseY += arg.state.Y.rel * mUISensitivity * mUIYMultiplier; - mMouseX = std::max(0, std::min(mMouseX, viewSize.width)); - mMouseY = std::max(0, std::min(mMouseY, viewSize.height)); + mMouseX += float(arg.state.X.rel) * mUISensitivity; + mMouseY += float(arg.state.Y.rel) * mUISensitivity * mUIYMultiplier; + mMouseX = std::max(0.f, std::min(mMouseX, float(viewSize.width))); + mMouseY = std::max(0.f, std::min(mMouseY, float(viewSize.height))); - MyGUI::InputManager::getInstance().injectMouseMove(mMouseX, mMouseY, arg.state.Z.abs); + MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), arg.state.Z.abs); } if (mMouseLookEnabled) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 79e5a70fc..c8b48e727 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -143,8 +143,8 @@ namespace MWInput bool mMouseLookEnabled; bool mGuiCursorEnabled; - int mMouseX; - int mMouseY; + float mMouseX; + float mMouseY; std::map mControlSwitch; From b1bd2aab7423c6a7d46867bd1c7bb70655bd6c6c Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 14 Aug 2012 02:24:46 +0200 Subject: [PATCH 416/688] Added font creators and a formulae researcher to credits.txt --- credits.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/credits.txt b/credits.txt index a0be85392..ca0ff7323 100644 --- a/credits.txt +++ b/credits.txt @@ -61,6 +61,7 @@ spyboot - German News Writer Formula Research: +Epsilon fragonard Greendogo HiPhish @@ -101,3 +102,12 @@ used as the application icon and project logo. Thanks to Kevin Ryan, for creating the icon used for the Data Files tab of the OpenMW Launcher. + +Thanks to Georg Duffner, +for the open-source EB Garamond fontface. + +Thanks to Dongle, +for his Daedric fontface, see Daedric Font License.txt for his license terms. + +Thanks to Bitstream Inc. +for their Bitstream Vera fontface, see Bitstream Vera License.txt for their license terms. From 9501e133f5c840836c4a79881ddba6efc140d7ce Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 14 Aug 2012 03:23:57 +0200 Subject: [PATCH 417/688] Updated the EB Garamond font to a higher-resolution version --- files/launcher.qss | 9 ++++++--- files/mygui/EBGaramond-Regular.ttf | Bin 231904 -> 405476 bytes 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/files/launcher.qss b/files/launcher.qss index 8be235f71..1eb056d4d 100644 --- a/files/launcher.qss +++ b/files/launcher.qss @@ -22,7 +22,8 @@ stop:0.9 rgba(0, 0, 0, 55), stop:1 rgba(0, 0, 0, 100)); - font: 26pt "EB Garamond"; + font-size: 26pt; + font-family: "EB Garamond", "EB Garamond 08"; color: black; border-right: 1px solid rgba(0, 0, 0, 155); @@ -54,7 +55,8 @@ } #ProfileLabel { - font: 18pt "EB Garamond"; + font-size: 18pt; + font-family: "EB Garamond", "EB Garamond 08"; } #ProfilesComboBox { @@ -82,7 +84,8 @@ padding-top: 3px; padding-left: 4px; - font: 12pt "EB Garamond"; + font-size: 12pt; + font-family: "EB Garamond", "EB Garamond 08"; } #ProfilesComboBox::drop-down { diff --git a/files/mygui/EBGaramond-Regular.ttf b/files/mygui/EBGaramond-Regular.ttf index dde4869030022bf4d25ac5ea4403795ebd864fd1..3f6f6c191d9d47870201c2979caf8acbb41a7e63 100644 GIT binary patch literal 405476 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%0Dqvm!AngL*}zW3O_YU zEmc@6_nI*^JHpR{W_@ekF~fD&qX!&JQBD{IMxh~Utzh18IW%w4XPUAZkYf%ZRtzNN z8=*hJpO3jb-l`qqtuz2JHYTe~`APX1F*2*bO2%;b;^{k~PbrvXNLcF->l4_0C7*;j zN(=l=EZGdDy8=#JqLd`S6W1R z3;Y~q3OtB4k74j>v>z?-2)~>jPVs9=;(LbLi!ZUujJ1(JbJ1g(EcRGMoG3-q$1SJ> z&JK7!6<0n-$V_X-olh+=UrFhO{>gz4j+TGSkMN`X7(W5-avJ-J5_T3fvBoO-@&4Bw zVi6KKRKuAoG!l5zlL-C0c|_vd|9T#;W54`-OU}1j^vn5n|7W$(w0=l!wp!D~2tHr7 zruF!{kf7#Y+kGLPMf=Z}hiwW2`7uo!{A+B~X6dB*nfir#QvFUngMp`MHqEUCwRkO6 z>!KBCWm>h?U2D|(YpvP{ZHzWio2t#$7HUhiE44M+I&HnSN!zU5r)}4EX?wK;+9B;l z?XdQa_L0`1eW`t`oz~9kl5Sw-&x@75L_J;4(Tns7y;g6~oAiPDP`zCrr%%?W>vQ$R z`f`1>ew}`kzCpiJ-=g2I@6dPa`}BkQv-->W8~S_tC;I35*ZL{_M?GXHhShKxek0aM zHZqMoqr|8*>WyATGiDxb#%N=LF~yi^%r`DKRvK3u*BiGO8;!e+t;RNEr?JP_Z#-o@ zZ@g-}Wqe?KW_)3sG`=&=SeQk#*eq^K&=PM+wREu*SjsHbmhP5DOMgqNWrSsnWuj%O zWwvFZWvS&#%Nol%%X-Tu%Vx`cmhF~Zmc5n(mP3{oEr%`dSU$3RZaHZ=Z3$U5tHbKI z##_^^dDb#(t+m10WF2T7YHhcUvre{7x6ZdNwO(mmV_j!mZ{1|wY`xFA!@9@1-};pG zdF!jzx2zvnKeK*eJ!$>UdM1KJXc4vucSJBEJ|Z=uOGH6LSwwY2_lU-b{t>MaBO=B` zOpKTsF*{;m#L|c>Bi2N$i&!7ADPl{+wuoI3`yviTJR9+9#5)lmMRY`b8F4D&$B2+k zu~}^{o8J~|OSN^e71+vb)wb@oM%zH!P+PlgoNcmgx^1p)v2D3+we33FO|}iTJ8fHR z_uF>b_Sz2E4%uF`9k#t=`^eT|`_lHU?X)dq*X$0v-yUmEwrAS&>}B>^dxO2nKF~hY z-fkafpKPCQpKD)iUv6J*zs`P>eS`f@`&RpQ`!4%l`vLnQ`-}F&_IK9sM1xjuDP=jwz1Wj>V3Zjx~;(92*^XIkq~s zId(esIQBc9ay;)i?0C=diQ{v}*N#(;9~~j5=5#py&Uk0KGtXJ#taR2pdpVn(EzUOQ zXy*jy6z5FmeCOrPmCiNJo17b+o1OPLw>x(^_c{+a4>@0S9(KOt{K(ni{L=ZY^R)A< zOL7@5hs*1Vb|t#fT{*5ISB0zA)!=G!4Rj54wY$c-CcCD)=DHTUR=U=>ZgOpOZFb%7 z+UeTsI_P@db=dWu>oeDvu2Zfvky4~J(j6HcnHZTKnG;zQSrJ(q*(yCCO zy3^e`?jm=EyVl*{ZgLNF4|TV@$GIoFr@QC67rU3cSG%us-{julzSF(MeZPB$d$)U^ z`=I+-_si}#-0!(RbARbReGEc3im#5j&;u+x?=b7S}?OEtq z>RIi%-m~6wr)P`je$P(NUe7_#^Pa<=_dK6?KKGpToc4sgn%Cj=dt<%H-Y(uEZ>6`p zx5?Y$ZS#)yPV`Rq&i5|$uJ&HTCB+@J;p2^ATvu&bPsLm+wB`4&NT%e&18R z=Y6mG-tv9m`^@)+@1*ZL-x)v5VEi_}*B|Ro_ZRpp{SE&9{x<&@|78Dc|K(JP8v;_elBnS&N_+gs{_y}Mx{sLelrEH{>jg+#H(j2Gw z;}n0K;*V4OAnevktcBjT(AySz+Y*i!`M1&AHhSAeZ`O$W((YIab+a~(93)Zipqw*&JvnhRF{lHgc#A$EglEl+SU>Cx^;yrgC#A&s>oM>`6pV&U-@{Hd@y661A0 zV5%ZUOao?3V%&)MMvBj*_(qD)q4;Ks&!PBciZ7!07K$&T_!f#UrT8HfUrO;qD87Q? z+bF(*;@c>`jHq%b!E%Db2xb$8`VxkKRjQ}p6PZMVji5nbD~$b?F!sS$P3(!)d893( z?^-}5BCXjoE#aQ2rk-gRST+|dgl#lwV?epYDrI`_cZLUNvtOq zvLy{WZo&_1W+9`{&$5sQ)YBRCO>g=pgTCoa-(=7?z449Uv_h`+rV*W?yhZ+(kq5Z0 zBIvvkyBvbfMOaXm2p>hNOiJ6xMZaV!?*caR*C^d981qPnItVVEMRn*yb?8DEZ=$h~ zP4RsxKAY;+m+F>Hvac^mmTan9U#eTS@-}ko%SCN-sJvz>4|0~;44Fpt$|FkePwmg6 zeEUNM#gK z{2+=ir1(MMc+p3LD9=L5a}e?r6!{F{v<1{Ec#Q}@hbO{lzX+Rs&=T$g5f*(Q!lDm~ zsZN8bPQ_IAU@E(m{6kDX)nf>?wKUvTgs82h)Yc(XuQK{(D1B2#-wdU1%COfZ>N*rp zv7ZEK5DtHBhLI<|N1kGImQy~%Oh4r_jBrp+WeuaU$_WR<2(J~&>-egT3%#L&`n3)H zD*S>L+XzpPF4XR}FrFY`2v2Rw7epsrN%{;Y>C=_U98P6c5_cRyI!qZjP`QMCzZZ!pl?%Q?t!F{*?@gRK_4RgVK{8co~Ok50`cBx z3BMOKEz%zoVf4*YB8T>fMNX3w|ItQzQCA@C~yKjo~(kuxNt_n|#G=1Hx3McrVHn??oFj!)-t* zgy$S8=Qx#OTL_2Hx2er7;qpaTlrO@fe1xgZ2vhmuy(nM27v&>g z!Xa`N<#W>0c~?P4@MGQpKQwd>OI`A#{fI~V5fA+u@!-|)3yhE-G;R7t9os}ZIMF7r zqCAOa;TL&UQl3@eG$PL`%9H36evwZ#(Re%2cs1dm-HazZwG*DIsXyB3+Yt6_Bp$*l zpUgv8?-FaCA%lmX%#g!j^QrRJDXifMxk%rBOy7PS{`TYWw<0XQ5n=Obe*3XO-%41S z6H-xpD@o?Jcv86(#y1p}QO*%6=LnT^BwWssa5*9@@)u#Gl@!WXp?sBazDhV>5f=H1 zu=%7qA#AqiD3x=R$~hV?=V-Vb5fq!sXh+<%+N?Pf$50sGJkw za!!QH5n++P2n#&ADPK3`>kjAZ4(BVvB3}_UpJsd9;r5)O5>AD47Eh7$sc?<`l(V05 z_J?!!hjSKTk)sHkPcvtKIOo&Ug42}$Y4cltI$Vwji*iKRe43a#9sV|?hXkdQZY2r- zFFKjnLa_y=i^|fKyO9e zOrNNo=@Zl^MoK>AC7vQLJi~bjpP82#Cvq_3&il-~L>%RXQ9$*gC-M?cGcWQ{Ug0?8 z5RN>yppII+|xL)Fkyd*rs^%6d_ zUS^!AjTv{|XVy!^ne{pyu9tWsFZBEQdI_IdFEb7~&|9-!BF?Or@QJ(x|AYJj{EOL_ zCZ%Afh#hou6ljT+P`A@JF)|;?9_r5S`;GS^;Vmbq7S+O*xC%ipcr=ti>VPC^>+6kwUZ(GZ-r66qRy{ zN;yTPm~)tKkXGbP`Op(JG)gE}^r}cHdK@{4kf^<%@?-WUU4y+%C%ci|ihcYI*q4jO3DJ95BKsrkrn1>=!KKFprM z?*H@bL)Mdh0;}WU>}&Q7&SU-+wo((>&z!TVT;Ucr2Uf^&Y!Od{{oS=V4>5#o=EHb9 z+sQBDm#{s28cxSNiF15!;N|2SqeyT+$t@Tqj@CG-7VmMk{2l#NjmEK z7MrZDRW%76>dopcYQ5S`?XLDv8`Pd^FSWPYsP<8B zRoAP(Q#Yu$sTYeKE)w|TY)y?WX>K646>Q?n$^*;5F>iy~i>NfRH>UQJIfmb*K6lb(i{(x?6o%-J?FD?o}UE_obktNc~v-MEw*wh*eMn z`@dt+0~6UK*nG~xZwy`A0EG4YvtMD0+Is-LUJ zu)}s-{Zjo({aQVNy|!=EZ`IN2Md}!JtU68|uTD@eRwt^H)W52y)W2aT?tAsL`h)tT z`jdJ_{aHP$o>N0AjE*&~Nt&!Fnu>io9Rk;)S+xl4*x5CQ=G0tTB=+t+npg8_el37q zyeKVNi_v1WIPB*oXo*^qmaL^}l*D|zBEem^m*;G+SS@M*wwsNyG~oHU9a7M{mmP-o3xv?TeMrT)A>7XgLWHo zxcq;BiH+Lr*fG6ByHoqUc9(XywpqJJ+oJtJ+p68G-KRFGebr{QpW0s?pbk_AsV(YY z?T_01+5_4)?N8Wa{j;`1dr;e{{RO+N4{5u#hqXQ0BiMI+RNJRLrtQ}r#}4ch+Cl9} z?J4bP?8QE#J*z#ZJ+HlhUD=nkm$g^4SGCu$Kl{4&hW4iRmi9JwYTwn~)85xU&_2YT z?Z?_D+NauQ+7ayD9@ReAj%i3R$dSXcDi-4nTpaQ6DObVTH3Gb&kFeCh8W0Uw<)L zorEt?o+?g=pU+o>`(6;nS~l~llhsSqDe9%_RCSsN0h?xs0UtORsR2Qj>kse>8q**W{ zyBv9izy7V1|4G>*kN>Fb-)IfW<|t_uVb;wwqtqxf%8d%6t0Al`G${l2SzTe9HC$Ri z{UWRf97rwj&0!P6VH5Vxr-X9R{$I}tmHg&Op`Oe~>fc-^K1`_pmMO50H`fvj^CpASEA!75gsOxb4NA z1^Z#?b`X~AhuE`_moKuHVaa}&y#YD)4y@lkfUWT->@!#!e-0`7C8X#{$k4yB?{Oc) zPwZ!Qj&qIoRN_ayQpp3GBmjy@B%?m0MrSilQ; zF)xK3`a+=k)i*@h`Z3@Cymkxd#C@`yZ^WKZUuVn!rn~}b4OT_ z;SIxSKF%bZt=ovFrrZr^QiP`5Lw?cg8Z=xHUjhCAj$+gp!eYw&>Q`QC+IZnc4*bO0 zj@z_VV+|Oq93$>FSfz^*7f)j@5u@%8usyvOzcdwz6BdLL%LQNg#~SB~ zl*&TDG}N^hYXk@Sd%n{K{rEiBxf9y-W9&7Yp#PT3sGpMucsB057>zS{8-5vwqBKo@ zGMqD6AR66`9jiottg?#r61`L})5~F>)m87ISLxMy4J@_l^m@IUz5*6oJ@sCC zZ@p3P1KX{>db8e7@2?Ml71toWMIWrU>O)}9HB4{QhwCHsk@_fow0@C3Mjs0cukrc> z{bGHhJ_)v7m*`XUOZBPxG+2Gj&}Zti^x66x*niE_=j#jfh58~`f?cLxt}oG->dRmg zwo<=BzfzCE%>fqJd_@>G!)`cm!elV)zakB{;lX`0K3IYUjGz%^TnyWQTL_PxY$-HK<0Z(Q1qutH!DEYJ!?*^iz}6WHm)iRnydTHN%)_Oftq9<6%AK zHlmFfSeL~a@kWA?Xe1fQuwrZ1SLyLaijiuh8R>|ORgW`!SPEFED-*)h=hSL_7lhTpPNII(pa zw+x+OXITiuCgai{om(&l>=*@++{1ks3sF3V$MFP=*%Y3}Gk6w8M=sCD4KXD+!&)xv zKKX+%W5?;%)jy{dRqmeg`ZPf3M%A->q-f?}2UNAM~yIz50FnA7Q2VfWA%t zlfGU5Gwc-~)OYHC(Rb+&!E*6oeUJW#zE^(~HjIzy`}N251NsxNW_(hAN`G2Eq(1|@ z#^?0s^%wLP^_O7b_=^6j{+fPRe;u}tZ|ZO9Z|m>q@51Wwef* z(L3~`u!KCOf1w}Oztq2iP2>svr2dWmt^QY7NB&L!PXAs%t^WW!$)EHy`p^1V;}Tdy zUTRD=rWw_uV}dNBTkCGv}~Nj`)-f1cwn;ohHDa3{iRI03LL+mho!av2ih$A?)eH1q% z9m9PQ$8k#HE8LNEf}iBy;3W57Vb%OM+#vBiY@2`JKhpWUr++zd_dNCnU!-$-ufUf4 zHRUkbmB0C`O*t&izY#hj>|ygk?~T|q`8zvyMOZ}%n{GwHiWm7LNJSx4pT)@*7v$q= zaFhLzw+_r^;&CHD4t9|aK(6+I9na(RY!*-D1Z!6IBaZ>fV}Q-dKEOUW8{>j@nP`58 zea^o!8IpZ~f*oHxo0TU-smdw9p~`NI#B$t0Bl6e<*o+$3Fpr9b=8*ZHm4)(!to;Gs z37)HfcbPtw^+B#D!q)y`o+z|o1$TI1haEm~!p77RyoibBHz7vc5g^`_Af^^HDo)D$ z)@kIvz({}m5}-qvXOc(aY|SYAx?sk#6z6}$eP`mHo;z7LaPNDN?mo;pTEVGzVP>-j z{CNzp`wE*t^NjhJX}pD*&O7)m#(d)w%y>S-Z#m{19hmWaf!|7S=f7g+^F4m6(B2== zLqFkn6=oo3F%Rm*dfpn$LhNiUW*W3}%h_68q|TPNPcJt))I+k6I}&)(;Y_+oa1FTve8NBIiAk{#o# z_-b~XU&Gh1ulQPiJv+f~#!Wik@Z0#^>~EOO>||%@F1d5S{|T;O_Hv4=Vg|!C%wIy> zz^nx)C@^oaa2sYUPVRz*_&F|m;{W0O{{-I6k>JOOh~>9PpV3$lGsyop7XD9;h4TM^ z2VHn0!br?&1oz1y?vqR0r-Zmq4{)FD;8B9_j3vG^k@(IO;yafU-&sa{XF2%JQQ|en zv7RC{!L{HtXOWBGEE|Zk+)kY355!p>AkOk9;w+C6XW37j<$2mcc!_w*hs0Bk5KrkK zp7J^IlrM>=oFbm`9bXQfa+-L`PsCGxCZ2MRcuJ?>DY&X1ZUw6!CAOdXn5RD zoFzz{C5kvp3~`ot;w%ZoSyI4RLOcVUrS!jq%~tSd6Pvk&%{;8%9_@rj2jS62 zcnlC8gM`Ou!ebop=;ZO*X&r|N!_rbnQganRtPcqOAf#>+=G=p^V)u{pZgI<|nMQ+5 zcS3IFvU}Z#-fcs{_OtKj}`i;spA38bT`K#ff>b zeql#A89xK^q#mc)dg5n;+#i59qfx$_U5X#1B-RN1xS3-k+O!G#Xi@0lyRe3F59D<$ z=2-V5j|cHfp?Os*E^)_Vx`}k#JX7wRP;>Y;qVutl8 zG>9Yk_pl+jfov}uiu;51v0=DB=w;T1 zTRLB5!*Lf`2OEK#gg$2@r7xu~S-W%ww`z^TeVsNoTK36)c99$<$FZ?;lAOdQ$|-UN znnKkE%#>gJW3wLE|)KsFJ?>ROXN$~ zQu$K(QnpN0!}4aAy)9Tua4l3TE{;9-M_o?D5YhtP`PP^>Vduwg{oZCGi@V8gN6ki|w| zy&;#4#F|4sYsb1n5gUcIhY~g#^k2pOc%uIaME@5P{ZAzNpG5RO zndtu#qW>vG|CbW|PbK=FM)W_O=zj*$|4gF)Sw#P{iT>vh{m&)(pGWjRpXh%9(f>lC z|3yUqi;4a(Bl^Fb=zl5E{}n|4R}%eSL-fCf=>J-x|LchU*Ao3-PxOBS(f>N4{~L+^ zZzB4?ndtu(qW@co{?`-z|BmQ?1JVC&ME@I!{%c(p0YU$`Wsz1x!Kh@M`V`%NA+L5Z6}K#Dx{mXp>OYJNu`6-m z$<^)%OW_mQul`%B2LH|)-2Y^yK}eMU8+M)kf7o+EUx~X9&i5F@>OeDJC*(5rO=G|i z1LjlS&&I(lQj(Y|c2V)SoUwZcGH4Ouzkw5bPJrA_40WonoWot|dV*94MjF`-@8ue` z0gptwwLMq3!>K3e;J|MI4y;RlFU3d+ zQn6HyTk!Tv&q&WnAIbqa6QiuV+*4jHuaU2l@0MSd-%u`!dp-V+_@Cl`PVgnfCGDb|vgfcq-xbg!dD^N%$@yl;}^4PK-}XN=#2INvuq)No+{$lejG@ zAt@z2Dr3^UgYF%C@34D^-#haY%O|!^9G}F0s(xzu>YlIeJ^A#>=fC^uXD!r;+jd0z z#Ja&&)Ob61`{VpBtg1eYmD3+_duV#N#)nYjcVOIL#AjjA10HL7w{YU*{V*QTyXU7B)R%I|`m!I1A$--pgWTmGnu+X?<} z`_m+=s4^=4k98L-TIS#RXE^vz5A#czG!Kn*<=?tp>Nji{{^_2nf4n2~Ki~bYh0b1& z)v)fY$G^2NGMZfky?rd&By0heVWui<;BEo0-2m-<6Lw4Pz)s2C7&T(2vK>1mJE1ROk3?C5*@C#SZxY}f%m6NBm%ulRO$HPYeC@yfvoO zmnrgEDNUUWO*WUs*T>{y<+*}+qiOUp`7 zXr5G>_FJ(Td_hKOY1|wms5>}Eqe7=LO3Qu?%Un5~mYm&fLMY@(%J*dE*?i?iId;cy z#0h*sZf$~u>VvuMqP{!41Ka8|Q8mscIwB<1YQUsg)rPs8Hd#^l0J|)4?x?eH9ar4e z$JD2rNkudzQMuA~;dkML!|UUknwt7G^=t0ir+1HT^|h51B}Lxkw4~Iu6n}E0FB|{m zLU9qaCC$l`%7x$Wz)l<JZ_gduKJdZ0yJKof+Xp`1TGXecr>sCFnV&9u%woCm z(`8v>d)}Cl5=`M0=_$d~RiS6OxbO;8+pE(i2h?qthjha_dQp9T4;}cbDhd1HN=jAAfxOaJ zZ+7t9=or~?ao@_+nAkv!(=oUvWABJ(?oH`2sAXJyU4O-4L|aoe<&~S`bDC~9Z=YKH%OGl~%dcniP{)*mY2LQC?3Qe|lr%%$q(qdpfkjzM`d} z2cN)Ote5n3LVvDVJ_)pwQC7zR;TjVfd{>|oy=F`&RgENNhCR3B#LP@k|> z+J|f;YkDt}!<}#U0`mcT_ z*AE|FADfZxbW~TCmE>lp7iJWuCdbG6JdP-5RC=<_k!|6EXO#0=2|Xec1)H=fd;P^l zWu+zQDajiD7xBuwXYa}`^r>DsJtnCn7;URsu=9bX3+~=pG_kSI#nYP_#+(~VF`MuG zB}Td7=T%*sqLr+aw6ubASFfJB^3m;!X51D!);M8yzlNccs+d$$x>|$MJT^U4DY)m0?9I%3j16Y(nVsrEa}4(F3XNO z+_=MRb+GE-xTFWeS9FRS$y0tiITEq~!h9WTRwAijJ^ziIa(_g)xvkBt{59-~kytJe+DquBm8n&;+maPiWlL4-7 z5=Oci;UPMp zj-GXUXQ`!$3$ZrS@L&^6+7$xZv*a$|s^-ePb(nR3>_+iTkJ^Pk9XFX)FuY`-EiYwTR_H#epQv zO_3bgp0LDp=!C{nU@A|_n37Ri5vSTBytbevz=hz1DgbLnl9Q2{c^PGiT|K27;xfC5 zgr(r$oVDY^tv2;Jtc#YJe8~p(&4{{PpzeCNkbWFXis4wsdkn9);;up;!-9@%%jS^i z4lYXbdELar!5c)6K)-O;^71k%uFBKJIcR!&&W?S-`rU2zv3GOzade66}tH72)_Aj$8~A ziN!r%yhnOkUVe5@&7IA?f~1R!0zxA|*V2=4nI|JGnY~>Cc`1elRHqUl5Ag%bEiW@w z3jV2AE3BM8V$|sREK997GCH@LO~&Glp~=3K$h7jf!hxA-se$y|E?F@p*@oKNZrE&^ z?9KHB{K{+TV>2CYMb3SObVPA^zhrl!2M05FPGz3hCu!%CrB|eE z3|Px<6h6ww?b*DHdR5hg>QXM<5S^Zq6soT*%}kZDrI^Hw8lI*)<5K%60*{?P;{U!m zi5vK$>iy>j9;}h;Im0>|{=c~MwG>hq*&XlFt#47+#RO7mw$AT6H%m{d zn=m-i`tFDVw~r7#GzsIwE`~K-3pi0!ZunoalEG9=k&Y1p-5q2l?K`(As?cGRt-4g{ zas;D1Q4#9UGpo9K5)&iUtn}Dq3%VS#v9=d3MgCXAeHUc*&#VHx8Jy zQ=U~fW&NO~pRQT@+PrBmED3d6wD<7JerpH6foh;-37u!9fIJSX=vAhicS5HU5*xN? zru3~3s3gK6yUcpw6}=h$a8goUQc{K>xk$(kbQJK4st|U?aEmlW5%7Ycc3u|oSsCfW zdj;1zz1IcOl1HRxW~TS=6}-afU4r1i^b~1Ds6D%1M08A+%@&M|iH_)(&9@*T5Kj^D zfe2x}($aYrE5b7{qb{v4f_9Am=0>mz%vRKKz@RdKM6JeE^*l1s+(}IQxYeyI6Jm89O>DslH-i~CS*HT=txW%U7-(<*+n_GSCu{#0mei9QxkeZx(w~aiGljwOvjlgU6}wY z9!)_zVJ4_b?aqgTe~6ennx z`J#=%@qjrzw7WrX$H7w8A3Bo@BF^MHtcX!OB$?-Nw3-B75}4e`10;X=tnbE%vBV zu*;KGpHAzo8lRvFh6yAXsa|!o`7A? z*!n2ZV_@kMFxoE`Rc7@`2~mMa8EmiIh_w^KIVernFt$){(U4zQ7$}Q-;~}7g!~pAX zaOvBu;#V)F=Os{Y<#!SjH=lU#f@psZLWlAb#o`uZL#S1*lG{-G7L(5f ze;GCc`B;jlr67j{CLe#{E68EI>?_uZL>rj|1O+!rgwF4(kjt zM#?!&=8}?yJ`!e!aBcJr#T$4 z2_Cu46R%kfEB><}w-KeEt@hT&B-m=)L*(2Rx7VJZ5S18Aa*a<;C=xeV%dCIrw^B&C z0`sL9tTRHQOZQZM<6jnC%?8R$G)|B-Qcq;YM^4jkfWv5Nt)OPXO*x2|v z>q_(cExUQZlnv18i`?BU+-SPIrF`&`k{&&K;ef>@lWGEafik_cu^Z&V zdRSq{B^LT_duY|`Ypk?+6QimM zO#EW@+&ue{@u9Ah*X+Nue(~@H;{SEPeIT8ud8=FlJte@p)MsGvQE0a^rplnyu;{nj za7r)I7O)3Q)deGo6s&L~JQpky94)9DRFU5>xH9}a*<4)O&?CR1zciq^xJ3B+p;*En zbUEpD=yGX7%f7H&F;zKVJap@rXwH%nqSIp1O!e-3>4KOAyZlAHyRaVrtm=&~s71|s z4E`4?A3r8)+7PZE$LSNV(pCNx6kObQM>cdXI~Y0!CMrkk0QDY((GV!W;b;ldm4}l2 zk?B|_wQ!mV3l)T?Lsz8fuMphe>^vZyh>CR>5E5r^Y|V_7m%-=Fj5(KxRkzcjZMJ~{ z1DW5Jp4{EWZiv^=`!e~F)Y762lrz`#)^4}=qq#D1iWF|3#>>31S)KTX?YM9jOv$Y!Koff3WjlWejTvb#FEa#Y8voVmM&j& z&Jz{qumCCYjQFz5*t09(wb(LarH^%>oVN-Dxuwt~N3s#w%@&#@+bqBlZw}*#@wZiB zZG3xpnAF48EFY|SqcOjP0=tbJFg^$r)4C_ARQ!Gh-M+=pmy=M6XN|B zrD%b~NMB+~aJav`I45d+X`j@QEBYi|+t9VBcT;&`gLJGHrAur^!iI7zp#330}nxmacu&# zU~$l5Ou*RGRU;cm>tC;rek$`(7}#>e{a4n4o`vRAqF*57gaJ&O*6 zo*BJ*c#l9vAUeyDA@y94jlUI>NBsWM+8A4gRhC;?LVqpeRU4LG*RlMHPIlk#LuYPk zt@Fir`VFYMi)ZZlJ(so&TOSJDI(N@SQjaY+OdMfzc%qwLHzK>%0QXs)XVnZ?4K;%{ zTKn!u1QiFdeS_H_J#bU=<+z{(@J->-`FA8PK;+ZFOpLQbK&J z#|4A>X6UtPVWJib7Cb$l3-!*4;ZNF=U<~KqTQOZzXyzX;2QVT6elc}1Rlu?gIbB!s z`|a=rvJ)C?DG9PtWkiMU4_f-$-I?j>y}ir5v2FJF^rVKM%Vn{u7R4s>t`?VJvFL$X z+3IpyBX#a9sO#NQ<-6kC%@IoUnRwl~1ZTBu9+$%tP+ghHo`X?=k7EiVtO>3Zo6Q}G zdx3GJ3zruJtcezRoIf9&i%Zpx#JFxV`{57AxYW4y<7q(qi~|k)p8Xrd|zVgC*9BdqYcfp}2@)X3lO*EYZLuDmCYr zQ2irI@(iJ77ORn1Wg*2TS%W%#-tXfhJo}1QK0B@rvle6V?R~G@KH0y~(`8z-;?GNJ zu#Hg^yAiM@xpE?s8hMY}F`JgPSEme$pRsK9ZMDkYh?Uc)-4Ula@IL|Gx}bgC$ULj6 zN?tnpzFm**}d)KSlCoc?~T~yy=ThVKuPaRLqMF zffPXyKnh3#Izmb^6FL!eR7L2ocpvy=z0GtXN@;9knZyyTW~1!&EOjk zFDM#ZQI%Znjfl2;TcQ)vY7Y!u`SY`jU*IjuhZ$YC`ayI;ZI8Nvp%31e_x3$wqmsK9 zkBW^^{8S%~@SdS&2K+Ww%*z;53uSK)?y4@#H;J36Bs6Jd}&=EWEl(4~ZMmLR8xVN*ySz;vC40rj~} zgQzSuKZEA@-}!EMy>rIY$rHzpT%A=~k(FL(^+1@3DWg~&6)OpXB@@*M)l$j^kH$RH zoK%LF8O?zPZvHO@SQ5ztp~je`C-U&|eFZxb@&iFV+G`D1B)i>kxvUP!VvqB_9p`K8 z^>J>GoQx>1%=KhPTur1sGRk}I%+lW;eNuDbZd-w-+LPQ_4ts0Q?=qsKck6Jkxfbut z(#Ax(M*M%oy?K~jRh2h3c(x|9_D1(ZA+KP&_qTR2}%cpiX?WbtB+CH}Jt3qzx-`e|}J5*H|`k${K zp0;w&-skMS_S$Q&;kVW{xf5ouGa%a>R!`&?n~yqzt&KH0+39Ude>xnnz17|{M|{_L z+8e4O=?Z?U4#}tAW4-oL^q8VO2)SdSP+3Pth)C8U970Gq$SYFkuz2P{qd`)_$2Ghy z7WHi#&*2ym@Rhv_J7(684)^u6HP=eMj$^sv<_dMWE(kZ(YEfR^Euk!q4kW=o*mdD$?ODji3E==g zaZ?4l;;_}o=)mZQk(p_Lhm9mD{>}Ccgei6ku|14urs*W*ZL1G<>`UOnTqLaNU2dDd zw&Mryx%rior+<0J#;4b&EH(BDeaPv5C6zqotFCkhR)<6RslR#FvEVYMt`Ak;Gj>ZNR3DvlC$%Eb zf_8JmCbKSv2k#lXj~?yNfZMx~A`5s@H!1rmsOf!C)SuN9sh#DC8h2!~?(pk)}N0{fKat{U#h2CnY<;a1tK3m7w&4kPi;J)D2cb&j~jXF!-& ze)at5eJ`JCO`OQ3Yu5TB>p%9e{C!U}=yc+#4eDQE`7K(r-U>}RqO9-lwJFU1TH!Sg zLWsePgO^P^i>(JYI*xx_=RF$s-iTaTJgu}k*2-PRPI=`c{ zzp7*7B`0g%lX6#0_TK%WP)BQZoj)!`*1EmEcf{po(!vSr{1=^?W9A@VRi9<>s$Ja+ z0bj@36Gs-3t5I;G`*Kf9XH##iNv@t4OFpo|MqhDDly9Z{pMuU=F*3sC0C0z2hW$Cj z_X5(m5Mu`7f_4a@g`{b4J#(j2DBD56Diwd9duE{X*6J482wY##G9!}IK4uYIOtCA< zZio@_c_?kMj+#iND`JGwX7_g3tZry+|Gu);HeT2%Elx;UgIN|Mn~qp3BOQqmm($x( zlm3eDtu-?)P-UCe!i6Si5xVfE?hJIZQ^FHZw~87Qoo!^0X9N@7bxtuRioP#U?9Vu5 zF-u0HdC;Io&_l7$IR$H%G#4xuLQ_&7v$(Vi+-J*PZZgUftP?c{h^F`j+5wjzJ#zTK zzWF)lP$ZeHcMkcjzypHSOBAuN- zS)bFAt+Jy^W$)E0g9qXDvKvTzlEAB5u`QuXaDrF{c~`@f(kIuThY|&sPWcCP;Fdxc`iI(q7+}xt z?Q^5Ut5*#THq>R3U;q+<7nf*_BI%Y;A3f24%VDr;ttBU%#-cN`z3O@M<@TN4 zz)+9$U~>msGYQzy*R}QV3E1IrEyWH%gelkoSo%<55TF8t*)G~y6ofp7QbECnzD+I- zGUef3MBF2Po2YsCf&4EYdnW(tV_y(^@_+6>^0@=E@7q|@vG-&1N1ooRdGXWv-`@3j z{vV$E8!`6m*TnGM=YMkK$4?xYz3cJa`@i|n-m^bcbS?QenwNob-@$$O(E=U~;pS*VC#_krEvCbched>+;@2}YZ z#ar*0yL;r**}eI1+AjTR{`SkB`)mFMZ{3>h*NzR{(L2)7d-zlPHhpk+hvt<>^8fMB z$MgAFTb09Owp#RcU(B{Qt&OHE8mm9sJJTM{)<$+dysz7AOL)7&Yn`=E=-aP(Rb0IF z|9t+E^WU7f``oE*t<$|DU%BDf%a1H3}j*^4^A405FVc(DdF^JU4V_LQ6`GLm%({p5ZcyUZSkT^a#pJ%T z)sg&{dmqpL{Pv(8P{~ldwf^sZH9VMod}W4x$sG+qrM5DaIxbhlhfj`w`br?o40@_I zDnh>eTJ~WN_X|Ba_*cngL&pJ}Wei(Wa2IH`YCsv;>Maeh5nK+l5lPwoj9EddA~Kx_ zNN95{k&mCxYDkKyt=OEI6zR6&Pnxby6@lmtCnk0(AagO+(0=9fhZ=86DPVNhm0PA4 zHsrs*?VmI+$194^^YJ6=R4Dm>0fD~wiP^AA0iX9woZUIR`pvP+Kl-njD&{%{pCV;4 zZxSAyc#?3#mE>4u^CCqCITXMJND_1(C9i?&Jz+scJ7|xH5UtdPPDfZ&N*lZZ%@Atb zs=g%`We*V>+6tJTq($%*VYZSMWB{0*hg)h=9W|+DBh;QY@&>AbGm>S@i9%YxM2-OF zgd{~&^+JL>V~lGrA}FO;?61kVOBeK5U2c=cV>h~^ws2=uFTK>#7c=-=W~axfF}XY) z@ofH^R!gahPdX6u?3;3tn7J=h>8x0raIG>_R9XW@S+8-rv`(YXrt1oZ4;g|LJ9=uo zZ{}CzMp^^m!XLFZ=pZA)DAy}II&im!!TL!zz(Ns>Kj%l957lFXYlYN^;v-n>gZ+KI zJzaG}^$9&PW}F@Y(op8k5$r zK@RdELE0(8&98xV4m%QTr2{=s44dcyxi^vk5k&lvxL_1`3uRuX=D^TD_rQ)C{H)xmhRwc>? zVy~vs_F!QrzR^cJ_@c5gDBXm)I>oOiG%jO>?olo8)?Rpo+X%xQDl z^#;3EE-ZlkKIt%2_)W$m5o*w$XaGQ+8{PSprFeA<0%I|I43Z33T3$ zYVoDR+bktd*qVuzA1+%tgQ3#u(`wzd;f&3ou3oM9n4_<%-d`c>!!DP>K$f@OS6iQc z{%rTE_8_e*#cW>CJSXg!iAsgT-aTl{BnBfOo zk6bCV{P1P4STN2j*ayJ6P$(I&d^5Z(OChf8S*Kg3C}Ft>!9Cno-R+kR#Y2g@u;YZ? zvDXox;q={psQz`2aNtQvU0ga{PtvI-=swxu^sQesRfi$09VbXw*C+Pw zYCYF3no6T3syF(kJLzKGP5g$V{9aYk?KS?I1uCp^ec`MWaD+ zT1k$X{C^FlLgQia&8TZ3|JxG{mhO}x>NRA|hdRX*AyJo-bsm@BIC=h?8lT0ydX3n- z+<}$N*XT^$`pllJ^nC!pP=PlUxVkDUtngW?P2^Z+Ca-=zo~XAnYt1)v|S zj7M8e4>1N#>40;DjN~-vLNbfMX-+^*PURm5L4H=B-~(BDoS?o1Pe?8K8T;C;%2DoL0y5vlg$H)KqFG3`yt z!CFP$I5f{)_z3JfWIv;g3Z`SdSBnC$s}9i)8v2xBD%O1B+_}x0$HgOUk`{Hj1LSgZcR?@?Fg7_c*|gj}v4 z@n}h_E861(paF>p7jmwBe3MNyr&_O?T)W`C+~TqCYg=1cnT;Ct z$yRq_#<%DE{cFeT;$M$CGLBA=Uyx9#;`j0q?QtOi)C@4FF#^vc-&n8~&>}2Rks!|m z>rPjvGqqYLa_G27R;&%Dhl8jTn-0fdy4}qv!MqM4d6rhyry7v~y*O%c8Ea$LPwsnc z$MvsXKm6dL}>V+?799ah{P@0WD|ImPo*O_e`;{WlI=|C9H`GM3nC zNyz`>si$Ud+cN*y@!D#d1L+pOm;Q=1Ofoxl8QzECo-hlIhFh$#Aw-~BE5n3N#~w|J zN!Y1cC7FhRcm(-Dbeh|$1s||@-=WX!IQ;eF4{RK6N%oFwqUM0tGJCr1)W)s{#pu(H zlV3e@?ayu-?is5a*HoF@x~C8Ce&mYANE>kHl90IYy8IK+xf>ZAN1v`2^;*(vNfD|f z{sOu6dZxeF29d&3!fFT=2`C)|lW=ia!%~(68V(lw@L+#B8I4r9t!AVPbpwvZa7+?; zIt!s9ZgyhqEu0fAi4phYpSOh~0b6b?-h*yh?jj`V-bUD%1(h zxd!|Kd=H^_@K6f_T5%U*sfm8v4obGAu$C<_f+*JmhA`y_vP=W4D~<)8L*>H!wJegO zI0vOa^``PyR9Ll!zSHaK_YCQx&%E!*-%jVBKk%u%&E))#P;{bU;|b}3`rUe~FEf4D z9-lwbdg!K?uKcPf+_K+d%J^%$5inwTRgh9=g;C)lp3|4cu?4g`0n-4uFtW7=9+{`A z+Z5*&fES!Ol7-eX8fj%XyNcboql-Wq*|KK3@@6dP370$N4nh_7i$Z^IQv-ac*{Vpu zYqtVeJ}Qm^Bco=VBO0Q3v?|4pRt`7EF##S@;c%)}OinQv&)DDKB2uOYMCtDJAI~Oj z0iP|PO>fWr;-{N_&hXDM1}i{6A@Gb;h5u)Z9xi4K^2QKC^r4V zRD;eAW=2q!47Vp_g=DgFB>{>--jNZ>6(y_ zq0WdwuW1Mlnyg04YV*WL_v=jU)&7)hkWWtC_|hezj%_`|x3rlZ;noIkU{|f%kb0`~ zyR$U;k9Vw}0Kv!&2jVf6{-H(iHor)7d&E-f)UJ4R(iV z@BN4Fd97i+*&8)lta^*3y3(g#-yrtv`rzfq^3R>L`6}IB_A9a%<Cd3H4s;g#g*+kvXN9=oJ$Av77N1!V+&a`66ol2BZWVOYafMg14T5N_NDXOzrN85l zv#_`;3fNFyW_7*Csf&=Rh00WKN zFe}xFFd0w=g|Io0Tb_ma-8*+|g9#7BR4o`Fn>0IWQ{4Sd2+NBno~lk!eh6})JYxb< zf+6~q+6Z(qP~ns%FT!4v&Yl@{8TGZ9^zf+vszSzU2`@OqfvpuMt9<5`i0>i$4u>x| zQZb*nE#XYWyWP2SwX-bK^*AKo#HVguYt~K9*RC^rV#VCnGVZ8Iy!jQsuRC&knrb1K z0<0E7z>8W4_a^+tU@F$>&Rt?vQe(|o4YyBN4lmXSby7I>sa%tF5Ka)jBsnMhH%<`C zAi;!*u1|wUYLPXwOrcweoZl&#@RH$XUTNwsc!hjGx-Ie<;iT{dCB|$= zayyQ0Y0=0MbZu6Q)HY~=?m|unL#g8@TWBj-t;8gh(M!dRmbK5-QBzqf;EY|`)U2m{ zGiWivc>$jW{7T@gq$pf*`ToUC8xZ%ITs_p)(caq9lu1QGLBFp83gSueB+@o0DU?~$ zq)QQtlt~>$O`_-!J%cDBS^q8s5>ueP;A0Z}>^nZMd|EcztbXI(`wrccRb;1gVX$VQOPTqU}&}DIh^lPozTtR{-f3AM2RB~Cb z6nl2xdF5deT;8Z6xGpNr#B<@C)gBH6%_F{eTOfdQR+=8|G=sf61}1!UDasV`2F!;9SVOgJXdx9emtc zpfrw!a^k){9j&X2bi}E^ZPRC<+gLAJ8-T_4LjkuFIvi@qviLr-6e#GSR0iOxE(hvk zxBU9+qBZ}Y-@f+6BWGW_Y0b@3!}}VC&P~##xDn$TwxUlvMdaiRgy?^fUJB1G;f7LJE1s#xR*cp9 z;l?Am4;^HMc?thUrW;!5^{RD7+Y*}U^0siLWUFJ<@pMg%!Bgv23>3Cdl~M${6aTYd zdXm+q>`=A|nPysIjLK+m7R%L#jX@TTWVeKG&uzGK=E_%Zb|rn1)zH3WcEof3XezNS z_1IkRg`4KW9fv-#<@P^))MByP^Ut)^`XTi~ zK4&;+)@bu*I>+|kGuyFidoDxpFLzq|CB|9wj^2{j-U zkH7Kotj!;(j!@MG%XCJZ_etVo`#*o8GbNrrbVv96Q7#{(rx-uo&KU>kDoXMOq5>?P z5mEq-ce z>i?L5G(@UoQ6@a2X<4ppX-fM>0`8ukbpGr;t<&+UXyY=oyYk^R9=FW4JAtqE!mH$gCW(xzzI z#4qG9fZ)(2xEUg@xAqaa8(zcBkGt()ixi!yl%zBnlermtR9Y_=ANVgSdl$svaVo>j)5YbDwV=Kzo%q zaU8zkVl$wN5G0l1F{Utv(ugraYcsNMGl{A&qOk(XW`j^DvqkP<@&SPw51u$Ej1QTr2cEZV5R$|s+O+$MY-}rj-%Iz zuSq6Z54{$pLmXaL$QjZZI+DRaceK*!?rhB8c5qWFl0A%hd*CyA4r{+k_#VeI`%!KI ztIQlksAWmIKKF9KTR?d=o|5PCwBoD8Gb)?*R(%QYaIwDRqPlor;1?7xjPhx$McK%B zH8W$XzB(OC)sh=#spoL1Uy9;Diet*jLa*i8d%JX9SGFSYGc0mY!8gIb*r|VlJ1T4>R=`#h0|&A#?K#TZm24^t;1t9cUYs5 zj`RbC(%urtSFg$-@qTz~nt)g-e~%lX33feg3q6uv3D7DtQ1Gm1p(wCa zHyQuZabF_hvv06FR@;4{QBRZSVSRs{(-;1EU2-_+voBViaQMQb27~S{z0csNI9}nG zP1=whmIN=W*4vWb?|6Ls25HktiA77dGNNevgw$ccvX zUu*@O=j@ir@wH=XMu8o%VZ_@h>_KTBQeB@^rr}k^(#Wko58Pl=+plgSc7@t z1m{2F&f$8*0d*{P&i;l9Mj9BcLbieNR8zu zEU*!qH*T1jUf3yWvM1zo z9Im>a_*$=Z_;&1%`pw=@!tdC{SI=>o>-0^#)@`!IRCX3C+x(xnZMzXhbb@ zqG#OX4i}o)>tknbt+k+;CRC;D5Z%1*p=WR0M0?=;ui5?|5flkxP`dvO2K_YD zSqC{26C?zHT!8^9Q@f%ND7FHuD+w`B2Tsob)|Fa`ZlxQZAHqvyg~)7BCPRq8;tju0 z*>c(L2R%#WMYB79(%i7Iwy}F%!~WV$W71HO1(%9}BJEI)vA5-o-$qvTu4$+ro&7== z5^qog2mHA~o`(+>{7F!qOtCHt7) zNm~}*prrNsRJU{lEWq8-QYnYY2$0deg3f7vZgAdZwm435DsutpiLzG3 z2;65)!(Q>G<^vWZYEfBz-YR|m{gK=!>)V@bEg@rM#vl7iuKkze!Au}7jvx0#UaV=a za;D7=+qgUr2NiT7tyRwc8`Xz1+WcAstX+|}d3OXhq!n>=?(aQy0XlQv)b z;j^LjyPAzsuk+#U-LH)u+83}os|JpYjQ?U_^Qjoh8Ne+YJGHt0m*XQx2CAIaz`jFc zuXPWe81azbPdJueCHzS98f?>&xT;ymrQ)o@CLL<9!kCS5tSMwQGz4uX&ApYi;rhS4 z9!LhN=pAFA!;7FpFGU_f2MrLqfJ(qjqN*W4?`S3z9bY4_PBerQ6nPZk6;~)g-W7a^ zp}R3_NFyQv&QMMQoDd)kQ(|f5rFxUDdDr^Ttp$=C+$IA?=G%6#K$BZTAC-Ew8qdgy z!7`fsvL7@7JX2#bf+hpMD5J^thcV_2;wI?@X&iM&yO3ATaEX#29S{(!$1ETV0PH4L zDGNbr1VpweRn~5WA4h;#j-o=LIv(3Ji zSe3yM$z38I8m_Oeumxr(hQd{0s4jCT*hux!_KSmZKjP})x2{*SQkiq2n@jOQAzMHv z(+_M(J-Vk*=G>x`IUja3tQ~Ew9~#W>Z)m(qH1)ikP4CYC4a%I$zv|^>&XGuk8hx*8 zZqVL}EW&x=pLoJu7fcz1GQtQ%{)Ubsy@ zC%=e$mwLnxb1TJdK}C3>rNLi;%WYCI3AOPC6a!`HBiKRoi0qT4kCa|ynk;>!^df}S z(nqWp|5Yp>0W(DS0HZ7~Nv9W3c|Z@=(`&Y}U*sudtO@dJWw0o7iu5I=8Jw3BINs3& zkrzE{L!*8Nuc8^e-K%ISZ~LlT0{n>qQrSy(MivK8NOeATZf}{~!9=1ykw}uqjPKnj zLb)v{3(bxRl#6%h6+d;O2zJXTxx9k}gzZembspFqZJTtp9Xb-GOx6I+l2WijlS&;N9Op8d9k zeyj1Abo29f-Muq;-yiS4|BtlcO3smx5GOP|q$aE2NwKa4oQYJE&n)FqDL~vDwVhlH zDJKgo^p(yT^}P8F9XIFirsGBzDgMpATc7`tbdTl(r~^18+&S@N5)06d;Q+(G17(Qd zP-0#dT+>Qk7OiF*=_3#fl!DLV(5LDJrEz8@Lqaei@Wt#x)Umru;JzmtqZT8f!e+s*0wD z!I6c9k+CZu-`O>lP5X9K)pf6)pI=?Q`S_#xe;!@jzg9N8Mi&pP#T{%P=xGU52O_TN z=IA9?-SXg?`TzI1s|Q-4W6yMX_gwSdkF8$+nNQui@%&#KA9&yc+jhiKcRg_DPLL3j zZq+PE2erM(UUmv;a3`U8snj+87g`J=Oi*J)!EQs~T`QnKi&}&a?z6UzsusX(@Q0YM z8yJ{fy(<6E?5b6>gWBF)U43q7HkX?n8k(i=$T&a#q#f1#Sg;E*U@Ldz=8zzU6iQU* zLYT;)U$BU}{r$^qvvV)>+u(aJQye+FHP{Oh!-@GDhgP#o{5 zba3^*efHqN&;Hxh=YDm=4Zk|~kZ8PX>plN;`n~7Be*9bF!1IT$t%`V@-fENi##P52 z9KLnSO?w+nUXR7O?y^gczj*D;+0SXtZMfryzx&Y#Hr#(+jK6u`7}9zkx#s7eIk4;D z|2X@3G4?mF^xM`<1gkpY$&rVT?|AsoJ!`sc)1geJb^n(x-**2ceTXS93U28y;++&z zUUWM$nw!MO1;MG)+yttk3OH3YM#Tdx6LfR#W~It{#OPF7x>yMOStL}{)YWJJm}7C| zKjJRLU*HBq!u<)-XHn$p3m2Xf?x70kRILwgOXZcURPxFd&}Gp?NK~8bqnDyM0dAeT z2lqTsEkqzc>4pbFezHQgl;8n9N}%Ee9Qnzs`@mRW$WPLkC911VrT07z8}<>3JQui) zAEs(~$268rJhJS9gg-660D+Bb3m9-73~dGG2>E&tvM9%et@xp*$H9O zQk7$7MRoYtz)?s8{7tyoHkkQ@>jNomngv_*so8bc+LO^-YTBuD>UH0*_uJ!UjR~6o zITG;dqrQwMznE&ayCz5P&eb~;6=`F=)?=>lH6EVG`I}lZy;ZAiguJu_B`bWoqCR4% z&^e4wyE)*maQSR~oews*w;ObRT>_$ZmwXzPZK`2E{h1%=xL-ko0x{6&VEkza#DkFc zHb8!;J~EU^EDx2xKmjON=Um;WEA67Es>yHNr-2<1>Sy#a6paN?4`pu`mi7cYPvFpE zcRRpFC2Y~f`XTxR<5(T$l8%)61x#OCOI;0=%qo|Ig4R&8ddTMGrVsh(DR&nuq1QD~ zJ_!^_<)2_P9=w1_q(i|rrgr|~)_}!n8tbLOFU6&*U4Noz~g8U|X2qFV+m8m(Q9 z9va`ca9XG9o9W%QM?SsUR%dE3hx)7A0Ib$qwKkVs6Kid(Y#6XOb-lA|#wYt^>-NFk z-JM~=u_UFG@ORSdOg7+eU~ixF^$L!h73+osCBT0(aYEjb{Wp02Uo-~!GC-MUa_bSZ zhc{6Wb#QA5zyp97EDD4um`0<+4kOnUmay)waGlm* zyJ5VgE9%vlS6|W4x2rYbHBguaTR^Kyn$2MzK$0Z3db-nF zt%>#xH%zDZxN?V@IzsXM4|{7iDN*Y56oxZRSIO9L0ETV$SV= z55cAA2M-(?A3roMpPo24F>w&j!DHF{8^RAzB^&2j7U!Ax5{_@=zr)uo$|za`Mv`#$ zkz7W5+-`xhtV*nkCF-;Qov&~%-mIL9H>0_FF4Bp}bQoS4$W3o0-A4Txs?A*4xd;if zCEqQ+0-KxapC)rL{Ia5Fr&t6P+AK5txbKD=i{!{q!io)&TeZ5u#9K;hpay1@8mQx` zvKpvc)()H15~%yFrZ6jkI!Gl@M?ZuTsM5c7Pp(1<)E-7h&xO2nvvh&z=nG7n+u|0#efNk;MIb zckkRVtw3y&o_YVg*(katw}tcotPPFnjgm3FAgwQq=@5?;$Jv{w zbVN!cKw*9EeG9vG%uL~uotEatbi%#cw>#@JlYCOf@(Jm+0)4(EUQ-wgve*djPy+}N zkfX9-B*zupJaFWhA@0q%j*4>83CLtc31|yp9_mUO8_lxOjH9ExPf7E+Ts#tx#KQ*{ z7q)KhM^0;JdtG&4Uu0i}2i0T^qF?gcN@(^u>E8;cwZE+D~$ItWsR}XAVM+PoQ1x8MwQCKv5fN@=i0*e{8P4(#2tWfKt^NQSI`F}w&;+d*oJWMQNxy_zt0 zR2%mF0`7<8QDX9)K_A=!Y_uM^{);wIHX)9#)L}G~)nUZOGEP}hB*JL47{S6lyf56e z@47R*XLo(z#_O-U=IX01y=3QR6wl12+uBl#lzmGPKBOSw$YBN+9oY>cj4?v;KnEq~ z7-S@l?WN@y3HM12fXrjc4w+$eej$8?|KT90PF8IYnyZe&hP-eCzJOB=|DRl~B!j8? zHM{wNfPApzq%*$YnDiX04SwDoaJ>KpQ9R_UteB>Gao3a=43PH|HT$F zd=XV<-4$gege^^}l=#c4ft=M4devccn{j=n?6p;u)qK36SfVpNYj!6rg_4iYZ+?pk zoj3g06A3x;-*>n}E?r5XVXZeiDA%_)8fFS@A!)#WH zkHBBeAdD6iN6O`vy#W!yck>Dynv!_ajn`g%^0KW{Yu60)cC^-3S5=}uE;}@>H~^Hn ze63_IFG!BUTpr+aVV9sm{r(?Mg%hLX1@C$$&iCAK&FK?IdDYymtkY6etBier*%{kk z82eZG*heK~bfR*h04}qjJdy=b1NeWaI=gy&!WPH6z&9u8jHCctt>!7L3D}JnX9rVa zIo2$F5j5xj$(ta5CGovy&)jhJi5;7O7|!)}HQ|cZNU(S`+v#Xl*XGi)vu?03zmKbH zQ$8zxnwjb7duQ_+RjNQWCfTlT>vPfEUL^SE1Enrq!Np2@6JSJdxN>{He6 z(wc8&)VVG4FGUff0&EQTL>E|%QngT^o@Y~`q%|*At7Di{X3G~-19pIKqx9QARg#XRJ}LR8!{uylYOHonxyETF4J?O4 zLOq|KXjf^T$GtE+g#XOhk@*e)oz^r}jl1P!WFwM85C^=@q!UfZsx#rnB9gd9lVn^l z=;3lP51K`k8fE!Y*jq-UybiV|Kxk^2ObY&A>C(_8jWr{*)x*Z-BM}nLNP)gcbE2Y*BUxls$#xS{C2%Jw>MiQ`y-1Bf}m>!&b zr0?2_RbgA}fW_x;v%KGTG<-{sxg!5J=AT)^8)j`LC|uRXgC4_`K6lJ1e#B;xF4=tZ z?4`S`jha?R*N)*6vsudxn&&n3@;cem7YdCbM*p+zQ#EtZRFzX>YlWY_r4mTwnkRNR zt9rt+*y{}e+?GOxQpx|3%xj6epZNWn!I>kov>P$H3j(g%+ryc@J8SiskUa|3hOZ-6Log=wb67I<@7go^THApcfsR6x24WToE z)Y(`cuL@*?*`i1?sb?0^Md2Ct9hGN@E^CYH1i?c{Y&ch$DY@de!Y~PYyPF%6@lZ{; zrnmxS-})Mtf9zW?iXSY@2{-x`MI4(8m%b%)fuL*5q=J5D6|Rtks53HA2kPz+u9F^> zzK^{2R4yJvp+yZGLx`72AbQ!oLCKmV1r}2j)gf3Lvk3?{$lQViTw{JP0M)$GqxBWR z=wtUaCw$@Rd+)7J`YU6fdNdXfh9i$W7X2FPxIA`mb5)?e`o53USNr4iEE!JHGY}4j zzFMy_i+ikl36;^?Qd3>!Ho=zC!1YPs5E*7B7K?J55Z$1g8q&$?Y;A(>R#d&Yc9t(H zX%bZ#y-iDi0~WP)p~Q#RNQEp30Hb<_Gn!rF4RsX`UFh1;87k|Tf8&{XjmP9P+jPOq zrFWyk<4Zf9J-;Yjdg{w4?s)vn_o&okPSZTzP-S(6L)XlFXCKvid~jYGd2Qnq@#>#MWr zfRFD$uofQ&^2eBOi;d$2XK(-={{ zEj`KiDf{-f#9IsBzMg*@cOqi5!dnfcO;KWj+C)woZogrit{#XD#fEVI%UUD;gFU5} zNj9qLFKs>E0XL6J;_Q~qn>Nnm23nBhjeExn{vy@KMr-s8-*aqyFN(Jn#=R;CJ_|HPxpaNha-!=jtJBq9=X{0^} zzI$RAr6a4eF+H{{WXFoNM=48irQ*fpeBhVtkkl!{WZ@L<5dU%7Q!Gc?>}BOhS1J=( za?MV$y85n3y-9xFtkxur8*NHW(#ZMORxHT3y=KbDuF8PuLdPB6Hd zpvPG(ciG4=jd6j?z~=dBW6w2czG z-&zTBN{c;nZ83A>>Jk68Lgufuw{t_S!(6lZiic;9d_9)D*WqxsztUOzx%ZZ(!PIO- z8jQ_jG8%76R=4`Q`e!RE-lz3-Z#{Li@BFDNt)&UTxbdoP%&v30(x={cYU;#X>uBvW zZcoZl-TZvx`kp(VE=iJ^t+oUz{0@&vZ#Z6+?d=Tp?^<oIn=*_ z|Heys-cMXd-kV{p*;H;4>jrlu^m^p^L%~BmPvDb-Frgvhga)igm4VugRpDn_T_gbA|6^#3w}y^B1XK#OLtQWnaWq$~>S~@zUntD)^YiNb;O(Z`L+$t;f3dDT|sN)6$0@>sy z2-3o1N*6=y+`f6^x{2Xc9c>MDSw#K4p18?c+sOjcYK15dFJJ&Mm-6Vfr0l^&W#MkZ zeM0~i@~7US=oCEd%<+w5=I6QYrm4n-o_g`o(2u*jpRja65T8nDay=yg2P4~W`OCQent*uQw7PLg&HDWMo9J4pob=~4v zU3ti8w|6h>>Oi5K%C#L`UBO^`?^TmyyDH{Q6^?6`)T!DjTjg}}6!_MY|8Mbc5TAuC zqZp<5lJKe^L7I(X)gGOA(hFV~02&f0N2G@s^hU!1a*Jfcwn7}ZP*=%>Ew7n26QQX> zkQyBh<**t{U!X^R(mQ^`TrLy`)Miy~u)nJVsf5|sNPI-B5~~n$h7>69^c5vC@(JN3 z#?xb%2h|+|q!X?WNMk4;8gPsrs2x3&4cq>HD;# z%D0U8tRgdLeAjUa*Nn7TpajWH3I~!=v$agb;VMtMpa7#I*$VxUm6UB%`*6XA{54b% z;=K{cSl$Dd!}axa;9BK)rk!cKG(3`NyuJ%tA6DPx2^Ue1>btDtx>K-lFta76GgwtB z=_YFR)Gj;(5z8yR)BsM-V;l6Xb(|OQE%1WyFk7p#uc?PJTbiX`TlR3-*BUY?d0sX* z#v5#IpZ}9Ox6^!XIHA<@n0=0pUhV~AH}DYi0R6nI308tqww364Nek)^o?@zHp#>a2 zMwEbP08{8INl&=&I;*L}cmj4#T733ZbzGNnjtG=-03C0Il3IM=B`#J_pyY9mrlg*9 z^aSjTi!KzWFRTEJq>dQC0CR`d-v^({LFv=9e|*5K2rpvgKlJpZ2p=pwucSQJ<9b;f z>?T<|YlR91J%!mjXo1^hP3+r(zz+?&c>{L`V6Op^jC{(s?1z0{?1q4n%eiU8)Vh(? z1AU|~&^ZOQvoM+al<+&oKQkEf_Z2*W71QS-nWBOM_aIJO zvl-NoBn{R1VMZHaANUkoR_evtEB?C`dgeObQ70`nm?XtIjZ@lEa7;mX8{&C^Gh+NQiN-o(>I-LwPm+c$a(TcMm z1dI|`NL6@OY%`Zb5eLe2!8PESPzIaJP|$0C$aw@dMn-%}c%U$biQE{@P6?M=sp@Sh zk8qD9+cBy~l2lmIc#zna4QGohL|#JhBdyO*%Vhg=g>NnJog6RQ$>a|qS7F&lLc|Y+ zJ-cF)xtz=8tb;?)?{!wXDrx_6hoUTOx$q}>9@s9cK)0s%=DP5HRO~0P1f8sSGr}`U zF%AlT1?e7eesy|<+ZM0~3TR+=85&rtg6!aKbeH^H3IqNXyFPt*Q;+h$;uaMId}xI$ z-kBaMQ{9*DDNaTL zRhIi0VOnQoM^A0F&D7euAGKxfKea024_l+F7tX$P=_m6a5U-oM0tIHKw;o5anUrkl zsdalRTAQ!=II7JY8VosNo6kiSMZ&7?Bdmz1O);`kLC7mQs6ApDNs zO8GtS=&dB=-{D(xb%kN73Sr6Iu#d~8#&dqmyv9Nu(+Js_L$v!#3GJQ~ks1iCgJ>61 zX;&(Q-j;S3X}p|v7im~FY!I5$?jns#-^yusk%r}O1?|*!D(y(=Q0BHW?OYqCC)W1$ z(1q<(J{3wYiSQ!-FKZzBpc0J124~4Hy(uKzJiLzf5Txf;(Y zUv-hT`f25U2xaHL({~cp0nbs7l#;KPzL6+SYojQOC9TTGh>BQ>d9tiA6q~ZPe81bN z(RLqfX?^02iRok}j}-HqNY@;FS&PGI29mKuY~ER{waM z=~T>)c?Z?MvNf#qS1lznmRFAPz~k#>F){Tjp$0fVR;c||T8Ww!|3VeRBr8Pi)H~m4 z?Jvzu(XK*WYHvc8&i~G+Ghe*;*B<$i^6FoFZNx8eJ%+8#v&fkuc}#2bNxtV%4*^(V zy=AeKS5-wCOM`0_rvYqom{pY;r51RHBG;_kJXa3|S;>;o(6T+uhrF`WOposXF30X_MlgNQ+;v5oO5(y)g$_ zQ10s{=MR09hm%;MpyKgX{m%}36L`D6_SQ@)8lgIn_F>2Hl5Y|nJ|_LPK!@)taXzAh za#tBaCM6@X%FD37w79U3iWp_8hi62j4_M}<9hS9)8!{a~aqP(9`CYgXaVR&?-V#5W zKANnkb}M*h(wEOk*A{5;bX29qY;N;%=ck0wIP--Re9lW8psIZId8gM?_GCj{I!X1@ zy%WBPWHO#m{kER`@8rv*g9Up0P`ZxsH_-z=BErjOabdPbG-+lbo&}9!?ytaHEuTrGyJ> zxDD>j|3Ny+eTmFw`FrsZHojquZ%=O5Qp$jRVPV5tw2~v49PHFf%ne z+~3<+pFx=t+=o8tpF~WGlZg1V0rmD3%n8HRz97BI_6{-1bZ#9xJV{>S8Ye~EBCiem z79K$zQt}p{%t|nI84^W-n!+34aS!&lwV+@KY`K+!EV}3aJ)<6$m*UhQqVBRa9}bxS&VC| z>+1pED?Um#SJg*Jb49*J!AD6oIdF`XpnBOJR^n}-xFPib3;f!EaNcZ3H}{cOAy2;VE~Jn z+$acgQMz5DrP1Srs$5wA-xuce&64%so!dd{k88>hhU0mf%NHN0(Tgm86iU|k*kC`8 ziNs@-Gtn7rZ3N21h!iPcp#LmBBo*TXUlATF%#kolSaL*n5QF474<##2)i!Z>^6+P2 z(ZeB1te}t{pi6_{!3@_*6?*3piEYcWp zGcckQ)SjZZWxcR#3Y`$Ouket5-_u2aHaayt>nhfz6Kal?aN#dFL$?s$kdM}Ik}}i< zq9iiV2&Dsy8Ga+cOl|ux;BqGLG4FZ;~8RJFH5!Yj-kXSen$uul0hGo0p36g9O{A*|T0JX-D zFU-(x$g%kiUD>f4S9YwgtB|^>I<6=s9X@ZO*6++;we;=}*<_AiR(|E60?j_fX|@U~CZSQwvk)*E$xMo?=B;uorYWC?aw}GszJx+-N`}U} zc?(X>58m;<_uh2fzMWe)QyxMWDYZNgfoOK7RHr?n(rhHBPCyOs;hP2;IS#%S^8SKzzMGD}8~xs@CmqK$ICGrd+l--)B(sV;C7#)=>_RY{@PGF_XzoSk@BxdX?#LKCLbGrNk> zMAhlB=R^zVpHabV(OArdd_vfLI9+xS=%FmB&|+bcEbuWU72=ptG=9W)n8weM9c%d8 zN{;IP;p?!K*tnLy2{?gwIWitfy5sg+&)#_T!Zv_?Mu!HdcyyvlwJ;QnP5uw!%VqY3 zhUp-*4&Wb3ufPRkN(`FH2yG)1ncEo4V#+WtsGcOC3Sei&x(E^0q0k@K0l^FNu3iA= z%&J^pH!*$(nI&L+h41&5=`+O~tY`Teucp)~Qs9;d38*(fQoU0lFMVN!0VONRT-vo`{V1VPYO4c_!NsDnL<%BebPA+p%`=Wif+ zI*w7l!S${IkU_BxHGl!4nmv3NaFiO&$g?vFHmlL5;5sZ8dCJb5-pIRSTilBif!kZd z#kU|ITdPB*KjGcKD>sC-D#j__=0z)x2YWac3SM>P6_+2rWN~2)n1*T)u=saXa93y- z%rgf;b+~=@D>=u{OlF^bSE??o>8*TCTb1K($$Da|k~4)Z2Vo1jtRrVCThFGq?#0$} zQ?WCxr8f~juy5%;s_my1YRzzA@lRFHGH~XUBjo8HB`Q*4oeGb!b16x z7C_HT7SvfZp*)w#P6?BmDF+4&W6x$|xszVF7<0qQ3-nf2U!?FJ;oZCj8`kGNamCR~ z5A2)YwR)(pn`$j&QptEU?A_+uMhCDn#BIo5$*aoj;_npb4kQWrkLt0J;B#h3NkDlP zuwdLMrb`3JM*)hL;&*B;)?Q2b1RJrnm2MhJN;DbQPY!fs(^SF9xXH8$;a?>Vr0f@V zKND!35uXu$!(=<{m`6CZy9$GmVRx7)Ef~s}&R_z;4T9BTuu`6|giC9f%iLmNhoF}> z`T%VTjg}n2d^$)ud>1c(2h3u=RF)dLpgnkK#IFbr7L1T7$$0Ni4IELZdlp{q!qLf62-2i9TI4rnDCL`YFXG%!uC*eo~o zenv1I>RU=q42he&pWdk0;*?BV2Ok@1SfOw{f-=h409mUs+GcSl63f4{T2OgKfkZVF zo3qEr&_i;_l8(g=xamj|AN=V1Z-3v-M=m+If7{lHwPV9PH?DX-V8|o`&9EEHaviOK z8FsnmUvwl}CNSdTJc^Vh$qpwv!bc_(a}qMK0Rh51wWvT2{eS&WHtMt%%kNc9z5$$SO$@S3OB01ieT4Wc5_hoDnJ zuR^IB!6NA`TY*H9DYeFIDqJU?ORyhB9zVJ^Bi^ni!?5uj~dEzHR zB*M?bqWcgMijq`B3$X(U*~wD{TwbpFqV1TYj5StZcsZ{GF9)maQv&46ck7a@_VNe(N?;&W9!5R^^>BRA;ZQ_=hL=8qSS{i{b1qpV9fr!E6~3tWDD&b(!R}9M zzT2wK58tHuh01kVtN!&W|UK=aGT_k3JPVvm+-dDWTd;2 zjB8A*+`N7Qjv02}pnOJ1l-@Upb(N61%96b3zCj!ag?ujMzQO9^eS<81PnF`8l4oVf zTi+?%rnKa;LFrK8>N$Vn{6}h+UNy+}_n`cT0&ir5dg&P^k%Za%wW}qCA11cw%|rq4KbAS;+qANviM-`nJgj5wP8i)@IYs_ z75NA{^J%7wHsEkba12r%V*9uRLO%=T0=y4-!=Oj7cBhR5=o(}`WL010_3yLG`mp9G zoW8t0Nt1W`4T_vB>qGfQkRJ3Uka)?sa!ne_X+^C+;JWv#!+WbP1Ve(qTl(M!u0MrJ z3tJ}E4)k?(Byn#{Wtc1Uioe^Qe-k)phJ9!G2;UR$Wi$jDQTQ_7XTZInv=cccbvi4= z93^7l8c?o74w8qkU~q8py1autb&(E8Qd2#i^m3?QltIS=LzL8KF)_`v>?7pylclS# zI(72I704alGCsC?sG*KdT6Jn%@nbSjvmP>VfAL;isGb@T_7BNE%2a0e;x0kLQ$k01 zt8%=2E=>)z>T4M1a)3LeDM3eyD0dItiaWP;Y8~Yt_4UO1Qq{N@R{={E!HHFYKX{A_oZCq;@d_Mv7to#BG>B{1@ybP{wFf%Nm3awq~LIj2lm&&QW+)wBimmzN96M>n*K^NgOXpevvPF5BJ82Esg*SW zKwg%%Om#M9(~%(G<4|#l&e-FktFXt@;`dn2G0nE1;}+*w_RbIvVkO;0QEbZ|vrl%k zk#(I)6hEn~t^Akbyb5D|Ks=Ngrd*$l{p!H=cJONcLQ;Yk@EKTFA4 z_zq=}iGmIHi=RdvhMhn`8F#kU>NL=M6noweiT8E` z{BIambYGkqYJv-Wa4JI4oFXu0nA*I;0+lx=9CO)1mMu?NTg)LWOMCZh z-8{0IR9Et1lK-5~K=ol#{kkvl*fiT)D%OtH?R7qfRyC@;q*NSEUwr*73)l6&ldhWS z@``oSRKAuf*5#Dms8t7J*$7w9@(}<-N;it>3ol`{>XcW21V|0J0kt<57xv8W-Z?ow zvZ|rJ4lB7VmQ8yvSc-2HvDLu8w3etNO0lsdHoXZxc}3rVC1pZTnOw}^!GbrQZ@%o3 zw$ut86c>JJdzg{suPtx-e`)&?IJv4a-+S)8weM>$)mv3{RrS6v>Alix(pkDYYiCb7 zdqN-(_OOKzARrz#y5aV)4Io_> z6=cYAK)Kz5``1YewWVNH-d1$w5Cl9QyaC~)B)#W$GQt9y?n3|2JAn3lGeAWj8yUv+ zYpkz|d6pI-@rw8c`F?c1f%y}S!Aei_4?^$~Vegc|50L`JAW`@RGbSccjDQu;k@k_? z7Qisb{UuCWC_oE2PvJvgK8}^+$$r7OYb;3Nr?-``2%iyldX5kHl7>~*f;pkmBG+Fa z9g3)E%JGAMm_cad7I~CFFATcM+-1T-F6_4y8}e#81~0*$kWTXZvl-7!7}P6XiTr{ zwLw$l`-3$gj+nnoJTB*J($CXt*e`UM7+LOM-vpix$OPi4zDr^yS}b%ZNqOJGjHgd& zUfwqp$jWGcZ%12A6^a-(0kt*4S1c1Aqwy{E+tPmNID?){@uP#nnY|UBdyB~mW@JF_ zkO6@bM#*F~nl`%ZNb3~Xj5sd<{}aqK?ybWi!LKh#E`ArNn%q#@E&ksciX;qx>}pQ= z#fD;bdwNpuIzP%VIG6Dt!tu)lPR6g*f~zGPVEo_#CS7#&!105}DROh=^5LZ@{!kZ> zmW2ueZj=L;_OtzNd#xc-iDEk(r*bi{f3Ab2(f)ZIS0iewDX95M`lfrod1^-@Z%m%^!T#b}A!7c`c+s*O86 z?#)(T)E4vYlK45}pApv&R5q>;kNZIlKL^SD=zj}B3SLw#*^qN22@ zz&+p@h)3;ufH^_tQ9h-U`4BH2EYP*$XqXC{in@37Gf_&3$_;C!@0bAr>d>UA+9}d8 z|2%fX1oHluo|pgz!0%@sO}dr7Cl2iYLQgDpEZ{Jhd#_lDHruG?`dOO6k^)fMY+oT%Snaug&c@7x7IskVc6 z1BBtWn|^v7Pz2sYSZkuNwhP)8f6IO)e=*zs$6n#Q7~U2MXmRwW=~|WzOijh&DA@`L ztE!B+9jg^r01vc4lh?g=TG|E*X>dkP9eI5KSeWzykY=(!JxbmHCYX3UB=Id@wloy0 zEJ{>yG<g z9`dHVKBI@o8~B>&3^dubGre)|y7M`TP)LvX&)Ird&XKpLkCeCjIsLf<Y6Gh0gq!=sy2&eBwRt}SfvrlL8H-gU6 z%8}8GP#IF`CB-3veUbNVo~C4E;xCQ~s<(jH8kvW5Ysgvsx@yHEh7s$lkAjDF|Gc5{ zMzP?AKr_1F$a#m?tzCn&fhA%|8&7b{)r``2W@}nEinC`>_|gbOS4nzWtb{NL4Di&Z z)4zm87D9z^)j~r;xScR53Q3miHChjF1Qf~9dKe5~sV0*aH=pUnp(EeD13}OcN$~hB z98^I`i1I~;1_^rt=^JZG)*!Ulrum2?(0${AcX4^_1$G7JT^(52C{gXf|)@;iG$ijCBo)h499qDy`KXFTWPAbgR zga80sJwzvv;mU;22Jsc*0@x8PVu#CV`uBX^smamdr30PqwbikxccX8k&JVCI_8sAW z=Ii>V^!fCFG)e1sqtgWt$6m2NpkntD8ngE)o zbv6rsG1eJpk9859&?@f{{^D+uIt6a1YOoko&^+SO2x-}byg?ufYDO&uf-iR&XW(fG zR#KBh2|RS+?vz|RuP3M=RJ>_gK-;z=lc#t`Xa`)-paRS}lKL&qW>Z=w|E}~iel|n{ zj|l_r2xf;|atg$=nXRbOCD~A}i)!##WDBpsW04i0A|l(v4u3T6tX8ciuLjTPS;xWo z%gVV7gp|Lk#FkC#*RC8V`698WYLBSAE@E6nPT-9A4@nL`OP@r^M|WL0AB4 z2GfenMk^sYiu1Q9iX{W=NK~K$d}gvGG-m_K{!vMR#l|sOup@NZR4MS?2HKP?jf5H7 zxnup>$i+F(IL)>(=_L&oTi`8qG?pOMBb?dK(w5PQa&irxMVO( zJRE2NVx|;Nf?pU~O4-(BTgta&_#QWnk!(ZsTrc<@-^XX#b$VWxoB}m+=hMQZm}X}S z5M)Vc^QMVaGXizw_(mkS#&r>W=JB!3S;Q_}ARP1VZ|@&Urhhafp_QU&l3p>*2tnj)YDvH!%` z2z}7bu~EqBU7OblDm(z&OzQN)U5I9?7wdxY(aUBuY}CJzY*?tR1;b0IL1kHi9tbnf zbRe1l0^Pv_`*&?vJ2e3S#-8?8q|#^QEm2Sj(FW`z9rDdOZK%IwZ_a%S9Fl0`W^t^o zFu}k&0+%Q(a13!&2o6^urVu^DeNGwJkj%KOA|nckV>b|5^CG^ZAj|#b-&Mm_W;^xb_XP8dy`{L9@G&3Y%gcMU?E~aDy z5En=VF_;u%OuiwiE2tTLz!4FHWIz-UML|=)g&WgJMbnKj6q=zFbEcaC|9i?d;M>3bVDBN_}AQrb9V{u9yF zgW;NTLJ_(O!Mi8&Q#I(J>r2#=fRH1foxP9r#Lq7gWP(#i1}z8?kg=HdYgvM67; zX*R|n(ZD&5NXLE-Bhne4(}=i}m^cUip0^^f%bW#?lf>RltXek8?X1|llH$C*BR=q; z@Iml%{Cjp+`Wz37bJT3-U<|SWNs9yqVR+_gV0BJhM|j$4YUIaq$rvQpHTN?x;@4**83E-g&L_Z}hp3%Mlb8okjMz zbf)JzZn9n*QopLX3(UHxe0}0%T^rU-tRh^$I&#i%_&n`iJ|cgMWCDJkub{dc)hH&u z{;)jfuB8|qfDirc|Eg9@(czcm2JM zPZVcB5#Cf?9v>b0f`8ut3+LO`tYh1gGov$ zY-%7ssmL>zq}1=^U*Or+dC#j%;K37JKw2Z2G7e}FC}vxE?=_m3n<@{jT2azP$wLf&AcHp7pTmBW?QQ=^l-Uc^`4-}i`k z-uw56Fy7Dil3hA!EV4Pdj3#@P+AFnj{p4!mAB`Ct5ozu}l=gI9Q9}KuZ09mZH+J@O zU%7jA4Ju7hOfL~kv+To5#SsBr%%s{>6QD1J$!eq5<90eYim8=Ii4<-Zp%I*WA6|w4 zeKsMZn%#Q(=9_N3;o^(HXtt6~4Z&tmpqd(@;R{t(KP?6P`Al(EW zH03BYr@J^T5CCn8YTJX()OSYowOh?F9A|X|o!~S3&=IEQ`MsR^9U%A@U37Hcp4~IM zc5GNT4nJ2_ycUqIgn5yjTium?c1+X|@5P!my|Iey9at2VkX|OwG%|ySVA~d|utXtAly8_h9G-6jISBa1?916_BR$T~ zhM*IGdO`Oga|o!qgi|2G!Wrm6Z7_=1Y*qrUa;ITRGKYZh5iC(9F9&rMM<9~Zb35i7 zLVm9WI^YmIWEEdF8YVp5I&3~qZ0E9FN*>6m$LQtvhW7r5xGpdp!EOPI65F`go>%jo zhT3H`?V+v4|7J415UY|t9~gCndQPZ{y*=GaJcl{DuWp}@!LIW;;dhFW4dDeQa%TDj zEkroaAqo`n%qSGBlBolF3RU$1?+28Hu=Rki3it>LD%VQ=gd)R3GLU7k(=r_gk^!oEElWo*xqX)&>y)n*dWZv>SL4aI6NP z3yWigON7F7$?1x-g+Y}7m+Y`4AS7VnZZO@$7oi{?h$xC|I5{2R`tSit9;nFxYpJh6 zbcx@)!M6bqDbLfPeH|BhxqM&0!@kSUnf4XdBih#{D!a^g6ElEij|!T-%dR%e>mv4) z05}#n2JETBg8FNHh=i)FC@U_otaq%*V1;5&feUns-g>^LPqCi#o@#ucF@G;_wsobOYh}`-KX#&M&|v$V&5C;T>ikDt+Mff3!_CFmA)&^7lB!lx`&GNc#*luQ?z zk~L6yeZrvH;Ian&0S1_(-BVGZ&PDpcogo3v(V~G{iRy=0J@G=6ggARj_VskMB9Oc^ zw4-DP96!8(8P9W6zGgTjwNn*e;&)I-Uw-LM!m{KsN%A5IqfgJQ}f)>f451x)p{k3^Hz)I4eKV2+!kaH++qI9Vmka{vdicmdhTxj_?-d@jAELJmIs++}}!k zPyc(GgSp^>Q70gvdNov`6^~N*>eONb>MYRhHql_@f#d8@`@0y<;YFCNeSrR9hl^L_ zO9;+3@jeIU99kP!a=9|YCjVO_IvrkxrpkvDi5)nSWYdxJqUDPG;NN`uforb5>Wa%R zJ@39FABOb2b`&Yr9q_#XU;aSQZ?~F^a+y*F)j^z-X>&RbDZx6j&goU5i%v?+t+*)_ zbwR%FcH1VrPEFMRs@cez3%S`7*|KPa-4jk9;`x+OS-*`K+$D(-1|A{%0 z%7@a!?|1DEfn1Pt?5h_UF7K(6GJER5<;y+1NVRA?qe5S zc>a;YyKbL3y>;`3b<3Cb_ja~L0X<%_vlP(~F&~fl0LHfAq&RQF*k&EdYy4i0!I1C_ zr}i@+WI*WY58+Q0e`*<*n7QP}?6Zi#Zb3l~zm0Ms@VtAy?g`8ra}`wJ%TNJ?Ec<}R zf$&%-kjbO4+v}b&D^&jX$M5@CI8%j;ca2_qc=yYM{YKGx*H)oK)xob zNTh*NnNiNd(ZSTX4k)}6ly~lvmCp0%^LF&wFpNv6FH^4=Wq%|@TX19jO zsE-Ra?Yi>V7Q~q??a#s1LCs*pWkQGHb6=G;_oKo~Un8MDmrRq>4qWBp9v<3Oq+f-O(a z1IgtA0}L+<6|~eR5uM+H%SF8AsMcyz(y14_(Qzws}k7((GaO_er#aBi`&i`)@zf(6)Qng|800{UAh{5B!oYOBhExk<0DZn35m zft4=SnpsHUCC3i#hwl*KVxz-UKc&5umZ8-(qP)b<2%Vqqwfa0e&UJp8{0XrRL>x&9 z<=p1fTaxWE)mW#3OAcBO>G(AWsIw_la4E0H{KkkyC&m8gk%RlUZWbsz%?(iP4`o#Q zG!7)?=iMe`0_byyMd#T5!-|BjU&E)T{7|hHi-^0` zYO#=Bj-w*Lv8=^oNKt;4D=pkQ25phAvA3-y5i6@Nug|Pf!=u%5E_rPk4M+TY+$Kgk zI&^=1iJEPOTv>R(NXE*P+ARrX>f2)SqJ}QTXgn&;j{94m*XoCney;h|qPistrw0U^ zMO^fCS}X3UNAi#G)FUGud|dS73&-0D|19^?AfYVCVjd3JI+|-QqvhfrSM!=e#S4clhQ-rUEkL7nCYxfiPt2-Va`CW8-c`z z!#2qZH}%?hkseI7#s$R1Ar$sgM*x;Lbk|^kkyf+jvqOE z2#DkB*NzPhCLzew5U5~zS?1j#)bTVeeNQ=<=09&rFEDc+Ex{*BwA2c=3~&pm&(JG@ zriQPpq97Q>3=r4^eB&^DQo&U4ARSnV2X;_bTKQRfS|ah3mH$1WCqh8a z`X1<>wn|Z_v*wxoHDX3h=g=qjTglGcF3>B?CX1P_S?=d3{U&=?1WO7W*tcs3dEW>6 zcuiq{tRRZwOX6HOU8%>?boDRnA=#ZrSC5EuK{){g;n1<5&{~F>EXaujB_gAlvKY^$ z1EO81?R>74-#eRk0qSJ87G)-wmnTt0%uC~4-^=1%|0?}%ao#0n3El-!^*!;fbLcpq zcb!A0T#h1=E^uIcGsQH|k$1gom)tLrgFNda|2^L2iNyu)BK_`fgsjB%yO-Eynyhqn zTF<*p=y|ogLPJJYg5?xNOyDGSn5J>v!2qDVx)7Pd^@|3{rZ^OvW6xQ7Wpx0e1&sd- z6qL<wH3HBK-g|lMIRq_bhCz9tUH!ffn{ylxOyc7e_ne7#f{fhtedwip;yWe zD9_P-*Un-_dJ1d%jx7A_q;o@VC4WIR$2X?408LPeXs+^|qjeLe5!bpG?UU6UdUvQm zVF&lk>|DQASOaPj1-nDL7m!=U8Qehg)y}}lW-#AhOCOh-wNuyzQ>)ctv{-o<2*lpnBSXk;YHw|hMfJ_q<0epROa9;T zPC8@MWr~}xe=%Qm98-F zv-g56&zhNIfydqD0x)sn08K!ge~1SKKB*W;`(P1!wMNcZWh*A zsx0)7cptk+iJxB(h*3o$2L6Fv#+_TZ)(xx>qO{OLwQkN~1aJ+?IXirC@9yneH?Bt= z*Cv639V!a?98=CIu^7$_!8b%bG;WXLwX>d=GxgB!5F5^`nvvX@S3FA-B4IU1Cq#q! zrHf}ZQ_l2Rx@A?*%2Yxl)6VW)YghCk*RrXh>|pu9jLfOqI<(qO+}1(+&z?C>e4F+k z8TRD(=_dgf0tUgmEK&*$k{5aI7OXvnI$G%g-lYoK*@i&{vyu>DKAv}I@9t%&LD<#V z(MHvnE5ll>4plJYy!%hOO-!?s-p=1o!mLkhJQPbFJ7k3JYR?VfU2R$PW`A zBD<&h3$|?THc(6!o;?Zt0AEEV4@7bx^vn#NVaD@fGEvwdAq+a~$PLzv(ZW{xZhh3r z>^>+CsDA=X8c+%NghBrVjt>?XZeynxZPI~wVs7`wA`4EQxDGy(y}M_2tesjp9u8O4 zR29XXI^G`YeO3UU2W#W@)K_Jo2VrewPaR4o5tOJJP%?vzoMaCJrRYXZ5$KN+LKNuF z3#XtiV^zGop`rnI&dSQMNQ4bVcfj$4+x&Rlhuz*S|mz5M$7FJRiJK@4%evvt&G9cb?z6Tt0 z5IX3~@+FdA+lx)f4M;5pEC}kHnq(7Dw<*Pe2k3ImgOqn{j@c|;8xAHvfp^HQCgU-M zjSTg4HPpr0;%#A+cP7mYexM6x1%XkmH0iHx_N%k-4sH=&!#9J0#3$N4hhVY39 z{`yBy6tB|}O;+ff5!6f!l-teuHdQsih?=RxK`~H*E{Uf}eOa+J;As>C)lG=XvJp$M z+Y!-Zf*~3^z*y$*vdnW*$V5q1-=QKnR2{#Pf}RG`BdD;=s1Rt0pJOCW=+s;LyXbF0 zJUl=<=^p~qB7@zTJlBzMEGX|R2Er4b83%VwU(UNsh7V!2I1oDmc9^l)5J#(=SZ!dp z-Fn0Iu-$C|6vb5&mkupm(q4tchr*!O#a0={1y4ha8Df|Cz32P8Eo*;o5^u&J6@muB z5MDtA@+-m~9A>B~JhaRRM0gp=yS&~HT~P9Va`&~}ZkW*i!Q=Ai73ny~k?i0w>7jUU z(l`D(^APX)dsvZGuv04A2(PUu=$Xu02q4zMh8R#nGJ8Y%9ehDzCBmhzm41Xu$Df_} z*r?1-oxJKw)y55em%m z#m0g+KxS@SDQ&#Wzi9Of4@ic!zt-f))b>}M9EmS~t;rF=jc0bwYE-Q=q>ft8{V+gw znX^DVU7C8c2TRkSIAXUTzsx~rjv{+FB1G_u8Y0CH+;Zw9$(UPEUKxN#*N$B~pBzag zq_>jf{ZEKo5iak)Ape2OnxvJX5*poCSjKg~L`HKm@anBuKxJ8a8Hb$KJQ?{tvQ#X> z6d@HW>G%7lH!MriTMnghyMMa|ZHD=A+5Sf&M})thk21_#lkQ=ap+iCX1q|_&BeI~D z#p||m;IBoBw+^NIpGDJpmr>xL^LdY+k3beAVGi^yK>}udZ8Tg_7Ah1OB&guZ_gmnh zLq;)Q&+*XzH+!1zH^uU#aqMZop%YR;n_S#~c z01DbA$1XUcT~2h(kugqZ`G4Jwj;l4A$<0mEZCpg+57E^WB93U5|%p|8x3;zVw( zb~2BjxK}w%d%$gTuSs8LrPzbtNFPnx<_;#wD`>#CA-?4rNwemI!y#9$W)Y>mFv5O1 z&rn)Zy?fX6hKW_8YBtcWx7Td<$1CBobm=*6WT)GhjSH)MiQgl#)1k&NIf;g{HYe9& z_g)Njr26OcG7ZN0+=tne41Mm?cir)!58ig`wO1WIFtc;xx+PWdwzk;eDvyWz{m3?V zk+>r~M&(iVEq+JHHb=UOUaNPWjgHIQx=AkIIyb3V=Po$1fA5ZM8`dowP4;&$0bENw zk%;ijAfYe4rPn{<`qB$p{gV&j+dau8t4I2JTjQlZ+%13=XLiGAlmS>v5d+ZfM6F4e zl8jeYSE1;O2PHmfY+6@+&bm(Ubv=r)`;$Gxy=|?vk&su6t@Cltt@Ys{i(vRk#6BrB ze6+@2jmgvSG(O$W(~38JKYzmZaXO%TiC9s3zbffPW&4oUYaxb;i)lj&7`aEuR&Cb< z%5s}oY`{9u+GaI_Ik2wkO4xAr?O6%n8w9y6sk*rCVk|n2AUDj%%Hzi#r1`*?9%s+W z#Oc8AD1MA^6`%}20ykO>07yhuqn5UYMOv*Sf|C-JW4W%3QcCUeu^s^PQrWQRbmcU1 z`{{G~`ahMf6Pr+9*5XoV{a1PG}+(Y5;;G9e#~F(6MMmJ`m&C7A7{^%>U*(1xhAt0xhDPe2QyofvmXV) z$<-r6z1_ekucS(a!4(B72xuj1KUU<>Lp7%d3VKAHcj61lG(Ffr4;@bZ>U&(9O#9K0&UceHx)p*$~_gj_xox?3qo{Y2vJ#Mb)wSW{u{JleJUt zWgzU>9Ur{q=9533^TD z{rDb*xr3i@`S;nm9gMz6F`B!C{V0r+hg}`Jtm2_kVV2LWQcMZau|?Z}uC=#i?dhq^48F>LL55 z_}Y2i9AEo$bKhVe)z%ISED{Fe^S->u2GpFrVcstE_3&MwgV$;@Q=IB2#JTe~@+VsC zGTBu-$(EZr^t7!L4`ZgX299^Mzqh%usd!zY^+Cq}Tbp3D+Db&-9)zNpH3T{9A`5 zR}sYUuAt^UIb9b-HrK~l0MR^f#buXXf=anC^KjIEjbn2;#0y%T66fgU@4l7Jkuc1^ zE@sVNRwFFT>32dqRKj&8F%9we2B3%V=yuJBPN$@G5s^Sz7itWO<)-QEZpqej>mjP= za)TIH50ZZ?qZC!JVjMLWI}@?!=E}{)0PI@+2|ttc{U;g2oIVo`|G$qu?2s5j%Jc)# zS*+MlGR1Z5|W#nIKvMA=4d;ebe*LD&_Akn`~ zlfwlbT$bF+V`nJZyhd1`6SQ>xMhGI#f?`?-Brv*o^LYqgi#N?5nGB5QI5Iaioa@NR zhIC6^EX!FXc^EjxgGTVeSmz>R5l0Hl$Ypev4~x6T&+=tsO&yc+fE}Jy=tB~g@ z(I*Y#MrH;MYo5Kxe!{;?2-80i!H(5r1lCY)$RdYyJD%Uf z0idA>XI6<+0uH*DhK+vL=KQXtn^OcWL*+gy=SX!hnB*#bDz3pXY+I~Bg1*|lYI z)r#e0>}{+kPmfqUd4O@oR8M;Yd!1xvo?G>z^bNjtLYf8+4=trp5=hem`8y9_8W|LT zy+NolZ7s1#;p(E*xo1Rr;E&V!XOxroDCak@ zu1Fs|GtChGpjC0M&I&ijP9oG!a;g^RG!hhes-*+5CmI9=!c0VHI{zIKnoL>fJzBR+ za~@#+nfvef(Cw!$Lel<@t&^))EpKXUsHv{X1pDamNjA*arj*mZ+VGaEzc3O%T7lUf z6GBtEGA497*v8<+r`k^_p}oIsiYA7nTA1*LjC#cCKC- zuC5M;8xeBB;lFen%mV@!ira{@V=RHwLNaRwCdiP5OuMGY764{ZQV@`=RI6^Sjv_;u zK)?3nH_d7VQO#l5GQDoi*a%ftPQ*ZU>mBQT9uBKSa}hR|Q?x(aj`)=H3a2}=K#eEI z5V)$E%-jWxjNlpi6oRrSu0?>9qmmA8VH!2XZX>Aa=0;>K76twIOwyk8i;v-3e$+Ibq2UaYmG?I`g(9pMV*|Enp?SaW!C}Yc z+mSP_*(%Lw8}fb!CYDrm&t13QcJuXz5Q90jdiiK|Tq{^X2!TWws8?}AR?N;z;73%o zg)Ahniv_XhOd&duydhJkWuA?Ml3evz6o?{}2G7g2;I?TdDT>{~xk3g4c|qY37OISu zW#JL92*#+c9e8R-YjY!#!C3{XK#dlds<8&rX`dDG$Vp)X=^&@vC~6+aqur;U=ER0yrsdN;&1s!<-Wx=8I8vOj8T9qM zaB%(O8KsTuJhfT#%k<0+`N>Qbp*l2Mf_;xyme*C(&6};ZKi^Qe46g0ZY3b9@2foEV zo!%dcp$4^ao01|UwazF4pEw|tii~iCQBxFky4Fx;4mH(2m%^5h4fM4%RWGSof@s)c zluxblRf#BL9_Fj_pdTtNIuAlT{e_HjP5xbaf%}VS&&~+GhyZyRR#O?$hXTv10?RAl z#bgzhSK1QzBq^wf%1-dW7$>40u!~kJFGs^e>IbnLs<^P{oJOMPnt6jDM7Fw$O3e-} z?dbxletUI0C2E(3iVCp4ggYVDhd>)lT-)a}g5UFf{*K(0-rr{6O6pA20DLU~W#pE$ zwOXnZ<)vP?rO;aF^O&-IY_EzOf5EreV4A)b8WX#a0THv?;+173Ubn5#Ug*z^$!$O) z2SB5T%d{MT%zTHFOSvq`UjaRcp0omzYO(7XJ5wP-`8(U|Yb(kM;)U@{2tUM3?Vh}< z+>mwVCq?`|bEnVztlz?dGT)+b{Y=7jCQM&^OM(p0E8_dOj`R|<^8F{g%?pK&RL;da zXc;`?n&LFE77~*boCkLfSCLYLdw_~Edw^yD7!E=>#nDlx?4~5`vFQx}M4u7`5|?z8 zmz5R>nn=g{30zZN>j0RuarVScl1m8H^g3NGF(3e%YGM&#)0go6Yu;)>(iAD}M$T*! zm7o>4QWY4=4b%nEubD1zp>os`+ySivpWx)`@nxvq2aSKf}mvp=W|mvQ2y`Ej`nApDr?d%KO{xaQ>GWKif{{iPU3cEG_?lH2* z@Kt1kJ*cgS&>8aOJ2j;$(`VIA= zKRqUPCiVH*3#4*7b($8u+A^0fDUm~xhsf-ra^KtB1*!45|C9PE?~6at_o=xxv#nA= z5;Z^3nWR!u(j+$I_sF>EuxWRs*3JDqHOu?pPxSffTxfQg6h?h*9sM7+8hHgiOMPWF zR0)80`Xce3)bCPn%{>XP4CK2fCG>td^#-O@mJCS>1LBUDvBYt(-ExwGV$l`-UQAs% z_lr3P|1SPS&&N}b&fX!_C9CmF%g8te1ICF!%vyoDQ=BDJhGdp0Fk+y4KDQ%vM7m|vHonhNh&-yx{NBWT7dKF z-{@%3XHC`{W{$6~3OI~6*9WQs4L4)Z2XI>TNE6YCINOIqP@X>i0Dl@pZH- zZ^%aseyK=GBmu>lIRQ=1wJAv^75bcZi$N-4MFxAVk1q|I)zUBHzX89KVYShSagEb; zXQb#$EluUc2A6kyXF>7fMP+N1Hwrzzd)1OiiO*%*vr{JKh&3Nqm*KEqz!Bu#3mNW9 zz~ML)d?`%B8>3OT}|5LTK!s+>CAA89(18m9 zzOC_{P!hEgvbKZYmZ42&kc#id%%zr~-x!IvRce2-XQr1fold6yh5uoY@b@JDYZA6M z>`6$CvfB)&q-v=;8K=t&=s8j!^PC?r4Pz~sUprGY`E3frz zGOjEtEZ%siZ}UY(R#QCaD>J$6L9@#U6n8mN)LmmyzuQ(4b+&i6?QXV)Z9q@#Dl)k| zHZM?ltR}0?R8+CSi@oSlTiG3k)6n&TQs0V4sWJxr7`zO&CWY$@m|Q4Xh?NkI0P+rV zIo_r-;CZwwV$b=dF7O9i^`g{Yym{AoqeHuQG*m^REmh?a!|829s}AlP9_nfwOV-p5 z_RyKqnaJ{1!)@R_VX0QwToBQU&D&2W!=E02z~cwn+j9mNIgGC|{fg6jNR(vA{{cHx z6|fsGj}c>8`bg?IVl(=`)W&+7JMf=NIh*AZx6d;tZw^45smB*=z|^t*iE_PxoxAx4Zmv%VV*L|MFX{ruto-#A2|1 z@LqYG^UAOkO#1jz)6Zr+5u>H%bKqG157?ixS#s(Z+5gIA(Dk|8dZBx}AG4D9+eTC% zOZ1x3y~Q-55uLL_2El6(jj%t7iyLryPV5`rRZ|vJJFA+eYfA!aZXVv*K@pR{H3koF z#y#*6Khqfv!vlk8MuX&IKH_H@=OmU1s^@Rb_r?z2xiP$SaJ;m#OL=qq$o7whELEMQ zYXg?rvhoVrzs%Ty99{{!;bR9_fX2?Sntr*xjUQRZwPS}*Z1B3GwNV=0aB9cM&bw_c zb4h7Ka9#QXXC2?;lCsC=72D-BPy1e?tFm+)!@Im2P8>FzLLG!!OJH5Fp|r&8vfaIN zWXJ#f{2}9+|MF|;bF&X}S-?E|AY_3@BxO(^YP1O@5lI0p?%SU99omFBl^_?HF`?2+bh2_W~mm~S}N<5etlun7AtxoxLUEPLeudV2lyOQhv zb!v31IyKudF%*hzy6&o}Uwoir3_1|$1Al=mKdZ^|vtP#kK$geZg9~z&tP=~D=6QSQ z53`#7F#81BLw^wMF=N58K`cUBK>PmL81v75k?(*1>=)7g4BFS8RbtTOc=S!6@0YZ` zUqavK(6>I_cOkB&_bs07W{tD2@V>>fub}Va=)2~<%GFZu>?u|^`#c|~clLRV1Ks7% zTo1{iTUz{3t_~v=vo~0yd|b?dy#c)kb0B(JSgyd_SYXJkKR*QpX81AliXOmnbC zAMhE?_@PvljPYG3o=xCH0E=3IKafxb8%{Z-xNJr^}KN=%- zr~K{&MrxjY$YV0Mv4a>hLZF$D2_UuzLzt`pQ5NaY$@FME2Iw#HE19}!ra5_o)QIe} zpJRl9WG~g`L@8g`%#pYVKn4JvBbg7?Iw^z{1;C*zL)$O(?A^U>)7U7Gni?v@p0$N* z;}MiV#a!opI`RCEkB&7COAv$F~3M;VZ{KNbA zZr$A8R{4qWw$6A&_fgFK*W6#Q7g_6klh%nk@f1Zyp0aYAg$NBy*b2^V1yrilFs&OX zxWy!z-$FqP^1@|@(I-6zc5T|QdJH&!U7m1A%s}ij8_fPb-{*hF_n~Qn4tUWWh=I^N zwJn^gzcn67=MUDSrq6HW`{wrYWI-#_(^sOSPJTcr9J=%V`acn zAWvFDh7n09Cyt^TIZ!^Zp>azh54XtVu#>Bg+j2nLS}(zLINV8opPY}tdkG}QJ={8N zoXP5?K{n$fSeT=;y)u#zoJ#~w%5$@GZyWA{tUoBNliC4%l=hBbG6qJd7LEa8S3HH) zB&}Psa#=?^B2G(vZfVdoXtviHxPs4ZaUEQ^gidav_a6!YiO8h>M-75iB8@~)ZG*48NO{uR-yi@SB>MLY@7F5ttKW#UuR=ZpjviZh` z-*H-EUH{;%?Q?hvgQ~qP;29lbUzbhso&fVW%#|gsGBs8B&Zmz~*^R|6zq&K!x2slF zR?Tkq>ln^fkrH!nt1AtI(4I#THFtr=lfVkFR|-QNX&{awCL6UyVO5``r~%HC;s{5$;Dh&do63h7PhxF$kA(M?HWck>GBZn}ZDnh7VOe6hI(o~qjvF7HymQ+}wqMrM zJt6<8$GdxV^Tu^8wauxIuYAXB4Hny+qoc~b@M<-m{(5SvHpqmt&PR018ZRB<$D`7bV=>p%1C)hD_{o4j>+mq+-=D&CEMcR zBU1^Zx0Yf0*|e3}1sFbP6T-9$0nJ`VmM@T`k$rh;YN)6CbbZUmx@fVztA=~KZ>g8p z7#mrP*_;-irJ%57I`v9JmAYqk@5qSLRvfhQHI0lYpBNi(+Dn5*qc?F+-0BN24|O7^ zPo}I1^>;|nFO(7za>Q$K`wQ`77&;U@s3O~(q1pknlNC9@g%e`%`^W^M{e07zjzC8( zRLo9Bm!q>^fjZ4J^HzIXjDgKFaYtI(+m^|0hZ*o-a#L~0t6Ht>SF+dfm%GZ8Po{nV z^yt(PZ>&;&DB7t`msMCxyv$_}7nlC|PpZ{j6g9u3Y>q`|Pu6;ioP~DM9~(i_t=RV+ z(t|>lp!f}w0e){3-$a_)#A^%KtJzlh1c0=mP=ig zffI-R*MI)2#b>YZN#JUYfjDMY%LwwG*c%s{Tgu8wn4q zE=A~cw!l-%to-r3xxV^p;)sK}539OQe=9*9&gAIq?%cPO&t(8|=LtifG$XZ9Novf_ z$e_vryFx;_MA@S9t7DSZOirv^np^?^@rtt2kk>8Em}VdhG{X1HFI=jkJtD<(ZRp}ZV?&&=gZT@Ii z@9?T#z6a0{4)pd6jaB)hHg<1$B3jaO!Ryn``l078`_9)Lh3xlZV{3=4RykN`bA~(- z_KRrU<*DC~Z=y@)bkajGyi&P$w4a0vz~z+L?1RIp6Y&BE`*eAE++y)MgJ%3RviFf! z9fj}wVY12VEP$_AliRmK{^%7_M}VRNK=_a{xyTM9Hs?)V{1BCpNwt7-Ef~^QhL(mm z*PJ%Ms9mOT8C)0B1;c4e(1r?`BYQ`=5U1Ds1Qny-K@KsJ8Q(O%X&8_$)gvk*k`;QZRU&N**f1-g3|cG_ ziU5EY6JO^W(tt+zHPagr&kxE$jxpLUsQ6Rz^o z?vg_F)(2D1rH)Szj5x&;P+~C+jUW8sh0N0w4$tmyZvQE}lXV1H!Pg#2y?sMjf3I1z zE4CSHyDL5N6->328_J@NfXi%W)}o+$@I3eIi)Dq%vf>>*ZoeyJuviKp%|H#B`;Np}Y}JNUQCb+YnNV zDh&5#NIi3ZP=9IICshHl;u01U-rW%o zb0CP-v6Wi{VIHBPy+&9GW+a!N*&QxBY)f9S21VNFMxifyfGKdtM=$cEm5{B4d4h}@ zquO9HE{-s^yuZ~lGcs#L`i0T4rkzwMmoahE18H0h4~?vKGtAhb~-LZJJ+pMZSSWeG7QJ?TN1VdK4qPCYcwE7&b zgU39+U}>q_;Z-l$-rfrML0Q@gx$arWb#5spjS3rHki;;GK9Ug%DME7y2_b|iBTScE zMHDPa5j*r_)R1CQq@pkgb~>~|90c4x1QDnq}gXLGDr(Z zLPx**#0@KQ1tBJRA=__<95hTGH{2ag|HN>(mOfSgiK(gFU|T_(?P`D~MmCU}Ej zGGax(u+*XcKn~9SUx(w9!#&+M*TMAAJIqZFWQb6HUVT+}Z$^HCT+uW1@t2g}C(7Nk zMc0%FCP2Dkmi=J?nW_Z*v>CeL0Ql(==}A#s6HlZ`l~$M$(GOREaUqi3)HAZw6GP=K=|SWQJ{epi5Vy%$jnY0CFWnRj7-MQJp;ijzog{nS7|IgDX0>6n z+fp#NEnFE7uL_3?BNjZTZN!8%yH3_4$#fM*Gi|9bXTbo5t*4vS+M?X_^!+CX#)mII z7;XLJ671z@+w!4H_b2M^FZvaW_Z&TQQFU9;#(KklQoi)$LmOrrS50RmuaV`-&YA5F zTUk(cyztqbUpTR%$Z2xhxNOmiTD;$YuDwor`^*x;?1h?5QTH`4-WU;-Owa~EsGA|? z%fmysGY30og0UJ^NvOoP*yG0h9>{`IH|O*t7=+2JZuA~@yjwT@LmWgQ(>vLE_Kuk> zKgKWwu)i@?IuFP4v0XK>aA+hRnSqk(!wv_*!8i}2;0`j7G{`Waz|KN(Id}^pA>tRr zF|}|nV*t_$ollhHM;`ozlae|+@XW@$g0_EU>dO~i7pQa>89ZLqZ*#-5&~j<&{_iEs zwQQo_7fAKmte#`+#Wj1X?9M_5xMI?;sCG;F!IG12eQvzzWUD*$W45HkWT{F0?4JyF zFAVSQ(&%O9m3YD~cbVZvQ-RB9?@Zm(?+c|Adyu)BR|bPYJ7&tB+=ujDD=vV|?X>&{ zn=H55o7vc(a1yvbozO?NBMxV|bm&YEOe=auQH-2*GB>M`%mK3s?7Bd2+DJKIjseXy z3Gf2^+WDmt6a6^P6$;3*fiM6_*+^sm|Ta!h>+N!$kgPdzHev+ zAKiSW85^v7mGkMqCtVmXEMgyxJ~p zS?~jeU6WUhkDRJAxa3gfXVwl}uxe>_2}ZB#SiR{|Lu*}qWdTt0l;J~9ymoEs_K{7R zKwM-m`4GEcyFfqZQL+cUq7~C;}~2`B#SM>zDyf>q!A!std%O0 z;nl0gMr*@BImS!aPg|#(PY1fYI*Yvs(TTM;D2StyI}zn3v;ifBxae?bcpq^0(Ko zb=V8Seix!dm~Iq8#G>;1*}Fo2y7$>PW*=7$1b_U|FQ@(hJ@swu#Ydr|9hKIKD7ykO z+`u>qhdPS6LuN=52R&mFL+Xe2dm}{ubTIs;B;?fHyVkGjX+?~cEFEP>OgZ8_IgxbL zofuHY77U*flrU~UpLg4Y5dfiGxtmU048!?w2%SFNOn?VRJyQGm!k(eF>o&X@Gqe7N zW8K5Oh5yvvaEHHVINDkC182aRdehfZ1FiR(n&!vhsTz2&hedQ11-f4m3?a0N^b!1^lH%IvDqC3?us3$KbIG=@L;^r zV<@x-;YfyG_HDykkb8DXtA(F3r0q3aSDCcdG)se~Na?5$(7CYiR*XYaY}PAhX4vZsYn#%LsoYhWZb?6n)2?aoW@ zdDc-_* zi~+(hWmf2s7$jtcGfgGzwq!tJNM6Gv$W9+FyN$?6wh!=72ME|e3OsmV|DKw#@L|Vn zcx>Xb+gmB5Y6x&JbO|2_xD4KQV*vaG3Dyu#y1*TGAyM>N5I@jvLs39?{d z1e_~b(I70youB#EW31uvD}VLyp%*^ohViL=TkE>RWsx$IO}5t+s^$P5^V&W3lA{A| zuOVzY@bTnDkL=y?nZtdzos2ngXag6P+V<3z94HT%)xtWvY%`(r!40*O38zK<{5Y#% z`&j+MyFUH;RbNfLJO#5HvqanbZDzbck&A~5qIlv90v*mO<7yVY_wbJ%Kf3Lnr?-7G z_1MnP4vSNktzCQO@qStJ)DgL9ri*0YcCT zgj}9-0}0n^h8J^li<_x_Q`08#wKMToz!8uZsOoEgnxgL|17 ztb~O`XzE~6qL@sjWHlIpzP4w#z#qd7%+42GggqgYi6 zbNbX74>NHohjjZSNEi_4Naj&3CX33QNQhdP!DEI5b7Z~pFcm7RiT7N%wgHU}vnBxA zkpeD5(d)w{py&kx1T`qIL@YAW!u>(7Yo}JNh*kn#HC7#u5V9pd7mBIziZzIs8uoEo z16eYqmxM>584)84Z}cR+9>5WyDb+42sWQOGU#H%w~BKnhs4{@WCaEj)3SF zWMGjKG;5@+{s_Gi%cja(`Jd14oasm6_Q+7IGF%yHNFZtks|5{uQm?@t!q1Cvml2cp z0lrq)!lmDfnYLo4(5GPlOJ`qMWP@dfFOkkdFaj(R?h|sP1i2nUsr-6S3LwAoI)l%n zE0QV+aWJbFxfK`hg&i^N&+STyVLC|Y{Lk=v2&o8SyCWtT;5c}7Om>|RvvIdEwg1ne3^2}+j zOSF+?itjOkFfC+)PM5Ug901o#hsGluW+arlY}nMc`CUbjkCo~PLCZFgmQ5@KC#ZKr zq}{Sn${hxoiJ%`IX(vNI{Ur*$OiG=IW4ZARZa#mkXo}DsO%Tl~sZT`^0LTgkR|+K5 zAoHb3Bg6=hh}kS5Z4`ec_h&1(Bkj?`T~&3VUeJUpW8%73k|yMY0FS4HtZZfQ1dxx| z$R8#$xKVge3q?iXog$waULEKHkaZy=LtKzgd;2$CHL~}j9iLp&x^_w21)gP6y;iv3 zD}3Q-=LU~=Vp*V}ved(ZCr9enw$`2)XgQTOZl``1Y69w6+c7P$8rqf9GlCCBtW> zdTCO6`iu+uH1T**6cRN~3{)sWF&h*!?IVCNz~xOQO#~n&MCLN=rKpmi<89=1gdqiI z02ZbVklzgm9eF(vvPAi73wM)AG_b|%E32v_@uC=ZzQ6;uLr4Wi7n~0iHA9*wNo`~T zC08c)T;&!T{gf2!I?u;31{6qbIELdjn?H7F@Vvq3V7Xl>x$gSQpF97~L&Y9vxT&Y9 zE8dd`IgIjecC09Dh{cum<86Gv92vK%rhUbw0MRprp7UGxKfBcOxf!aWvGLqj` zFe{2lNw-Xf^BXa?SITNAVW+=R8IRP|SCTEU!^>^KAYh1D0sKcZj!+B>+(bmq;sQ|K zoR=t@y*_k9%o>OeT<}7dQ!Y2EQEz0dwa#B+b^p!Ak|i55`3gLuGe%CO<% zQV%jfcL;3g7+5kEMe)2Mj*UW;2uO!#x9b>8p=31Oj#9cPcvNq%muVrubW2sDv9hY6 z(&U3FiJL77f>KWs;X(#bD9ye~ZBZCUf$Yep(Id5U0r z>wa{oYV21V)$N<^vG@|9 zdwvbf+uRMGALj0#MPTm*TvoC#nH-&pWV56=M|^pA@BX=@un5=`pfrj9&Fj%Q(r9G zEidi4;pp17Z3%m!!&B7XaM`2Dp?y2T)!U*))k|X&P0?g1kqEi42<*YK)N%PC!&9)6 z0kw?~IpQRI0pbw+$v~-dAQsZ;6vCvzxx9vMS%19?LTZcyK+b2?<2VbyD+(XEv= zZKGJVuMM{bkuedz_KwzW6!?K1gg*zu%RA9yvko03P-nm_WD)w^a|U{jy?BpuUJ{X{{s`wuLYM()%e)z zo7p{o8}nK>tsB}Z8%*{jH0uqprC99>DpK%?)=`@f+@vyix_CjrNH>uaHob7JDwiz(K5@K$EzCN)M+G zCCl(L{E{Pz=#6o4^#;Jwj)eU_S*ouMw?*1Yi+vUT3V5x&vX{KcF}i?d5KQ3B?}#g~ zPhxLc4In-_A%cJ)*3fnJcE~4Ft8GqmN#n-u#>vjwVv}t9`^F8)diZCp>?>BM>xWCP zS=UzFv*zk$L)UI-+w+M0r0lZ|UiGO<6dyU-#=qA+15<+hwtsm+U=uQEpf#`JI2hpfpZWHou z`sYY&t&CJs!lNHI4NJrGF1QO~vvI{(YipM;>|+k|#>6lINIJ z3!#Q3YZ?oZ%?6vLd3Sg7x~`Z_Su&jPEI-Afm3C9-hUTKq@m(x_0i2PaPhBYceF1w6 z>EKq?WnwS=z*Jyx*{r=6U%S1(s>B090#DhjCDpVgGYYMI{BXcWeMEnJ0~Vn8q` zg96{_!VST0gG@rv`+=^`+Ujt5aiPN|O|dC(nE%h-o4`j_mg(YWsrA&pSE?$hRBB75 zl2rD6sce;f>vX5n>3vHtbklT0FEl&NKsShp;NYM*3aF@vh%}3WGmPV?*KHgJy$r*6 zmGLspsKfYo=5iUj`v1J&IaR5o6S@)g=l%T@1UmJ7r%t}*U7z=Pqll=vDb*v}i}7kP z&JHVV0G5T5t#j-Lj%;H?&MsKgkcy#zAC&Ool{0mkkV{IAG}dx2>-**)5ia=Bf1dp6Pwj246$%G7Bi#Lf4vKBbl5fnXy`@%iydS&ptB| zG9tPp`I^UYiQS2z%5YPOUGp-0mf5S`)7`$lTre7L>&X6F_6rk%Qmx2Gx=N?-e|WdQ z$Ok6&*&Cm@U;Z7*PI^)OQ*Jl+6PaZWT`6EJ&*yRPdNjq;U0^V=aD-Ny>x8W^15yk? zwzd#uBWo)Mo`0tgNyE{h`D!va5k#W0D^w(|{Fs$q14q?Hy;w5+u~1c6GF)D7HA1Rl zIhl3Fd`D6mEQn9G868~NeV5e;leV$}r#PK#YYOc(Jc|_xL7HF}@!x#no7WElaE;r) z*ILwhZ*U;xzh-9Z{B%p=@{P85$;g9~bpr$8!Bph?5qDE-TruNj&A)Ott*`Ulycw`~ zji@&y>YIE!yFIEW-`}@M4KR10DR$UVv%bUDBUSZ`oXRwf*46}zd;A@>rL7a&TH;$$ z_DFwa&bABJPOeX0`^qHTM-3PXZGqaM+V)Skcxb$ zkzsCt6EX;36IR%l|1rbF1VC0M={Os2113hzEC&EnENS!SSny zqr1+2ZTIjUU)wlz_qU42uD^HU%5R>UyyZhPSN!z}K09&Ui+4>P`!?PYwfyH_Q6DJgvTVK+xfHiYJ_9}`%4AQF z8j+E-A|nju8E!N!KI}6^kS+flAK{_k4qEm(#c-aAckav)g)fk%Iq zBRDWsSfmWb3)ZpeHG{_Ia0crJG~ak+{P*gD7-<>*M^; z%#l4G+fv*+Sg+QkCU+mqE==Bd&&Y<)oESEOb!vFK)@Ho?;J$zQb6IA*DsF;6VkkivW95smRb0B9|^cGQ~THS`yY1+^t+3d$D*KC!VU+>q%r#eLCC#>h+nE z8S+#X0oThMic6*vpDL0I7yHtcrcy}u-Mu$OI)1}1r(;yR@_XZ52@j*A8koF<%@u;T zy!}F>24?EQ3q#>-9TMMAWB_uTdl z{spzoB&gFv?$(hH+em1VPI^@=Mo=Wlt(PTc8Ob|b8fc;1O@YwFohdjBbgPs>xxfV* zyf|dT)A4fjULt!CC{8l?=|JP1w6&==Sz?8Z4NG`qfvWY$HgKSLi6kLz5sx@op+YG9h$Rb1S3no!CqRA@ z{T+SAbA8YSU7bE}nP`H;F8`hg#E?T^Z>GqDf*8}f?N&isqV|X^av#^Y$bG}_j%-U) zBN8gYnA0Cl=`E_yz5e>=D3zvlT9ZZ6BR~GP&%h@KP=R;f{x{Fv4kb1A{;zlLzPcBr z4p}Z7?T$E(eRILgtDkt1*IEW|A2SAgLDYY_f91@+nSD6>rmf|Q`86GgbIeJ19=r4J zEDQZ_jPbv;{P3R6on2wE|GqbaSCC(O?lJLCp*u&pPdq)^5EV5nCq)h}5SK{SGsGo| z^VA3$Ko%4K0?q{CCK0o8FRmah?gcY|OC<`v3#L(6tbF<7X)vqlhKUB?i}DSGkwj3e ztRfXHs|-_6j1CQ%rvx6VMwn-*+lx@PL&;!ic|^7$xeGjKa-d&{RoQIqa85>Wj}7te zt?f%)Uf!p92`M!l8Jq4*WA;D%jWFK)GDpBjEo{ht)g280n+k!s|Y zlY<35uXtaK|GH?CEPB0qO~Sr4+kfBCt$$Vq1VCs;T!wceC+7v zRA;fSZ~GOuG-nPSSnt{Es%mdcH65e47PvK@J^w52yFwFTRfyw;fBG|DRX8<}XON@Z z$dDi>O*Ng6eUOIKdtR%>QAEURNklI{L3<&i(27!X7SS!96%fHAaoejH9@zjBxq z_8zRk&|-oWf|OE0j8(isc^6Fq6eX8BWz!Z1(?O?I0V;&G(t;q)p*F*@iccMzD zz4AK|y}!IBdLZfau)FD<2pwleUlKxd0D>a7pvkdDVPWvOUR*#pgiTp84%;(7C*L>_ z!eD#w9)D<@JAZH(H274EUqbUFYK5-gUXks883zN}bb^Wj1w+JDXt|99YL2jK4jBPf z0gek*z#XN;!1Ar2^YhV<;?f?FZe}O1@Vi%-yE1K63D3#IRkJe_qk{uIEy+ZA*zG{L z1|>`;*wlbb5Q-S`SH@_IzhjaL;ZC#XY(RH2de9kN-Bd9~Uo} z?NuK~jm=T+MId4k4Og6@211zw$p+qeJ=(GAjc94Y8TRB$ZEz5boMc>9awCV&S}t)e zb`Y6gN{hjIkPE?EfbCR~%)gZHE}V)J;5d9}`_}dAW~M9RWjk3p4{E|!fx)j>&I2el zqU1OjFk4xvDa>0qq}BKr@Ifr@NuL1;B(RcG1^m&al3`aEBRo1&z-Kfy5si1Rm1};? zl2Y^Iy!P=MKJ$NXbcH;6yBIPXO;$5+MBQ^q(*KK*klt!k?Y`qH|GxRm`#Z)vLuJv4 zvf(Sb%8fVN-G7I@==(!s1$9Bg;)A)mAeWQX1w~aFqrRldWqx4DU=KNyycm$Yl3A@& z&t$(>9}=3gZ@zg;Hv9DTPM=lM7>g{SYfe9SedinhGi05WJLyha0A>BpI{tYn3`eI1J47 zXT=AFH3xFZCOHUL?i?wQfTP#5*&Z#~;NSgm&;F&H7L4m*WwI#sHAb`Tz-b>i%)RSR z+Ot2qMD8fU6a85ARn!a|;QsY2tbXQoQM9O?lk~U=2o8YtPNeq9SQLchk?R7W0fyQ?NuQ64}wm(`4Vt^@o5itocwrrHCRAu2&q2o9Ybuf}G9>cB<54CR0L zdfFhZSr^uXH4H;R4m4rJ>PocGun2onI+S_e!jF%9?OLs900;PcQ^l9xP&b^>S{pVc zhF?i+*dhROCGfHFh4)Rpe7G^B3Fr;xkf=55T{^!e$lv&rtNz!g_wD@5|GDF`Tcqnz zZHQQhfdBDmpa#Wc)C(#@pWhfAIjlCH{`I|7fE{YDs*0;@lDea7q6ao5_Nec^`%aD> zXxwZM@j=JLA)UpfH>xGkP|NRs;?3LM%WJ=O+%J?Nccj*74O*k>Ju^nTod-vPWJ_)- zGWyFIA2!dS^MbeyXQGEYDMz5I5TC%|AjCJs6HF{NIWIkP4yZzIbfAX@h&g$}8 zor%ER;p-1R`n{RY9KQWqPFJyA)v`8FAM&o>dG)T!x$)+pTJ*PUm_OFhbIoPFwN^*y zt`2MRwhe^#z!^)*mD-??IS*G`8_22Qs zx1;3I+zA6A_jlHLaekijS7sN41=NaFa8-z>v7|9l^wfd_r56B~nm- zYVUwTx<@n_?IUk}>AGikZt=!WHdqrQ|75eL&KeH~XHWUe8#ELnNWWT$$_^Xc} z{d~m$xJ??4P~<>_U&Tw=tQiuiW&&i+mM=$P&0jfa@2C`;4vWZZPX) z(tW^IX*CO|6gQ!=%AiG+h{3>3La6doI|WXiJqtW{#pMgf_8i?jH<9UTudfUilhiju z9&@|-on}ZB8FRsEb>B7786^P;RzL-V*qHn-#1JIJ4-2+Sbsby8JIv?qx(E&07SN!f zBK9PLkP@+%+j|I#oX5q>fa?{0Uq8I^|1ofg^GFzr0KvTqpOL)Yx*~qsopU8_Q zT+HQf&At|T47?Ly;9xIK*(=ZSqgNh2uz&03Sy0ZBfnA&oh1{0BkzHuob@DPsoQif= zrjWQq6C6Dr0Bpl`W+ph9A#}FXLscNuyF3yHw7U_CLSe3)TJKn z#;Eu#ld8bQyj8Qc1d7EcZ6)oDn+HE{{?a8Z-pu5`4Egf>>5^L5R%cf@m>EfSvAiRq1xdPN&vh9Sn6R8~k;WBhoxk)74!b zbB^|bPNK!-7w$5Xo%8qoXtt!$HRNE`Uj;)Q;wKYbgj@PM;MyE0O z!(X(OxCDzHm_(1dsCo`gdSHA3$_W2s`baaz&hHVb`=etbN01T6qi{Ch2cQMRNfkux zD7EcWO;WdU>ZoT#@t;z|m>;ZW1#WCGBmG93Wj2L+?a>7EvE;df3mewYqMcrUFZiPx z(U-WyMaZmGLVd(Y4AwSgBe1j-)KXqa6_{@1sUrVM76Rz?DE*3T^hJ(|{2cLqv**#R zz6~W&`{o`gi;JoirQofy%GzC0__?vAov3R7KL(fYV@K8LPxJs>E|H= zfT$sbrqbzDqxhGKsM1xseh8ad>^^!Yz0DKQKpRQow|NA$_j@-l@vgp~gbF&FTz+8h z!j{?b(fXwCkpB=8d1VG5?wP3wlTyZO2M*{Wh6ZBEOa@$l(13bk_9xU8F@rct zbPy!OR{=AVlB3E9#6*xk$5Uk6n?KZ54%BL2$G#&0(XO(7=GsG_-&@;ehm9y^A4?6j zgUr$QP<`1UM|(-E&T7zDO+MYv1>6o)Nx(O@_};MeC;K~sn~N)MbGG-*w`6~K>hU#$ zbE&e(h-Oo23Zf-rEvY=b!(a{{fAIL5JB|n4I)|}HBfPYGlTPKcxHaIZuo^8Mt6M56 z3)I=NK{XiA0P9on?LuwL5^Xj)Dg}+(Z?>O4w{Erj;(2P+bV?|zYgK%+=#{V_##=Iz{q41b6K5VigTF5z z7|#)W)eOwkiFYUz(Ts$mYv`?=nSLU#5K#jCNACVh=kAwdjs5CQaPrJ>pOcYnDU_^9 z8gRE$Yz++nAu`YngKGUHaQ`3|rGt$_{Gk*wA?i>$VUfJZG?{S}xFp6l`cA(D(!sK3 zYGf#sEQ>bBLkF!WT5&I`jT}u4i(tBQ6!(a~sB&Ia&T4g%^?*AcE|Uu>Av4H-bv_%gj6#Rfx~NlJkIW#)4}!%PVvjJ?=&RAt`e-~{UruGB zu)a#vGU^3JGc07vkwD1jB9=_;6^38j^`*^u`-6i|WI+NT{Kto+ygT z)Iz|M96od|erP!94G5~Z&D_3s+gNK!3j$cy(-r)lFISubkK@$vTYn{G1b1biH~Vs_ zKQ_2lweVJz*=^FpB{v(4*6c42`J;0$+;Qg%b1~nZ|E@Q=>-RnSQ@-uFjt=74(m~;Y zYN+?Wzb)zQ#S!s`S0-wIp8eo2KR|l#k9`^_)CRjKx2Z@Z{DJmqIREPTm$hF*4FBug z>rZ`Jk9a8saVW9VfjV(f4US`Y9E8BxVMKV+!U>jLh_QhZRnTyAWbLBC0*cT~-B#XW zF{1rE&r>;B&U#+$8JL5~sFOjVS?*~|p8%_OwaNGr3%_U8M_34%7y7H`pZnqykAM83 z2R?N3jW=9(_3_L0ZC*Dr)X|2NTx~2FvJr~FeySHR%4`;0{b+~N92!V}XuVt%)E)EO&BHfy3e;*je64ax3M@M=lbIwjNV5efd9 zZUp02@Ucg&Zhy>ip^JSrTZsWpqdeAAP{HDR4@VLbcKeD_>+k=g_QSZ@(r zC2ha>VGw?^5oDA)qp_xTx^b(cx=J7Hh*Y(O(jAqaW1ZWY8UUhkXhiuX{_l)%u z0@fZmfP$%@*2#a@(peyq$2`=I!`ECsO~KdCOizsU_r{|EpT`B<0ul|}JU>tPdfCH( ze}(S!P;>W{d&erveR8{|(rV<^N*sLe!Mpx+eDqLPm(K087-ALo z&rV!B6018RJY`OL{l?1C*;M)9WOFq8rmG}k89EwO>FT4N`jN|e())dK%O=Sd3^jDF ztDV1RTTcKX{nP{9M*IB!>pFH{)4%5Q2DazaCuIzJdpoyTmQO(>SOicw5+sXPkD|(q z2TK_8`Cwcky-oZf1o-6;T4W;tz(5dC8HZtP6ySE7z_(wOu!X9wc7TrdcOPNlAA9#P z71iF~eh?)W|DqVmHQX6_mC;yOP+}+`T0`L-LMTee12Gh}e#`^~qL~8Utq#;AGO1kS zk~uQX+mbnm@&inu%#?H@n+LShu|qqyZQZ( zsL8}V0q#Pt25E{`tH?~;5=xmMd18@rA^67Crh~i=)O4t2P#&{MdhyS%WcufiJz)Od?E&-09&7Km$J+n< zy*LmH)Qmu0jjhs)_+$Wefm!JSkg4FKCBNMw0}tx5G|Yj^gS zqj!E=D?V80ZBdjS8MFnyW$*xhE#y>t-(=tPG;{J8ZTth@M42hC*62-dsje%|yFUZ@ z_8cHLC74I9=~-S}Q7-I{Jvx5dJvz1kKl@+m39g^poHmxn0FE`nZRDuNmI_DF0R`zH z#+3__RVY1RTolkGW9RYbKgUcEa*#`!(~ICQM5-UXxuR+WH4wcZSB?Tu1YX5oTt{Kw)Pv*^4#C4g#9?T>h7*Myoeo`-#odx6C*3MUn+zj3TpW6jHaATD&4} z^M0A{diHK!{q$v4z0V-F9Xw(SqVZVSmKK7N?)cc>U$N_}kKQs~Jw1YR_s8BA)R_1k z^|p}n){sl>vzVNR7xPnRtnss?q)3L_~C zeMt{ZU&8Y!AOZf2wQCTN=%}wPvT^J9b>Pn+{;1zt-uQ>Vtt9bpEg>v&YtDZI{Uz?_ z(qKfJM>xSkTOe+UAK05(wXJT z$StfHSXZ+YC($$b*4%ddo+0!*@na3fxrwtG${@lppjZd`r=qfk*UV87xd%9^$6n*`g7rrh|@PW}51y@Wx zm^-rBjYr%4<}g2cA`jmJ4aZwMr@Hg-E#LM0GjYVwgG0BIJqp`f7sC4N<4V)T)FPGR z7tjR)$bO~Z>W>J6p9oJD&Zea)8!pLM(|}&+r7($!!E48Lk^_S zbQ*`Z1Gmtd437+ElP|r)m0V$4gtp09pTn4a{je`KmpXc~`kk)k;L4k`xw_6ov1-DJ zyaCM42)C0POAo{JlKbd@-hoGSR7*u^sDlET;V6u2$~&NC4q!W=*kE^7#ckcZcFjOv zM_YYOJm|EWP27kogYMCM{u!@ zbo+i@ht>soR!M*^u(YVU#9^N#4nT@D!+rw3j{ciZS5)3nR&*!2hNFMLD%C#AO&FkF zR~hM1VD>Alf0Py(pe1s-sO2Cfio1|H%BhJZ-7^)b@Rro6NC4KGyx*=b8$PjYA3oIC z4iJ=Z6V`jn@y?J_6f~+}M>kvVi*+v-KLk+%w1(Uline99VDm2B6lyFr^eC-=z1tLv z&bI#ZXs|)2V1xmoG^$FFGSTaZ zGC-@6km|rPD;~dE&6xXGGg^@Ut}-9e0}=$8;bqTRoQ;x{P4nvkcgXbjq+6S7Q}Jj? zpvV(7fOiY%JBE6o$N+i}!U#aTfa?B1*Jo!H+;W*QAP;CXk7KNe3jiW$o?JdA&t)8z z#1@5W05Pks7~6aIx@Y(LdkqGYT4U0SeuE^6&g-RHPJfmUJ+%3u*U$X>W!oGfGdi97 zG{NScyARLZvoIV*p@b;ZoouPtu_dx~RxRi(-pYpF;`(h}qrt;zK@S-3YwJJyp1mD= zi#Au*>EW;joEvU!1nX9r;D0*%x1T$cee>JbCCWMuO*n#lsbhUzN6+A~k3F`*cwLvEKE4>pR=)YO0daaA}F#%=PeH zNW^;lz&-3SmpGe1>}$HgB95 z@9u1FtcV3XXsj)8v-~XMGhEFcm329p+W>x-(KSU5l_4!;RG^0LD!BVuQw?w&QaMK6 zjE7lPrV%Hq-dnCDt@2xnlCO{I{hokMy{?{*nksy$M1z0mi_v+P*`QDKr4oQxcr}^W zmLrncwfTdSGavZUKMho`KU^{=tM+T3R`*)+E=+EBVTuN><>x(qgOK2h5xp50J=pz- z$1K_O7RlsyCOnPCJDbn=1(f<7_`vAG2iI3*Ke_Kc{uEF_75o9x@ekszP;ZwMj36@@ z-LB7ZSD};;WdJ}4sK9&#s|h=XBJzB@DT1kyF9dIgiONb)Fsw8|={2=k!-CQFM%9t)Cc2@uoGckvhjMbGp+2FSbC z1q_T^D>{GP6Yl7CgI&~RwjA8b|7~5V-di&r4mA`zK~7d)x4pId@CUvZw<0t*&=ejw z>4c+2)-~;qjtuP(q$e8No9*dcNlkR!2LpPGQD0Z>4wkw@quGH6WO+twc^rLm%DEQqnzMeWYVsnBaQ6KiMV4s67)vALA z^f!J;AR_!FphlQ<2_WIL;E+`O3;Ujb>?e(TKYM7ypX{pVKbAal%XnMuptnjTXpEiF z%zxhgA)YVY-02Gm*I#`q9d5r?`0$gz`t;_9ZW(XB?!^!C-+Q59C~Ah`WsHd6h}`ck zFR`EJ-f(?ly!7^wZ~yqZz6W=I{x}v%MDOu8#kklFj;DU~%f3eL^I8Tv23R$;V8m`} z1R#rpg4XNNp$^uKT$x2ZNdz7hKDp1?Btd2jCh)z|*OH2|$}$6X8(>_+18XyD`+8E9 z;9rV?Ji6cBkIc1IU>r;^b4WI-X!yxve}7dnDnwlHG2x-7UhsY#^NB__9| z8@1T1H5HjJOWmhp%WC=WsqDe_L|H?vzcl>SdVWDSdtlvmlS-rUCI%||wl=gm<@|Y(Ef@+3Xj#eGlrh&Z&0*e$36S3E^gfqe*A+Bf?eV&9&{-q{bcvSNT3vQY!qRATHoud9 z?UX}snk1`6QcG(4t?soGks+a8?qQ|MzABy(DR;P;{KGi0N5Kb!foR}(1EF9!&5%jb zt5rtS%P^);bRwD@=egOL@y(N)M~8dUZLs7bWuYJd5EiqM8{@}NtqyQ_bCVLCSo~eF z7<9n^ZpsI4Rm^(Bg5(kB@`~k-l}E;v`9DR+f9>|Jsb5AX(ZuUd$i3`tP(8cf@6OEv)R{= z0^CLRS|@rjyaHVj;;OkLveb%$hzo7K81{!pNemW}I@eMa4C7;4wA7-xk@yUQ&;yF9 zXV@H^vfMcIDGisQTX?LjI$Ry_Iql#^(QqL?MBF{Bs#OyX5#)r{l_lD84OzN)5B;1v z%hC$-FJ5zJvZgQl`GEKFTWbzPEbswTy4vw$eee6m{^zGAZ;n5*t;xAZoZ2(9$>s6a z-1UQG%2I4B?rMwu*Vm4G?RffVbx;zU+6EA-e)Ig#)pO`U@fiP~@^9VY#4i9MmrZWF z7CMjz{!N;5pdqVj(gjGi8R0;(n|hd^#@V5Y793E z^coz$>yn;GPY310RxkflD$mkp#WvMuq5q*&!C$b#Fua{?j1aDlnVd{&YVa&3 zQ|a-z1H!^MDU?KCraO^J4Z7#z;jXfXx^1Dl2|B6Ve5RzexT2|gp-od())|h^yZIlzFzI0`iV0`e^b2OQd17?E zbf~K&&|h!&NPFiEpp6!recuZBLS_|TYH(PpYl1(sSDH0OZT6Qsivbc|ERgUZWHFfC z7T&2fYRr}Pp9O2GEe=DeXmTV2-|{tsH&`&t@0C3E`u;#k*CA>BD1KS!!}{~D3y+8+ z+z|H#Ip3Fp0z@p3h_cYFl^1p)PX`KT6cgG~pfbPlT#HQfRzMZDmtIB~DLD#3qQudaAs_qLX>v8$%Jx4v_F zbo+eY4x`&sZyau`9Bn;iY5+5`BVdi%Y7Fj*?FTA*lZ?N({`{NxEnB(6#MMrc#buxd zmgWAD2Q8W zr1B9MxAF?)LbOEbfhrMMugaDP)sv7dQVn`!s0X?=7uYEmJVzwO-AwkuFZEuBGnJ?q z%S^*E@fQcoA(tC}-awX$)n;+IO3aAFW%p@JhOp$yeUhRcT}XsF@Pp~hIHo!feYJ`F zZSXq;;VGxjrq$$(7oD-pcka7RzeA_|NN>@hZHztOGMC+=xqUKc8?bEnR|%|4*Htyf$DW&!SCRnl`ZXv6`GlFFH|N6HhGKL#JCT% z1pn3}lFRxp$~#sLjV-4s7V@)J9-ppRVJN^)V45?)cr$ieOvIOfFOl>j`wATO7|)$N zas1fPD*+havUy{=tFxoMH4%@Yxdq`%;c^!7MK%_IFOagbK?#Wq6TtENI03#pJpRFz9)qwKM&0xAaA#SWrhRvHQ$?{g zWl|fodbM8EiJ${hY>-C3aol2#r^;c21jz>39CWQG8zikWYQX*BRTs;CAhST2!z?_k zxVy}ja8Lb(RkkG#cLxLg?a(NQ{~CaH%i9#*Z?W@hnoD6*RD5hGH!>pg zdNAjZ=hPs1msS?klm+QW%HnONvXWARH=<`=roXR9AMqNRVSPCV+h}0*2B)9o9cIO9TKNg3f_Gn?( z&aG=FC&pUH1FVmf*=$Ly6z{Y%V_8xAES!VTjTnQu4L=*<2p<1QaeHPt(28M<95Vw8 zA6sDA$^xS(U&1##x>Ss+SYRwj%2FW=xheB=ydtbLhB|+NmAM;iTx&!efc1#evEFENZj=KN1Me&$`=X>U%WB< zzF}v;p(%)UK>4rd_eTR7-MN2FgrdSkaj`eM5G+`C4WeNPX|SlsUmtIEh;Fl0CwR|& z(WP?nuNh+gfJLpg7a29C5VMir6n~2Izn{BI-iu<+1kj5~GY6wUvDv_~y=b#V>2no6 zH!$0cTICwMfr?|cn;vDuitXm#x3Fn`!`%9H{k=Wu?#?=jo|FcQL$;Wi?6({|iGNX`AIm<1Tm|@-xv)k0!Gm6k1 zDA7cpfQ;NIBpeFE5u|n4tQ1?D6XqyY6QZK*RV;e)sbwLC+gQTG*JeBo%OxN@qQaP@ z@I>~RGE<4ejiWJwqfrCl=5TqWP@};c)%!XO+ZW-A&DWpzZ8E>G9F@T+dv{ zR%wqVLIDGMCz0>Qwi-??NebFv06Zw+!}@Wt@CqySSS}wBKmnyb1^E%ohqPz&kh6Qt zRvpwzmJRnJbg9xyLi{_kyC0ai`iIw5-&CwpTSBgCRjMj+Rc0=cOf_5V{3&10NN0IP z$8y}=8M z^KpWZ$WeQk7BZ2l;*wEti>@K<26@&R;nO9q=H8*f{WX&x+}br(A?ftiEf22U{owk8 z-#GcnNvT7))725#e0pN%=MGi3NOmcMZ0`o%O->GF>uQwctUJO`;U zWRF<-xUaXZrJ*(+MZlrd?XZ~9-8#b$u=H`x8zq#HhK2UXs=7c<$#DmKBv*jQD6xhn zEK|L3*Qm*3)mqWA%Ib3pVx{llAPX#z9-4ueA@x*rswm{ul$y*iSv^*dX;iG291f#7 zZI85BO2XDyrsv#`oW=R50VIpw4rat)Pi?z7>b7Z0jzvsfheUD)HZJ7M9k92?xZ7o_ z*8pQWC(fJ>jzVv~)$bn|dSRHY$c~XoWF4jdoNSsnzuR(^0g4Lk~UGv)H?9<-UyNIpbkG}2t6yu*T@1~bm=zY3k3qp@}2 z#m z)pyfyPEI#7gdBbt;@xRDMv+06CAM~uc&BZ6ca zW<+7Fo2|AJ-@Sp?KcB3(nj<5X8F09Qo>8kk@wYc#{dd>R-rC=N+x+{k|7bLGs6V^O zT4X8+ZCS1M0Ds%wzc~7ryzS+VZb|gIM7_rrpe{?Ao*SnB3LjnfgX`0K{(OGt<0oFc zW9ZD`0XpB#^KYQ{XeWATSEdsbUnB|%Zc>kxX6$Oa!Ad~QsE0E3wKdgMNuSqa2f8zg zo`;PMDL!VQ=%5lCL}pS!bpmOC<3+LiT%?sn^x-T7c{e^Jp0vVGv>H4I+=_hRllZSh zDk8>1dhHo0kV;8Y_T;oVQB%@h^O#Bh316%vm5L5E7PWX%MI}Z4H3s!=t3Q>}joXtW z#zbmI&7bI`uhFPP`DA0!ZcoZ;bxB@_)%Cnp`i8|DC4W{W*}W!xUE51m8Kp93KXZGEXrP9YrNw>E8_n6Llwoi^Y5lshud0e*)9v7HK1}#bP zTvzP(d2AL?qU_+I2B@ci>|8C+nbNl8Tv_scv3H9}8MLcoxMqs>E{_beh_3jYw%Slk znNzEhIW4O@_n@PyGG_!z9+!iqQ${l*im_M5kAxI+aSZYW9)Zg%wM=QOdX-pN+$ASZ zyUY^_jeXG$Y-9%CVg`gcV(0#KVnT3VXbPTt%4nANzzn^&fIV2lZIo3Rx&?E@-8z@^ ziM2S;f*hkwm*rwru(WwbP7f8vTw;tOb`G?mOxva>skcUFd&pK%5wgV%2n?Zt=kmBY zTqX$G<|gvBmVdUQW}|^DjBuskEKQ|~J4eQfQ>h}qd!YB)>f(zyUg%|i@1|S%H=ijl z)?0B6=}>CTA>Tb+Yoboi_b@~3+d8`cCf-c< zACxgjOHV0(q8_P9&{Qjo2Z-IF{N-&%15ZMv^;nilYoLmb45mNNK*wbgtCRKsho3%FTCz9mD zlD)o5Tnis|oO^2Y@iJ^JaJh(P^9Xb1nXyy=d#V8TWKj*BPCd!|SL*dHpN1qM+o|Mi zm3d31q*E6e4af@BA>dnZ^QmP#v+BpujIW`t7Ok%1QLo)#e8cq;o#SyIZ$zl1sav?=lT-h5e}#&`zz!terC% z4J?Ak5#f0*!m7~}{E_ZiU4FQM!g%;pCr4S0@Q!4U3k%Dp$sfSZOc!5%K)z$cgGkf% zrn}l&D$0?`3x{k8i-q7}OVh3eH%&I5@+ep(h3fWR2TUjJZRXD%#67%!+fyBuEOI|zvqgavZ z#%;5^w_WOrkiH za0+6eI2Da;tpW~*clp>keHAo(Q#hv^xwB{Id7vhhs7w+FN)>NFi(opSOuPf>u_&#( z3qsY(jwJ9v^bA21Tba80gmmrlu?UDVVlz1wstEYPFTvv5H_W0*)6n1m0*T>xG#WO7 z`Ilj2$Y-Txjxt(BM6(!XCf*?5ie++2fsS0@OhDp8J2W?`cJ$mlv*BdN$%&1(pS!`f z@Yueqp50Zy=K7YfDKid7NgGZjbeAC0XjDP;-!{s1 zl`ONvz?`19gTHk#_Z#Y$w5)7Ou>_}T36zI)ims9WetmykU)S997Y({DLaz0@f*T3B z78(lzxmIiT*Q3w&GoQQBe|;?62X`(SM`5I?M2&R7971noFN&I20 zP3f_#(s~9v`(hq;X0Hrf?`trl9cbv0z1OHnT^nM_b1#}*Mk7*hvbZO8!K2ZDS}&Qb zdZwcphE`pR9%S{<(x>G(5f~Z~Kto;!oFNczkTWFVM72(2^-lu)Ic7B}G^iE?=wJX{ zFbAVh1O*3?BpaKJ&0u#0GhqKTHaP686VQ;@b_@cw(bz7!ha~ky8*^44^!JWc%!%tOTW9f`o?A*mU>DcAX(;%F)$kqDU!4Vi1?=7(%iX5ljolE^$=VSd3J z)R5KJ*{rQibzSvcl@+Xl#ci#%)lyqull6iQeeVdfENmD{r^nLS7sq;f#?pNJl2%dm zWlYKao&CFuFCvfLx*745`eBIQ|1Dp8sU9hsek6Z{E`M721Hw368|31!7)Ruc(6mVO z0w6z!^#DDR(pJ>mO(=S*41f%wD18h{RkN^s%1WbI&~wGnxGOyRkLI6@@`RNhieUn^ zDJAoW@N86`K&p4m)KDfI4MfUAwqyjVm!N{IrpRT>JJPurpU`Myph-lT(;*pK6?{Bno2Y~a@~-t_w8_iU_-7?MBX>#+)Q-3OB^Gu(~x zu2w@pK%fAwgf{h(03D0qoQN#_4%W0{jEQz}YLU{SZlwt-C@5tDqzOWQq!2Jmj{HH@ zPLB-_0=$Qi2&zGXU{zAm4_YePKX>vs zs235xU!|2K=WA0Ix7XwN{#!fWdHu*@ey*RdI~(U=cEb8`^1!no)k z=?XN~1f43u8_cXtU3GK4r*_P4ZrVJVuA6M_y{f-@X0W2zUU5uUVyYa)FM?V<{=2H5 z!AP%Fda0c?BmK&E`SEXnv`9Y?jKGWEpJCXEFN+M7yhxD+k(*W(<$b`)FZmQ4I!iua z@+Bu&P-kW`ma<5lvDQ=@0lFQSMzR9s_3_lXsDWpJ#h%Y&v7`*d_#GX|%8ri8WXB6; zyON_S-$)28B&fDo%^g?usBU5d+S^}|o}bG`S4c%Ycgi7pohoyCCEk@2&%cEFI;&!T zG;#;oFL>6D#2u}Qod+WjtqN&2A8DkV2|^Glvy9lFh*!Gg!VjTUjorCuRoZT|BJ~_9 zEwMJ*8p*goncedCztB<_TLS);B__p{t1iS6E8@Q|Wi^EJ&zyUbZ)WGCg4=#JQIM&3 zEuIJ*ecTd3a5I0Lj*E*zcYBtOAi#)2{yB~W(ob|{%+yx#sq$DnQl>((xM1^v5|Vvn z#OkOd8-0W+FvPg>U7EeQegBKD*h8JB61#e)p?wS6aJAW--}7^?WN*uU>Ct_U8ugM} zbRN8pe~5o9zunS3*l&T;oPSmI42#cfbcotF@YkZJ$P1q2svzC?Hgia;$`e^`tzaG!xacg1JXv2)B-$1VC1Q~%C2*GnaNa>T#(#Wrl|jCO6>(OEGQj?Bdq4E_Wd z=xv_K%5Y;-i7jrf^mj4*s0aAbPE;u>_|Yx=-KO%tDR196vuoqu+)W`&chy{5_ty4^ zi3KrPMvQ?%Z+*4Ax}hwdN%Vh;4_^Q%g+P|;7=F(7;95e*(B%ST2K*CXuE!2>f@Y3D zV+Qo85Ro8LfQs^(=Kn}1tabS;)a0=_GGwva!W#@KBV1$g*kPpdfQ~BZjd+P+F~zx3WI_tY0dxFrc~HMNj0@jQtRu4a@cD|lg6 zt3@721eZ;}P%vSYv2>8imX9?ekZu$K7+QQPT$*JM#2{qA*Hh*-mvlu3H?n?kVP~et zM>7NDd{pB9bYYRzgx10pRCb#vCSV!KNbPs%kudc~E(ueG;{FZrKGrg9;?&7Za+g2k zxg?Lo;H>avU$%Yr^qyPpNc(cpX-^;j;OIe*+uzoe-F9)8MALWAf?4Y>`p*{UO*hNj zBCM7Keu$MjBj{x7X{f-hpzO77lZltm4p_46LRywHN@^qPRzTPzb2p22TP8W`X2ky6 zfYMpPV6Yg-!K8Q=%&oj%BR>xf4~%NPaaaDiu*2TbW2wUA!q+T(tl21`o%YI)HBVYC zg4qlyo_kFyBFMb$Ys$~dH%yRUm;JsMK8?=l;L@iFI#A>k{MMKLxTVX7;)!KXV*3K2 zs#n`#JNG4ubQ2TC*Qv`dt%cwa~r7yKqw0$nS^Y>A!0#DOmb^dHZb~Ll6f5(+8 zIo+sXe*Y=O8chdDzRJ-=~A4Bjk>odMM!U&r~gZLsDpC$|z1dIiKTTYs$ zi;i4z=<dz zlW#i<{+wix){+Ne$hV4XUaa7}$Yi5r7*)Q~AOQf{Suj(JGEY>vf@KX94=C|A>{$NF zoMGvFk{zWyM@1sjlYJ->>1c&!43!oy)_{U!1F3i=dZ0)W*$sr{%fswPF6P}sNcnFo zg`RgWY`JD;ueYbWv9272x#3W#u8c~r z8R-&wP|NmA{zhLqs5n^s3v>HE|h4$pkHG-q0`(1fzR6V#8DG z;jMz*9mzBaU^CdJTCG8w=DC_`q8$o{iv2DJH7Wwyq=|2mDacq5gX$7+pcw}d&N4F* z8Ty1s{b1j*e*un*YPF-W@J+i)8e_pCz54xXOLd~%UscnVEbqMIU?LqXHA!bAM?BaR zOtvJ{3mxSOWYh3mB{8u>6q2VI7y}JUt+)m05@)Tg3BY0oa#f4m z9_~WR3N!U;?T(91MIy~tG&4Pv>F%seB@mkk1&bzp6OmZNZt^GPbE5R8Gj1T2N0TTNY_^KDnRV=-$Kk|x#uDhg97eJoK}k} z9ZSUGDW^?aLTl|j-zWrxwYhiJt>f=vYfbOmL0!9)ccuG)-CyyQt;V_8HB)0Fnf~%p zV~;iMwBwc8`!@2A3ZKfq@7K6D*|X_=!8l%(e_wj?MPApA*Ok|JgAiYQU)4Ww|CN8= zzH~bGzB(M+{0k#Lx^#^l7Lnh$L0;!%*wXLllTc$Ink5f?2iAFO!F$K}ziX!3L2uoi z?xDG4wb*aa2^N(_ho{p%a@E;ih_?ynXHIVJ z_jd)HLVQyU!(NilIqLnG&!eMU4#M(!7z2_4z0XX$I2d^*;|>nD#wct8%Y|8G)}T?j z+N@IVfTGq-T2M2Fvjt~)k~5nvbOzWdb5l%Y?j1fEsp(65G*YE)t-E*5&rMI#DTZP{ z&~_jcDT^e1rBf%)IQ4Hpe9?qz3+ilWf_01E8a$>1Pq7lXXJmzE=_IVz zJAWALm-;%peP()UVtBBpyS=q;wtm)LYp3EN5FDcap>l6=$NsNywoonztxPV!9A9K> z!;4X<96BT~6j(TlBm>398RH^J=;g!Ia@b0dAQ-kG?UeK+ru@Dlx67e#mTDCtg0p8k z|7+o-EJJKehVZ{+GGq`>zVU2}lHnr1m`)-4F7U~dD9JA~s+T~!Gl_B(#tX^{{zSmy zVO*>-0k5AQ4{V6hFg-akRGX?OFD>!n4GsOqey7bqGK0yZuM4xebD>)&0FQ<9Kx>7p z47tQ%`W_M)Z@W}0j9O`>Oh#gO11`A8>o&HT8Wh=ub!y~)BFiqePG93g>^*3mu0Pva zuudc?>2)r)OqHmM%rDbQ|*}2+UuwIav zTWHs4wQfDzS+H6$BuUwF87}oytvX@pdX?C$Dq(i*$AK85cN&+EgkC1A{?f z%#x@WSqn3Uy@Qeuv=X8?&1np31NcrZ{tQTx6&|r69V`p+{GQ$0whRuWyP6ungOLc$ zm(BB~d}%0LY4+uG$@z%zqk?_?0smBfU%@X;G9wAV76QD;KH;A{Sh#Y?AZB}EKXZ_A zVmf}i(a^IwggMkHMx&}J;1_sOcU6oV(?1;O^SB*0NzeNPA5I>-ueR)41@|M)e}{WA zFHi0~I|M30LKe`PS0Y&fsu+$Mz>8RFNn*eybGM%AT{Qr$+LM#Rs-N)N1Ql$QZ}QDd zca`H7n`Iki2>*$u*7yfdCq|RnWCSV#O=m*I$mo%~{h8+ulW0;F$y}QRDw7cv{0Q3GvZ$`lixAX4z-hTSjb-OpOUpF?~-@}Z% zaIw!Ba);vO7DX<*VWYoKyq(sKd>7R}^F_Sh8dLqtTrF-GUWi+lUQ_T|s6bNTjzAi6 zs9eHA=>ZvOtU0D$>Epl6n?ZwfU5ns-9XI8jJbx(fJ zUge+q|Ji#JD7%j8T=<-G=f3y$pw`@)db&NghE}UJ>$X~ROX}9#Ey=djmL=QR@@#B8 zVS{bJAErH4-W1aD6U=wsP={h^JQpMx6Fve)6h$6|;ubZEN6nWq44&MM{kHqx zbocEyUwn9Y@7B#5)~;CA(!3bJ=te+Q$(zTT4f%Av`{pj#&s@V*kzWTO$xU#o7BM=^ zlu}e!6RNifI5mQ4+m@Bc_ipKfOsQDn%IOu(6+YU1%iitM?n{bxyF#=>8j({$m|5)R znGzx5RLZ4PMw%1{!z6qeqk)o%NJ_$Zk%RGKequf;DYAa{cw>N;&u%ibJX!?!tvd0r zC!uhGjgv3;k~#Kle+RRb7enxVl)Kr{A1({?R@EPuaWF zyV8EUp|g$5iY#R8N!YLWu$)nwt(|e)s=^zx3VyCim`aX9v#3s10~Y|;LrF1=I5jXj zBSJQxtH?8UKUbB_9kFU_Ylf_v8i??Cyc$S0)wo7eDZ%BQE!kZZG-H#0h(?^3`6Y(I zUF!c5p2<;bdfA^Uo+|s5TxEPrlkVkyhQ{&w$3FA$Lx1|w2i|nYZP#COC7f#q_wU}h zZHv64n`X7u&cvyrEWGeD`_0bn`o1aH=dM<-=r4#y$MgG_>J`0PPaMD;=?a)JGXOn3 z3hP-sk%aC5$3l*KoVS4dmmYz+KzrG%px9-HaZ`)=Kuk#0ai~apN`?a{DryD1wDFKi zHhcETOOA~k+`nfK)l)XFTRppXb}y8?rly9*YM6e^{kGo!j<&xbx=0(Q|A3Mo%L%d? zj~&NvaHHZmogjWIFy-Oc%&@+_&eh3of_XfX$pMxbsc+H;n>OW6umxr^nX58az3#H3 zM~3!n9oVq8cSSdxp0zdlo~RnXK#&>kOO<+O-z4vB7&ENDP>8hM_%7o!z3*~z>Rp7; z(WIOtsC_2$+o*@(UI{dY*H*1fb$9y|fzTQH2%7_E)Zbj5G$wZ(Ys+PKY+HwD&fcDm z)~5RT$e7IT%k6`pQ+h`C_>W>coG)^1V!wLYnIrqqjB#}BG8z=pM)PleTJVp=h-?CmMG*lg}KI%S3F$vi4{^i0y+fS*x z28wdQ{&W{*Fw8&J!|Fw_4w({mydp+b3o12W!kImh&!r*Z_>95TVnMWJvkvTN3}1vD zP2N1S$us>{A{&n<0McW<#4(Vk%tc)NI=*j4k3gwIY$z=v3+v|2sya}8KrN_RzaM6T zpT6$rednmqoL9~b=LT5r4DSbq*)`xd2(O?i$;vsY+(;%380Z8!J`Q`CgTz3fx5zJH zR!iV%7i#adgDdFw-wOBsjTeoM9Nx7ZEU~hte_?G+eu1lRW)+XqkAPR#b;9F=81XX2 ztumPmCnLc-u-j4BhdCY=a3XccWW>@G(Fk}H#GF7&X@~&-!v}ZoLO5JoeSH@UMxI{L z>-;5beBejU12&^Rx$j7uv#9Fm(Wc(eglmQkCWbf|5BEbscgyY9T>^(O#O zEVuxDHT{cb&LCNooa%LEsQ0zd zihc`x*ZCVmllWKcM_hin_1vJg$dF%}@RWowPst`h1ISd4WC7C(v5%sVIKA4<(;~mL zAtf@WN_%)M@wR;byWae!dv3q%-1`bHM7U}UbTurCBO2y!tHo8Ws=Gl^(M;0I(1g7szo2+%rybpJ3Q z9M+I~Iys-b%RjISeZl3QLj0?jQ&w=Y&!0P~tszvJRzqMUQ3mcfj6%qtgHZ@i@N6|Q ztSbQV4`G3vi{UB+ub_qpbRiufuIxgj^cvk?tV+`xptwdp0!Lf9G0J`%xr!UYVM2>xr+L0B;0ue-)4SBMzJ^+=2*^wC{AyeUuIIL%XRWI`+`6F;}877``>x% zO*dYD`ci<~3?pL~I0J>|w%RIq1+_e=_tCb+{;Ks=S03D|sOy`cnplX+E`7=M-QL6^ z8wcph>#}z2#K_?-aOacv+o4i5dAvWq!X9#IScrdXIeDK!?^mhoq!ITdF#pUG46hby zRsdp)$5okZi7c9+m9qR3ilI1nL0!TIh)Pf0=z@dZ!zrWgxbeToyD8z^tK~w$e)d_; z1#d)FiKA5LFpnnKglWtJZZcj|^pVl1$NV&(x`a$g{luD}bwKk$=)MDa1=O4s%NiRN zE%e@3S!F*SzhHkf_V=z#{SthSAA@b@D{b`hl4fIyX7!Zqr@1#kM?iFXsB6$o+xVoWa>5w z+gYgewM6-Mejckwaw_Yx2Q?OEz=+Tl?T5;g@m=@Doy&;c-Qv3b+CM`|)3 zcJSdm7^7u^F48)U-XMv>-7kT+P7`Xnm)wa)pxYk6o<14+4WO3p+8;Jy^%u9HPo+Ggog!%{)kgOf2sQ@QpLoG_iS$p+=xm+UBluA+8cFQKHlVt{+!bp7l0 z?;G9)$mC_6?I6_58NvXv|Gmz9bwA7gKdEgx-2acM{cm=+TRvZkf@^M19a#je(&*^Uob1aU|a3Q_$Rz$lG3 z8e%{o^0;h<5Sc%Z7BjmB?r&Q)b4F6X$*SPbPq^o&U_W^S@5&)`?N_33rZ);IHZsJ4 zViGxy?%;TZP`>dR#h*qW!;@<%J^(@Hu1RbnxYr8trxzolC$KWaY*I?M0;d2KiQ|Zr z#mMsN!sYD57|wtt!j8^cBuA)+cA(y(ia88KNHa{tl@Z0?!)pvF8?#6Dty$jQx_H6t zSvA#)hoD(OU-&1kJ@!qbFW8?Rf5d(h^aYOw&|7#fE|fXN`RLMfqO;e<>nheq{+-kH z5*RTL8jk2@2+aer9iz@kZ{KnLvE$eU?)ycGI!nyHGu2i$wPfQLdK>RH^6&hNn7wCP zcVY3unKg_WNiN>jL<|A7$8D`5*uzCU;)P--M@B4N86Hx zo_~kRHSZ7jcl`YSOwz3VkXm~kk=*5lM6a{LO*YP)I^-7k{|c9+hD|mtpz1N6vuAm0 z0Un zoHyP5#yj6|%MDi{Gh*Murq-^S{>7~|Rqi_)Yrfwf*XI1c!sEkO^S704l&CKN_>P^b zu@PdDa_G88%o?)XQ1KcWMNFKn#B-ld>a?=Hs4eP6$12;Z3Ux%v6m>7@4cS!E8Bv_R zeui?DsRyXWadM`rLZoelHE}GKgc-h%?D<_e69Y6mOSZNp+fOk z3G6Dhn=l_lZ)!ew=k3>Di`scWJ{{Uao6pAeJ&nYdjSbR^TIV7zOk5C&L}m!Zn(&Y?uSD80=j{D41_t_DDZW zJI6+c_G|!vb6+oPB8w4iInNF1#pLA7G&$OaBzni!?dRNm`hl8zkCjiQQ+b%hP(M5! zA2Y#JTIB}Ly*RwI+6z~2RgN|?inDc7>53ZkQjG*oR;hGJUloTaZpgw7f%K>eKM(Z2 zlsf|dM|@rA56OlAuG`;m^NlA_EA#l#o!d8WLM=FCZF8ngEf6v$`46kL} zhZPN)sDwcAgle6vK%4>nusHI{Bxn?QZ5V`+SGQ#=bdYGxsP>FkjclsmG&GWi86FF& zIeqHr$c}B|uk2>XsmQ$sY&9b%aYNuCWZ~rx z>p!3WFs4Y5y1VAo?in2DU$wGpefN5#<}I2(x3+UmXNyGj z2@f%*aAWM#{Jz9(p0~eXZ^8F}Xno0IvS=v^J~66YvCS`i0ovxitL>a=X41qA4TN5dL7NK*U5m9Z#hF-d()V z7#~C*tXc^c<*lT%V^+=J&i+j+S9GlHTnl8$1#@TDw9jg%bG4GrFU*1QuwRe;v7xK| znDvO=6Qb*%Yq|nl3JE^(3jYieNP9{Eo)ISPcoK0u@;1)rD-e_+oSDlBXA&g>K^a4>wi#_)ShdRaBW?de#YYRje&2pX$49@_ z*66zzN=WP_RDv5uRws5C4Hq4&68Bwww zZ4JvCmzz-J3Zt`=_J8e*ll_6cNyW+DWjJ~ZXg0z3`B z&|~?S=C$)G_SikYD)!i;2fwB`AC9p}R@VS+wSQAzFX9}RH7qkB!1$WtDQB5)UFYpb zBGxs*mnL6VgIO1K$9w$#D*JC`e|sPgZcYYAcqTc< z-9T81p@VHv>lqJx_)&D9lICy#^1;mt$-o3dj#8_e3u?tK{KVO=>jQG0&NbSob^>%V{EJdrqw0`Bj>3IsVTxh`V39c#LuYsspZ=J#$`WP3MK$F;JppwEE+i>`%J%Jgcj)x)i}@lYGyXqh@;v z4NZ>-T2>A2WD*HrT>BRwA5PnaK}Y+?>TMu&eAGTJ_a^D6FlNs!&~(JV9v!>zcbsKR zZfYc?{;t|jgL?ai74^Dd%A|n_ygzgNN6vo@(C=eHKiGy?;fp%yN@Nf)!B_&jXjOqR z7X@Mv-&Ropu8hTW=7kCp{EL#!7WvaU)^XCkw9T+3jU5d>RCS=pb{<`L- zc`Z$Nc)4@EV4w8AyDa7JIfOy~Sj`odO~eWE^1*lFoNF=}N4}$^Y{!6S?(o>C*H1rgMmQ z7;QbUee4zqE?qBrAxW$aSD z_CEK01snfO)B;KIkEoe8`YEoQMdb+I;-?~;mZf*c#4St6ozHD9_5oC;tD|8_b!BcK zKQMEKlI@Dt#*c#B73-&rVGq#!A+-_|^+qgnGJ`B55IaxiiCc&kgqEPa7f=q!-uWS> z8|jE&N;Uxk*z9Hp=wT2c>;U4S{BWU>GKgqth}rOVo}nRW=MFrrRwDEV3E!UQLy`jI z++ttc+y}&?&S6xlw>#Qq*lqHY)DjLtN?XupB@~r1jmck?n}D39!rJu7Sb=D61>GtO z5AaSyBg#OJtJ_AU0*qvTmrJ`|9?2ONQeT>?Ug- zb$;NT8@!(%anH?zY7aeGr8!=|XD2y0YbQH6?l2K22hU;H$Bu8^GJpbE%&%>3f*o6X z%w+Q7GrPo`Bjqc4#GIo~sF}Nxi|G0|>h!vL2MIYVQIgAT8$L;TiVQN6upyLulZXi4ou!}!nar`4Syy4f@Ku-qBA)-CE}Zm2e7vE+NG zi;9uj8lFO@b>N<;$gr4WcW2!^WJ+(V*e03f*y~x)HCHHEKa2Xvqt?e|uMguJdysY? z!`mXp=Qqx0atsL{C7+^@0IPbR%4K)$+_ZrSihSN=hjYU|ncM)M<~@$YKygj!L)LfY zdtB2%u^6!?O5?~S<#Q}tvwZ2I1#@QQcIJ0V3OM<97QQi8 zWIj}~Amp0wDsEi2hC1}(g}HtCeIoVbtZY8m&GrWP!k<5$({#JIcnsfWsak#?C0fU@ zhKgi@m<~YENsj?v)hvY$6FE~Fz~5WNlEoKabYS1!-CH)VUbSM`;!{gbwKO(0=IZ!1 zARWIQ6ATv3>nAK?Ydm3n+~xUW0KWi6cd7zeLZF#^2pHFhl0Op9p&neSLP{w>+!8?o zSBTDb#^b%EP?0%`EEN>CScUU;Y0ISxVQEI$vgV3r{HyXKNQa%V5BunFoxKfoc;OV^ zUF4q=#fz|dcmW-#TgXF*0f8ck84-0GB@wdN%uGcpQz3h*VIdO8KTjlZLUd|aRl3;R zbm`d0;RE}2Zs(zFx}y1t<~dDGjV2DAXQbrZht3MmW*IipmuMs-%$mtEH*5L|P4Kht zo3(G|OvMGl%RDzFvLJAN4i{nFEw}Na2Kqi;vj1~tA3C^e$JT*$Yf!nVW%jW-$7aq# za*FJKBi?H_DOuM@9(>0BH`#Y&1Xy2GRfP)XpisEU>z0mUMlrJzFucsL{xdO1d6=6_ zYYSx%9K^!HNg36arb6)#$n;HHjELp{$2ej}MXaJZr)-qMfEHj?Q-f$~`CBpxtO~_4 zcduw~Ue(^b5(L(IbrtxlQ^{JDTV%aMWFHaPf#1X^lcjLwLyLx|7m@q|^{79Z#N=ho zB5EDPQR=csDBB&~0~=+>(h20u14hEM7nPaO^hkT^Kuxt-+5TL=h-aL^M(E3zJC~FH zxd(`Xc~<7fJ@~w?TumZCP)=zJ0I#k|uX~l>fqJ*8M|s`J-GG9$i6DHiq0k>r{vk#G zUbsYE`uExc_Ia*P#o}3yy7a$A(H}+G6LAz+_K1(gmZd$)tNUBK1XnZ)(LJ97Sl-%I zJxe=V3-e~@Hs?1R-MJC*HCK3ahrXrqU`Y3`YTnm|_o)%S$)%?0R-W<=Zd;G?rk6{4 zguF!ae5fU9zaVBG9Ua-feaq$|AU%MiG#}M(mCu5p3g9!u#@U_jJ^*v%r@WtirvQ*E zKDug+s$?6eQdQA7(;jH<^1bON`=A?RdQ`jy5G}s8uLr@J3+GpFtJ&sLyyP4-IXC+D z=P1osks0(6v7Su7bWb5XQxHemx>-H_!2S-HKU|(3{8=QNU-;eo8 z@#k#igI$e%JFc8Us3r_WhH5{g?|-(6F3cO#P)|yxo3Q>6{h#w*8c);twzb6AxW)hC4v}FSp)la7$E`|+ zbtma>sL~6l3#uG}DUHTrC6kWE2*N3hh6qTV0v}!nxHqs2a2D@C!XUL3>YKYkC07Pp zz%abWw@2TCMoVtu9^Iq%s2wW0g8&7nnh-B|)9?l}=1ssiO41Uyju4r6c*ES4fzd47 zNp_NcCq`&`ySE?#%on2LZZ6`{-^DOj0|a0&Un-E#;@`_>t@~w;+{=^7H_&ZzR4_xI z7?@;Z+b8W6-sS0khg&|q?{NR8`yCWWh{i)8Ie^on2@%a13u4akqlXUg^~>V!Z7p8H zj%)#Cjs6lEiQxT*1{KTn6N^eK{Oyg zXL!C^h0{<0Iq2t$XSDu6zGzi^QK01;QKyI!rj1>of5e-KBaR#s<4(mHCdfiVMC;^m z5Gcsa2%6%!-2bVK%YC2fd;u1h(1q@T3*@~wj135`Pf38wM4ljYmWXDo91d1Uk{pYemD^K*|;3sJ=+l{k(8>xI)EM=1$9j#-U5F3v?=W>WHa z)|dL5r5hq7W2vl@0?u(Z>PnagFFUw@8zQk*Ec3gb@mLgj z|5N0`ir<;?K7Jm^r^@gAF7Z2qC!7U}CyLaVPlGS5)5u7l!0)VF&XodiFpNM1(9{6b zlUuN;X^#Wz3sew=#Xl2cyb^OEhrQgn(DwdH%!NEWm2o0SiwlHER^ox)9z|M%xDg5w z&_nUDsW-_RN%$Vt<(ld%&WxQP=q$4mSFH?t`m2S9cn&_JY#wqBzGB~RJpo>N+P>R8 z2f$_EIcQOLF5jp?a$^8`q73UV6CO`w6DhGtB*5emeFN+cx7x+1xITJh+m_V`1#fDY zKc{9K%`*4ypHu#Nm^H*F)7fJ2JgJ3?P$q9+48kj6$p z3_(5zV`nl9sZe4tytq)83^Yg6oIE_uFOXfQ@da|!w7vlCHx0*II;F!O+&%Iw1EE2I zg)8|8x?gh9$l;yaH?C(0erJ0G-L3Ke8yn)zo`mo3wT`HNz6k@MH zU9<{FNTWP4935Z)Fo$Ue%E~}{gu$Q`Eud%dtUxVIn64^P_;1YZue#yklz{I5ruX$s z5h03lP;ml}#KvE=&pEeouHut@#8UVPqnPWvl`n7^63ZQg-#}Xf0tbv{m~58Ho2tqt zsRELaE@lp~d@>i^*9FszXX6PnH@lAcx(aB3=?E$AV8UBA_XDK9w?~CX&zo75uK?(i zGhh1)a2NH$A20bHq%Uk94~wox`W{yFU1X(!k)Kw0FE9rX0t0v);-V4{>|$7TnJkJ& zb{bht=|mdRo)U$jkZ6H=-W~nzNa03CT}Im?Vz9M6WdDgvt0$r7QV%ArE>W~vY$2}S z$++J&ajjUPcKtTvi%!Og5f+Fo$Mk3MvVkANw1Ydpe^Y%;YX>4FVRBIZM)K7K6(1pd z^<}45_-ZYNc+X?++}R9^8M8ddVC^CuYY=^4oz5d01=Gr$k_t{Tr3%u*9MLH>R&lyG z{-eZ!xDS{Uci(l*)sR3(_6%;{iiklV*mSfZUU2@rIe@gRb!um*cPRMFxyu(j@#i{r z;wa{f%xRq5M~Axr7>VEs`9XR8d7!xhm}BW9tN=|bgr<4mWMmOY2mc0%<>KNlGmRmz zVi+z~!EWqD`l1&sy>VcmxLxotz4CG;LHUlZX)Scr*PtMvB$tT3^M7J*_tE-G&Ual} zzxlCu>}c}Q+61Lch18R_btNaDB<=;7-+1TQtKfb{t?g}FP-+#kY;Uarl!Q9F&NqCt zeb|8$3j8<~+Jdl*Oz~*T5S4PY)r}))E4`*f+jf8o>T?7>j;&L8De4Y&DqY&X<*~PK zYlx*#;!p8$8$hujJ8&_2cNFUORpupSr-CxlXiLS(+rq3b+5_NDky>=uowr|g<+0Hb z7`V4=M$GFfQdjH)wJv?{bFOpG`(rkkNz?bsig$-PogB*&KFS{e3xvoYUUzNPb`DZ9 zmm-9jTcEHC^$7r_&qN~+Bwxvm7Po`9%*f`&F6f$xcrKpEokAae!>21CoC4$fhR2(} z?us)EUl=*OXAlVrFlUm}Td61rAc^Mn70Qk;`|*tPsJkC;eC*zh^L@PD;tCVWExJp} z36)7&`Xox-ZMWWn^q*5>!1u;k1df65c|m5V`TQpL@wc9BG>dmcu(s(C!QGdh1o{X{25#TFZjF*0Z78cCJj1)`g8yy;|Bz=Wqwjp| zE&U69v@8T@3Hw$#Qcm2^PsuxhAa(2O&s+uofT67en>Hv)w#=Mi_8NDXb64OF`zx20 zh-o4%ceQ9*@&}KWO#xbBw$Y?)k0vGCM^N$+ zz644+^YD2|WR4bF^6)g{2(QuuV&Qv1GEIE2rhCY$Wihx zz@#ZQv0zgp=!#FmoD$Oik+KrS#NP(F(l`f9f1?PhS46CbM zT7Oc{b8N@bS#V^_Jmu$%s>DiKFYx3RQaV8yQ40l~NsvRhmXbC}ngvZ8129EhTn!;n z+&MsLa6Gg{o`+;>gW_|qL+U$N2ZwMya$%v|E~Z37aS+H0()`;>ZVSyHvmSS84*U+` zo3n>DHpdakPMXWlFijfiT^|zKn1GJochRuaD2vsiD==dS>xgFN5hE?~EQE=SQCvX@ zI1U&CiXzf36V)N@bL`|LyAj;9eJcaDRxGQpY2m62CZmP(u*xr|?a#>`tp_iiF?ug} zmK2oh5zH=62xpjar@}8SG^J_pBh+#PajPg4_Q9*cZHdALDyc{XkF8i%^E%~qraZpz z3;UsfJYFpHMKmYtqmjO-<_saiSOa~Ae2OrF+K2I$tY&zA2esh@T)haeV0+Pqd8@eU zu-6*qX;Fge$+{grOuA;!Wsmbc-qX#r>-?qNEOgm`k0T$0fTe3`JcNVL!n>s+nMo3r z(^w*52_J$%ACyWawLb{cQ^jlq{$TY4ozw7$q~X|!>%kz@hR)8LeGmZ|G!rI+6zuoiOeXegJ160{7iKpB@*Morj63L%iqGSIxyTLNFZk5CUrU)g z6vz9V{KH2XE9hZr*d#zf4&gDS7+c^ig|WNb9!We@ldv%Bo{{Y#v#WDR#4Yw9vl`W- z=I2JL4;kNXE#7&5WPhCeB>VBCu6x7%c(d9MxG>TPofcJrN4L(rF zHUH(sTSiBtQlt$tyoWwhr5`O1cU9LJ(*VkK#CC-~=k-A<0nAY;2t(hecvsJ;fkkK2 zy45R}cXcePtJywdJ8v}=L!|5jpK;$ zDj>_53(8D>Q{>ZW@L|?%>@9siMf4B9qw0!15&eAS+W7s**B8oJkDWY0Na-Eh5C+@N z*H=qZp~<}A?$6H=VP zf>Y=N!CulFNs73bwc;b6z|G~s_TcUTMoKY@WHMs+Fg|tO923vQn0#NoK=*EJ!g!}t zG&F3Cum)$4*QRecy3%MchP_KEd5aNVk6iL#GhYjw$?Dw2$BrD@wPRT~k&qWJ!2LI3 z?v~lO|J?n8KJuic-iX|{oyBcH`DR`({CTj`#N4Y7$|HhcDS0#4IJX8>K1ZMNie*g= z3+B(6h1`k$e7}4MgNe+&;QT>sCeOd*dmOgUk3Rt(;d+4q!JNLOG(9^pi=2Wi6(sj* z7qTv!2yuhQ1{6$FCQ*@TS+E=YhLiOxt|;k}$;L8pJcfGuJ_gQj=zy!@)|;=t_OeR{ zcjAx_tnF*Lq2q=IR51a!7My~BahY@AJi@g~t;NT!cga~NT~8M;^?e-AHiYX2%PtzD zIc^kcrz{RcC>pn%Ppa4nABZXjH-KaTx+gkLbe?D}pe|X;Zg<+vJn*iLSH^Z4n#aE# zyWFZ1z4mMF|Fa$tpDt;8ril4ce@3zogpK8lAs{$`|KL%sKxDEg(-lgY zPD7(irvboUQIPqbxD)GFT#^X%_+eq_!@dOvE<_J+>Jt;L%WB?6jRrvP_FDGNP8#1BJD5i30goc>HM zPj^*1mqFG53CsYVTrPzg^{Ess8tJqGIRLoKmI22`8I5bNICE^2i35W>2X5JN%cc!| zs~AsWL{SZ9=*s_3#Qx6gEieckupSNWt#wWb^mX_&@Ni%%a6nZs8!Jwzt&DMR@~kgV zZc_yaK=sZ;1yyWrk+J_*c298tMA_*STWP3J37sCoe%!fgPKRd}^#X z({l#39GVvaLSR-cl~&Z$ndi)N=S&^sqspGbI~n#A+zY;UdJ~qW@}7&!1>7SWvoJ+- zzRZSUb|#*-F(lU zf(j0D&q9Au_7vQ+1^ZJcXjSoRG5=W^M%LppK&%p^Z}=jBqdOKqpo}YJ3fV)f`EF+7XriVf?&nH#KO@OW-Os95HlB}XAtXb+OFH7o z-`7)m23REr&QpcYNqzr`*gY=KpSQQ!GZ1(3;}*u9YytP2EM81=7B4@g*yXb6Y>p{w zscf2Y3`p_C8u)eCc~}p!Qo>R4kTw8x@8V07Ls1Vd{N=U(6{XnlE4c*=)9m~^j4xFSO4d3pg;Sa3OM9?s> zg|8qDar^pd7_TriOdp2tjX7REKC~@$_ipsxb+|%~yT=+pE%`Wd;{C!ImS@;qDPd~@ z_b#NDcZ0)aPsB!Q7`Sn!HcT;~1E>-5h!Ld7FEdifTyWvG@803q5B|Y>-gV!b5m$KT z^r>SbJKnqdy|81TBw%}+ciAG)!RYp}U54k=`>l^y9|-XLZN*zkmj*v}zLFVv891~( zIf#`4f<-|zZi0f69;y*&n<~h6H#~52zhmEa>$PXkpv=U+A@~ip_ustvW@d2`-q^cq z8HwTEwSOGXn!Utbd$0Aq;9gpvRx)M1i8l&exKrE$ARjo0sxkMqA-{WXhlVMof3dx| zK4Jg<1NYzihFdhi-tP5pDZT|OL+tUQh2Bl8<4nZHpmapR6#Aic!XEL{-yTq0dKju| zDt2^+d*N1B<*KS-T*+mta`MzuH!MJKDhPtZG*)@T0}_m&!BtfiL$!j3Qv*j%ez?-k z=d1EGsWE8n5Jr!ke#Hkz3g48*h1?RgKu><+DO0OlzRa<&y!?`j4j*LLBkNEtyKMPo z-JNYM%mWVU2QxiB>P2l|f9*TmNg@3JSth)j2;JdiRkbtqQ1od9v{Z&}rDawY88N(w zqWhI}fh=9#b=U2;-2A#L_6yiO(C zABSG6pgIup9(SPdL7#v&ZoCD!62&_$iXw%BD4p2^(v`JDV$wayjxrBa}8joNP#v>X3UwMJ!s?o&QYEm$O-vo{ z=+S`v=f8^m2Q;x4suMd1%n4)vfji)6xNM~-)Tul3Q7~!Q+ugw-aqNSIS$!A96viy> zi5?DWrLKTGt?_75a_slN=N)f<%RN_)9Y4Bn@6K%m7VT*DE*)R}%b(jH3D}fWZa()0 z8Q@HH(WbL>%)#r;WPq3&m^z3PMy04Kn2vq;;4o6q`EXd%x6F5`n7kYA-!&@MRo)6u zYPsutw8(+m_;nHGM0Vb#=70z+vphyZUGxd)8-D z`gW{C+mtrQTY$9wmXaTUQu15fs$s9oCme{=%BoaV<#8(u(6=n~4P$*naS@=p!1HgV zvnkwmRaF|mh4dz{VvfLy2PYgAkJgtmEX;hhGd}qKcfa$2d#}C>Z=rp=0dI%G;~jo8 zUyZ>9qAla^0(QPJZHxMr*!j2Vdu*@Knl}XMu?S8RX~`!71fRO zM1KE)eS6?+MH$^yV$L(NkNkbwrF|j(or{&FG=lQ_EugG_%xk+tV;jW~_>Qrs4=H>{boo4B({Za+O0c zspKk6!owwjuZTVHmHkZd#H3$<5$@%zgbE*xh!1_@PyYDB58m^J8?V3O^vR1+{(I*@ zG0Kc63ZLQu=Q+O}(W%yUMDOffS3GO%h`0oj>{XXvcJh*=BZm&`8``loY(I4GKc3I41NOtc+J5NriS-XkHZuEKJ!e8( z;ehZVb0W63it5&s7z7X}fsuXN^fHIWhSBEukw5xRAAIlc-+cD+(-$2*w13wQPy=y> zrB+97n0sdZHpf5HHb-y2@EVmT1)ii~!y}&RFv(>z6&cdvCk>WNnX!eq7& z^z$YZdn6>X?AHZ*z^|uU?-D(|6Z`asN)9)nT%l1mVg7Obw88R)D)aR=T$p|Lz{wa`?jr{S}ton;pxdz?vVV(Sb>DnlO>Mk@fJo|uzugo%I)8R&;#G> zF(Xekrl-}75-4^|(lX%eFV4oaP{o6)jc1Zk@e4Qu_9j>tjPLspc>%K`O}8jInMz6U zRw90x5fM37O7j0&1xn!8qeaA^c6PKk<#y$_qF9o|AFEnS5<4&Je)4L5VK=^TP_26b z^dzK4!MvuQZNbGZiTZPJg%!wA>H;dzjPzpVlLZ!%^J(WW*QM~n9N_TatH49GmWfFK zyvaN(2XV@hlogN12yj{zD`Exh4NFixp)OxHb4E(mz%%nV3a3H*=1A1L+On#3mB+Cao(%tM|GN$;+-A+dkt+F|$E;?CAcF^X%+P>sTbKvfvAT1$ z$B6uz*akt9c!Sb&1{K#o(MvM52-xF4tVnk zt=qG_eND$2zj}`%=jy&fXs+Lf|5l}AA{fY^CjjpB0bC>b3st?I--IjISv&@Fd_f&qIGkkPl&DFc<1-Mqws4Io-YTmi189 z`vXIWay-p3aElhj!+ps`hYsx6wrOv1Zv>V13<{#!sN?Nd6bk7t1VW^__K&O z@@;eFTwg^+3b|o)W+S*p3~G21(v^Mj@xw?D8yMa)99BV9>^Ax7e<=M^&drndhqeCc z(Rr+R3Dqd&x(1~}x+W-4DKmy;6DiXnx9pApN}S2 z7SD*@heCs4y-zuu!}?w>m%)xpeTi(=sONMl?-|@W0IS8Oo=s6|o|zNxg2$DfC-nJC z8+4(b=erYzq2g8G7D~n}h!VqcT8E32D9!K%Gi9%-Pgw@ND(pj9PHoyESj{kipGU~Ry=bZqzznf&XLZdz+A z1lLxoFUZ>dNXgJX*1cBg@eYy?Jl`iGu7ShG!r5eGhLVA*8X{Oy1R0h4rv z^>Scy8?v!kaC~s*=6wVE@apJq>-TgCCd|9E^2Hr0T|)Q(G7=L2c9VoxA}Jn`tDY`l z<_y9m5^;bdOG1%1{q5VjIvx8Yo;`a8w;kAiVC9O=tzBC^^}>^p-&8hYEhDAQh|rHU zF|>I&31l4vAYeKakdv&wZ`DG88yr5k8wGq|a?s)4=%^?lEFYraqU_f6+3VO?0(4xPq3-fk>! zgnrrDQkiF#z0wtD)#eM81xZ#>It^qU&caD|jatUZ{tgt2Xatff+$4qZ6guHdr-3Zt zS2h>du131a%GQFGA^Y|J?Y_gt{*8;B>M9*&Dwgnh%$XCtin$;E1{r&h&Puh+0T?Rk@i#mHqK9dEe zAhM8Gjkiop)pa$P3(AaOrsr#x7&ZU`DwkpkBhkq_$w*92m~^CK!F8JjX3sz!DN3s+ zuTV#_&0R!gLkV2tHzN+sN0m&H{WxsDC~p|<2XH^B*LC=uKMCJ^oJj*ED0*rOJA+*i z#HnRbWS`>7aC5=w$^eXy*1lKc0?R4a83E*GqKXf=ONfN z>ir|wHIG}*nSHl!RqI>f`??C4b_LfYm~P!u>KVp$duiVz!%Y8saQK`=`cO3uQAvSXQs{gt=@DWh!_No-T z*kfoI2{^D%WqIEN%uBLTUDg{`{7?rl9)DxSLx3v*4>8SQ%to;CC8*8%YB z^q~XW2R0XbmK)_qy|utU&RpkHl!?MW58Hc1?ytjXgME$X17;Q*MR?<u-EWXvrt=M^UQhQM^IS$p;CPM z%v*nMNv%RVz*}G}(97d3l$HS8IkeHT5TblpOw=bVk67PmqV|E)t>crX^9{vY)BA?v zE!_v=Ac{dsAymOzrik(8pvl5wdz-{W%?DhAJJL=M#ymN6wxSv8g1lA3_FUaOrw{Kt|QA_bCgNX~*XcM*n zyYIU5j@#dWX!|3F4{qNYegU}l62-If2Jqjf!n1#?Xi&s_j0GG|82u2a#5+oo!=<=M zB4H2d-vH+W2x;`!g`nZqzl3pI3={Av<>o6f7s}F?I~Vfv%b!bedkIO>K)_{sJ8$Lk zB*tA5ps>25oA~Iu@6C7JaqZd5E@koI1N*`cFL&Q5S3m2!tDm$5uTyv*G5jh6+eZ9{ zjmbEeCf{C|5DdD|OLdv^ z0O}$5H-ST=^}`;*)rJ~I5^^3MEzGGea~@>Umpu=1>C2i&akEDXKWCPqgO4peqH5mc zv-sv4ueqAB`2@rn=np@QeZ1KVEKT>$H*ZSeP3ZXK%_dLz!`9;!8~qUJlIvZ8+?8mJ zs-yn}Qk-9z4?aN5YZn&=E(PJg-hlq7-<`sN+Z6|*)}&0SR0Wb7E<$@OOxPo=J5FzW zGSbw?C&x@@{35UdwANQ{(UE!|DShU$6Bi>_3f`x6YgVrCQ(E2<7o1KX&fb@8RTrI@ zHrFa1s3(GeQxQNn=wv`2 zy)HnxK`7c_y`bE*wiH+hL+I2CI{7g#uax&N>2yNTX`$w>ax@a0d>>)lbKwX5QH84s z0Nsb|^qQ-Xsx6pGu?zp(nH#_jdtm&};4>v%;QyZuenP z_7c+XDRrK^C(G~5!{f&~)^3#5qN9qa_934_pOR;E{RRk#E1 z&?JzpQh4a5P|(_!2IBi1)V(DRN{e58)$6XHXk~eE?9fura)Rt`@QLDI(es3VANS!& zAg7yz@AJ(-*-Z?-gli%&EQB5OKps$FJKVDbD#9C~6k0-7aMhKkPmMxVJ+P17qgA~t zS9k^nlb_DL+U$IvXHe`5kiFuA=3b#rDDQ>NkkJ?uqvBGI=q`M&*q9Z?QME!Sjwm!w zZSn6g=9{n$c*jn}mTcLKdf<(ojoHjs(OhiIpgDAvTj{Ci$v~bVDc&mq!X%z@5rpU+ zK+yHmdtIGkj5aJAZr7=R-t9VmJXu^f&bIk0c{uI*d(v^6y>tV8XNv^>&T37ynw$pMh}JnEd+F) zK6wc_lCdC+cW){b3P9`=eT(}RT<0~iPTRN6&ql73X{2Rl1%zjY6^RFM3hU%ZDHxX~ zSf@8)xpmf>b+)%QH@7t{#S+aO#5JDq?PbjQ@uYi6)dyx&Y-pJ^PI)iAk$xq6*x?Zf*~|YK+DkP%<}Q?3r$_loc-9-~W$)#DvzH#vwlvo-#!4lgNAc_qb2jbg z?5FI%!r4rKXSb=dX&*Va+|B{cbMWCjWT?s%@zOw(@rrozxRpVic}7wYkq_%?5LTkb zjsw<*QOK3Z+-8&txHy|l4KoiKr?sjoh3tQ0Z>EqwDmO;x1Bl{%CB9HRUS@Dq^I$p2 zseU7R0Z?$-7oy6{4cF03eDacGqYMDpxW2Eq4FG)$mMvW7Rh-c|pQZHwccFiB3iMZ& z17Ie_6J_b21pO0I*6JnD-}iy?^qL|Hd>$;#R8ynmuc_qsU~; z=B%2_f2=)dge5qM0>aNFTY{+4W;r<kdfKQC=feh6R2Z*)T@e-`_i>U6je)%FbOIzI5`%8wCVhc}jg9i@9> z?gFmG=?bjn(ULv+xY-k*>Rx6o9v#c=N%=9t{N;`1U(4Jue+BQECvngGtMhh!&lK!0 zKK_}Sl>NN*0_4-*sQU$F7E^W#YJCbqWiXM0=%RhoOD2SMa|nhe91TvvB=smG>MGTl zTGO<81FBoTIzdMyxjIa8^?ejdH!3dO2$g&6#PQLCI91y>0!pcEftGF(?=T)-^AoSq zH7m)e*8CLi+Ru6WoOV9<0yRSGGvoip{kA`e{r-{K?+pa<&ZGf%1hi@*9AqFt22&KB zK%HcA0U-1$@UG$6>J$t;%q3_?rty5E8bq|qmof^lKV%kD7$9rq;@KRLZQMzK z59Yu1zI)#I`kSshd&Oz+0t$T&Y+Np~wUYM#anj?>A$kX)7+Y zuznfA85TH$5P3lT7tS#H;Z$h>_7c7PrHta^jLD`EO^ayGfP@EUP=^@*YskcJ`|0rn z>0y6E(F3&%tiKQO1~7pfj~-C<#YZ+>-gs%F_;_Qo0ix-#z~c??q^Mg$)fuh1@Vxz} zN{6jcI&3X%>Ig5gtbbR=^yPRG>#`TD%!U_(tGd#39*1-gdp>F?GK_Avl#pgX*3wW% zv_l70RrL*l%f;7?9*A%^XuGL1W&5%+2VEwC8L|he>c!K8=!a~o(odo~90dteH&_`7 z)e+lJTA^9DHQYReXdHHdmi^ z(cW1ujaeMp$WPP0s#K4g2j$P`#+)sMJjqZ97%wvdFi6o=3}Eh9!T#i|^8N87$ebxR z2w>`g0AKFY($#`(v|iHh z{Yk&M>iq85){i_%Rvh;pTkbg)|BBqB&soFDzhd{(zry>}MtvkYR(6X$4hYuTrk9K( zoAo$MconHM%S2Z zmo_Noh{>iiK-v$?lRG%YA#;_vGmy7BpIcKPI4;1BiZFShbZ>}~rK^q{&B~`T`P~>3 zrC{__D=DW@G?s?WE6$v}gh7GZ1~#lifwL8nhR(=+zgPKnCfax75<#$b*?mu9)x1ik zy6?U*ODSMTIr*T$eV^8#!Ts_0Gq~>)4jI@fGuhy{!F`|pXw%&HNaHWw5wJ7Fp7q`l z+O~3@5qQ>BD(+`dHj|6t*t4J>UXm>E>&A`cSi!0x)F6fiC3Tlz^Ei9vlH&*Wu+sb5 z)k`}YQ5t*ELZzmlQ_y0*ny3n4LS7&mxjd z+aGd36V0VYOi-89v_lYMWWxj+BF+f42~|uH3tSfGzv=cHue$XG=dq15urj z`j%@Oh+h<%pRo;`w^_;Q26uK&&XL){X9ht-dqA1lMK+9_-7If*ZEI@k>q_40-}lYU z|5iV5(NpPC&H_O!MLBb}qvsagFmi6ulncI{W9R(y^SzzVTaRFVZ!om8KoXfBg6#Br zxy<|`8%EA=fk!)UXMJzk@o(Tvo{Mku{A$>>r}4T^d}9mjU}}c{rtV-Bt%gbl?j2mk zJ6O}Qy3$F^(D4jaC{|xpF(L(I6Uw8e7P($7pU@Bf^8h6 zDZ|~(FahPoWBDB@ub%%1LBl$R5JsbzqwFTSvDa?2IK#2KI>^T+Ixwa20K)Tl;@370 zCDHXf#{Vzq@jdlMkeoDUoUvfAB3@3f8|k8P$(Y@V$AM(XCQx`Q0Zd)(dta}#RUbRw1Ce{x<95249(9CN-C7M;6{Zvvs&4d=n zsCUVp%3F&3W(5<#1?!}w?u93h%E?l|RXQ!2MT+Wf-l z_Vg32lmAfsoh`*CXpxpZ2tzR#+ITV@do3P!&03vKg2+m@4~(Pq=_!q)1g9PcekkWB z-h4v3@DIf=*FlgburEtR(E~Rar!UTRb`7CY##&KdQ*dBlZXENop)rmA`a_$(tyW~K zpk5pbRU$V8%GV(BCVV6A>w17nXKIn+Cyuw#H=bYT{Gs@VI74&5_#o#DZ96|z7ZbX;6PzPv%Q=>ese}Sx5V>yXEly2Y-A;}A4kZTW6HG0ggTQjK6mR38shE~*pU(LAyTQORLbgu+DxipY8U^{q1VHYWvG zO#XS}oDQOVt}gHwnzye{VBRI20`o3COlsa%BkqWg`*SVqYVix@u*JHzXT4wh@nV<= zlSAAZ$)i||yCa@p&{9BCa$6uqW-g*Cdr)n+e({{vxvlD5o0j~b@Eo4_HO)aCVs8$Q zs=HF89biL9Y1ccHT#~n5}XoBI)L-G#&}c&dqC#AQ{k&F|9^foM3JcP`D<#7(&%Ci5}#^GCzC8OX`XX&`iWX+wtlngGo7h;AiP`x2A7tSHqGyEpuD^=Po>l zC+uI(9Q@}_&EdZoI>4{^8I)4wxqHZ}*7QKmE$IO*Rb8OuN<7DSfx>&hNN4l2t}HGf zcGLspzD08gr$K4Ms~5RRTRP6;%y-VP5*@(7coef#!fuB)yEO$!j)R#7Xn^ z(LHI@Q-HJmqH|E)alo5KKA|nxIL-{5wcFMC?{Xsxc+3C_NzM{<5e7aWED3LZ?`w6% z4!6BO?7-j8YmGDL*c;a~GteJ;VCsbj=k~;Z@Z8kd^yYR-$xqTP?buiAJMjg%6G^{& z75z}T(YxjV;>t*9Ph6+d5?>}r$oR?A`5Dqt+OgERE(3VoMvM6a`&tXy!Cql<*|^Wa z`F=#p`?B+ut546@ll!IfWo#s0lJ0!{9nE}017N$}|Xb0x|9v^*U&cgEa^<9Tk zps#Pfr5(+DLmLz^UvVG@=Bs0wg&$(h{u25E7mN2Ps!)0sh3V0^w4<5t1oWMGU>`F$ z{SI3|rRuYPDlRq#Is4C9ztH~L7sOu+9=2=Mdt!&Y4VV#a-V<5Bzt$CofN_xwxNKJO zvW}beN=|5B?arw25O8OFwbK|7i7B*QgyuHUSjyc=0B20nn;HxdfV+t~BZryER?g)~ zG?XaX*~_%ga{}6#_es|8^E69Mf_7qsm^$rz<3+4@3IqCRH^s50LA%m-m7<-*Cx6c| zxjzN_iN_zRN!l+WTKD>MJM46f*_Vv5U7WOIX_y02h=XSAKwN^dAukQ2L$Sd^>lkv@ zB3kN3$U*1p>d?Wxy9fGLp#pqk{hVWSk11KM^2r!0dzCjI*eK4^dV1GS;W2s9dJiP| z9p^5BVq)gi0)Hk#t&#wH=JS0plST1iyyYt-ToyT>e4yrN-EBh%w+)2cQ-I2KY?g^G z>7d&pwxX-^$}28C32b1LO9l${wO!Xrh>x;)oX6Y$o4z^5z9Dmb;iu38U&IXW3eC{M z41vzg588T$&a;n%EK(}!E<9a%G2oV+6_3kwsVx3U55LR_herCBUu|Zi}YZw8ekhp>yopP&sT0D z?o&=ImnslDY7ms3QRiw3qfqanC#rNR7$u*H=Us%4sKLx^vHjI>+4eOTQwBn}Hlf>*R(cfSnaMlHE6dxBG#ccc;_&2<73QT--GJv$E{-*M+ z=wb)UW|FX>U(w`=SM2)F|X*cwSG~ z|6%iTk2>Wx=QZei_d51LtyerJdK>Ac^|qxx^(zaPd=?pNu3A8!J6VzL4}?r9XU+<* z2mX#2@&v?kN!LIpD8M(}aMhKBh(R5n9sQenmP=rywj)N&_pMs5@aFqQwa;e0RKLte zUA17s`JxB@ju>zMeBG|0`PQiUqHuR-yBC81$@bE^*4x)m7A{PS?3%KJd#{%?F~Z3GT~t z*7vop_MGTy;9EPXbhUobhx`#?e%r5Qg6XVEc=anQj?krExk*B-h7o3@okA2GDrq(} zkY`w((n!TEQL{;(!dENf>4+FVGp)dcAQ~ki?G3kFcgPmM-ge z-1YlRmf}UsZz@@byZ&&0z$7Twqu4M@`f>eJ=u*1=IsWzE-QKufZsIB$d)%|^tvzOc zZ1TO=I=FA|ZSK8m7S%tiH4K&ReMx_)lHw*+5zHn1Xf0A|zxT#NQ`>vo?q&8q zW`C<>@4pq^dteCS{o~(ztlwvT)jyZ4!|Z)Ye?WkUOIL;dmGtBK!=H@kQo8;{{=N5j zUug?f1`m~TZb$qgzQshIKj+M|zXv&xu>Z+Aiq{yra#d1_Ni1s()$GLD(8PW-{EFVQZ6ms}zSOx!XfMfcZ;w2e(fv}-ODTufEsZ{6lw z!#J!~8uzSZ4W`0cnJ>$=Fv%L!8z{lJxtF(wwnBru2KXNBF|`KzhEo?1*~ExA=%Ciq z3YTK77F9scDT6yBJK#lYc)&5*It@)oZmrT06#UGH3qK6f*6zEmJJx)LC4BuME?GKf%m7lVpikDlf?9_y7EjNOit{-X#em`%{ zby%zW%q{tRxU<*Kzt)yQgI+6hK;NR)%G@#R>$hdCu&Y`>QT7c~2(e%SW;`vNo-v{$ zsa$Msf|#euW?`Es+c*wXu(L0xx+Dp^e6MgiwswH^=*=(vjC2TRV@oey9QmEw+Y3yJ zZE*J<^l)N6dW_q@S4s~M10*S-2e*7OdZ?WPLU%cOjL@s+<+3wKv|I0YX|EtXq$?eQ z+%ZrbyB7Y8M~~emJ6-n*(4*k;#|*@Lep%6D2IA3E7vd%k78Q(WtRSMe61yfY-IKFk zq&W^aSJI$2iUQU&FNs}W>}w4~r!f8y@Thb?9OzeX$!;;dC5hUM2?>s0X<+my4>6?GZH>Yu@uXIkB5T@nLNoZ3# zCmp*}he$j{pQP{Xh3Di3`lvbKw7GMlJ-5oYPZ#WKWKOU%G6RGs4nez`PmH&Kbc*yW zG<(c~zh_WXPAI8!%)=EA;#2!@xI^oE)#Gu8*88f)q4t@96it`=%_Fwqp7hY|6-;=FX3CiCZ-0v_Z;1`Jr$U6bGpYDY{mUg?> zuoKH*C%#6f`E%Wi0E~ineklf^4?{6Xuh*~?Un-X3#B}thZKMmR0LU=TkxAYaCz$rD!aSA(q>qT_2Ai*$ax42;DPJzv9G{2jwuoNWKi zWBfJDMU!WyfkuC;F&Fu=h-b!ct0BJISD{Y*24FA#hs;T-lx{tUau|#u zD6@R8p5<>SCZo)8zUI8&U~ERtGxm>qo{!*!{KjH5mdx}uti~{&rHdUjIjzin_d9~w z7|yx-B6IGj6F4)+FNaNrzWd*}oWTFs`FU0d9cxfFH!w6$H*89a@$=tJ7eNN%4YQ5^3oKkLKXx+cFIffxR{7&W?hG)h|HZpnUsj6##b;G@t>UICKu5@XMbz_ zyE+E_|DKy|XC$RxmCa6OGub2oebQMb@gnCqMU<+9l}#qHM`E^ZXPM*!AhdKkjy!un z0kLXP9BJ1I6-dSU_oF3Kc_toi!hwog^cV&ff0D7H=K}{>?a1N%!-G4wZIS%Orly9* zyvmnjj^dQo5PoH@LQC3 zYX7VKpWS`&ui?IPz&r;8sA97ouxA4Nfz&=$&S27c%CQA^2^$WCY~JC^qu4yP`RPxxUIy@e1e3 zaxF8QJ6)PTXMf*v^jY|;a|^VLU>c7qM@RU?RLlZ2Y3NvLNg>d<01%r*S`<~nzc*Sc z5laC{WYR&)oc7|9m~-**k;D6jcJJD@W$o%^-EA!mOXkn2uFR$!Jk{k%!uX-c^@jHD zRk&`6q5WCxqugiOdgD*o-?6UIF~Qqikz0|@q$_X>K>VjE{*fpZ&m=~0jF2dm%*Ntz z)D3HExcSDjSB@OnzIDr{-W3g}+D6 zHq#lqM z83(3gf}b?sL4MxHt)==qn^+1O#|3zgmYu?XW9BQWq7c@kJ~OxGvP&---M$TF&$^rH zYYxmkfOIm2QOKC%zjD0s$DPB$@o^f{L_(jWq|z&`C(&eaaRD%9a7hp1AIl!1R2gt=g~=CFn57NQ+w9e`g)#fP@L#Wvi8$e;AnB$$x zgqe54g`@Ts>m=#XKwkvD`0wZO?_*Z|FAut3-mK9R0TLY}Ah|(rJc8ulL;|?h25BUo z7>GG-t;m!|tr+if3X?f9haBJX$E5y_=91fQQTMOZW%bkJ3+m!6fL~P3Zwv=FHO2$w zpg@e{2#zmq+(cq_f~YQx_iGBZMmuz{9Sg@km&!!7#yt!X95LsXbU=D-pyBy%$F~>_rF7V^E?v|Dm?&uU$CFM5?TPGv+Z8B^QwMS z4%9a|wbnL{>Ns zOh#2GUxaEl`*-gmT=w$r)m2{;3(#_&53UmHI&N8+l*Ls-A4&2lG}Tp-D9~f3)mv6MamOjFvQG-D zV<}iCv9<5^t&{tiw9nsVStpgYu{fJS^(*?&D_~!UDiPcmZV_1{xaRVcqlfoyudioK z**Pwq{}tbNeBU2`fpk8P$@#vIy9+2K6_`Z1?+02;{{1>d0mzDG@B7Wre-MYXR(xQb z!9GQSc^Om+Po2ib#sFtN0;n=Tc8fk(S6t}*4Z!Fl-~v0hDH&(2VCahgOc>d>`VPHj zBI$^(*nQ#sKs&vOI#)Np=~(p_zJq@)>%+hNis)Q*XxLa>f?L1@F95L9!AlV51$ujI zfEdJagO$7C!uzfBYP<~`FJ@h%$J?OBvmV#uDKasJJUkx!=~rwaDDx5BdKBr%`&DRilVTLn_v()>0wuNbC=a4Be;m#G|S z>~AG?KwCq`F+|l6ap_kKaJMm&_V<%BY3a8<-m^ zcFNLKf+W-ETD1Ush+3^)QxBO2wRd86bK~NL^X3?P6)L1+4xhptz7bz&-aWu?ire=B z`#t`Ng?c8RRQiSiRdR6TQki5Xl{^Jw8md=O*@Sf=lY!hueYasePOH)s+nj%=k5zRq zv`5E`16ymjPoSzQN(ILi)?|11)&xAC za%=J!e>!U_>E)YU#G1V3(_GUm1*60@eNy;a>_Wod&%K-aAo&}SHl*}2dF1gSrp!Tx zb3I+Dgm+c3L9dIp`M70ljmGC=;^OR>vtk)Dp8eJweFizlug1P+cnP&n-)$epS$ymh zU($2^h??tKnJZO3^=QmwY2|hwk3^}A3nm!&N~&rQq7v#s74RuxOJLa$uX8q$No2)R zp76n42FCEZi!x&>x0b#~B?j559sh-WD0YJDsD&(vKeeF)ch3(&>q4v}s4FQ`t5`-5W~1j3scoHK{lBy3LMzFT{6rGs8qN}rg;VflW zVHr;DJMLlofBWu1rC0q>^eWg6%o4`4v=Q_3-$~}V6i;|}QWAHK z)T!{_Nv2`KyOV7>| zOU+Do%iR)dqGER6wtFS-7j&F?-cYdR^+~&8nSItxk%v zA#q+umCQHzV37GO>A2l(@h0Q^9#Hc$S&MR$B{RuXCU}!cHDOjNRn*L6vco28(O=ms zrMfE7U%~4vji*K$wJ>D>uN&SHJGO1w(Axu;NOe)Ew}iqE^UWFfVddxST_XC!f!nuq z&O_duOS(=lXWcC{XIw;j&g)^9_^zs3vYvLqFWS4LZb>ER`+h}VAo`(R3A8$*&?OSo zOi{B0x`7m(gPF;3W<&8)CMlGo2L@$K7dg?L!cB{fkhTttn&q3u3D7aqv9}JaU)!_1 zy|sR^TNP5|Sa{w~{Kn2(#cYWIS)aG9$_^yw?KvA}Ud_)BDtfEilr@8);o}xHX`tTC zUn&*cT57B1HZARJq(W#Ae?7JNknR0+?S#!O+9x3Vy1p^vVZr^IQ(xge!eW9kgsVKV;?eRe9(Js`5qIVem{$XDZSW z!+C>@78_!Yzw%nCmcVjYxSOq+cq7`eQQq-0aTegi`LWfETmD6?>ka1)*cEQ&sb>6! z`oc&i>%>vp38*?sOir5?~ZL7)-CI3Z(Uedb7;mP<4@w=nYvuu zJ79i3GG1;r7G?wA2EPF?N0cq2tPjryr;lbEvaig>w487@;(Zccnv40=I`8Iw&81v@ z!n$|-^PtX8zzN?`Ca?~atincQ(o`EEVB!B05i8PG{|}y@#%k7ttGEHj3RToqQ}niv zxMTQQ67#zd-4Y1$)Nykt-KNIH6DaX&6tZ@Vy)_qVnDm!A~d!WC%P zP+Sa=00wkUsXNSx9l)+8`jl_1aeHlXO%yW!Wcgp5lZt z^~U54dfBDNj}GtMI#ArWcJ=b5jr9R%g{EQ2oI^DHO`W@{GyNdXvch_|za%j%tOI^j z)h)m=#?ug3#l!_hu^Oc;dNsabK{YM#H#8DCRloY} zOOD5BpccX!~f0f>C?a8fsM@tJM)Wp^>9kOjeOAmV@-oQCI+u|Te5qF_)i zRT{eN7N)6PNNXs}n#GrO$k%*&TjlDz@>`g!M-Cr4uy^;y^=E3xwKVV|7jyH-no6*q^+-3erYf@oN5R_$WVz4Fx-pekEQm$XA#0n;LL2Gasgi9{#fHi$;+skj3nzAN% zvdh*~c4roR`xEsoUwUR)7_6Xlw^9@b9ji&A8`NsGY7iSKcS>5!l7sx^xst3U5;Q!~ zRE2vRRqDl;HO$Aa+(vPB>FL?W2zfzd8n(QqN(nyrYjDb^cGwli_H(I1=3|G&_Gm|4U<&b*KBUol$-iA8Fa%;-6N zifJITisF=R%qqjJPkZYsMssmTTP5?p-WixBy8``K0p|4o(U_Y5&O9}Ap8w~H^IXns zG(N{VA$a|H4<^p2W|H~OC4|Cs;gM5uW=>97vtj#kBU(T41b$>ydkWpm2Rwn9SLMiC zsqbeK;TSqq9=D4`cvFf2hyX7$00wwX`PZpBpRB3z7^T98bzWJl^%x}n?m)_0`+t7z zj=4jEKald(D9;4X7{4%&`H5y9(UZut*~lMa-q*5!rBVfue}wPSq$A4W7!`7XB$4%T zJ|GzI+Qa>;>aW#0s$=2VtlGy{!@^MShu1Hk{_U$h zx=e0v#N=@rt)i8yZmn&nw3K9?X4kR*gU%Dru=oz*`zo`*9|X*+@<{?^ERa?7n_rtSG*38_-?qQ!$*38_7u?{hh(XkF;tXMu=HVBYAA4Cv7 z5eqa2n-$0v{lhBH*f6_CwU5YJ4`dF1xHt+RsH)CaDw9BV8GS&08AXKYeyHTJh_A`t zq<~bJQ`Oid?suyFWUMNTr80AKNL)%~CiUoML1o66>3p@wYjDdN7CNYg2xs(6-s8qK zSxhM4j(K7>i|jJE;C#`u`l27Sk$F*d6Z;@tE1se(0EVD&nRmY4@(x!$}9O-ksE2i5c? z7pp5-Ly8e>PKT2+q)0N6tYxx|xeg{riO!}mFwB!$?eQ(5LF0-)@}$8j?E20Qms>FE zmn>pl)Jodp-_z?QfBeaV+Bh!fn*G%SuAr#5-LMy*z4Dy!GfDZuevmnlDb6NMm6XEL zoSGUj70MkOFu4F@Co|Dd4nStdyrk70*(S;ww=$>Q7q(+g2K|zS%zc>Ck-wTWr~CF} zPO|NWJ@^h_UNu%=d)Q^;_`@>~Q@r8v8kvH`w7@;aJiJsYT_>s=NSpwELMlY-(!qe$ z!ot6sZmnxg#sjs%TDQ~UxB4ZKF|%fBD3Wi0<7rrm?-WNPY?yu{f^Veq75D~8fMitg z@sI!nQSp$!x?S9MQ+~x=yV?;_rY7 zoppCteO-TF*PQM-ExEdUeZDw*3A4u^^d5Up-O0bJny*@uUx|bfQKJOWSQu`LaVx#d zAf}^IV1%EWD6y(R+zX49lpJUXsWGZ~gR}a3r*}+i$)#&ZWY6z)Ijm-*EQw%Y z^O^Z3B3T1W372cZNCi!}frwhQ-R3Ml1(-}j2S0(|1gm0+WE98`X4^*4fBd;A#+~T- z+96|y&0JMs|Ji|W{CkXfXHjic!KGmyCx7qGUAovXuPGO5wdp*vb)F{_zwOa46@K$- zWL~e$)?U{oSo-Xi_PUzs%t8L$WEG?Sr6+hYKlrn6ro8p6SkX%Udf-RSU`PEjpE=~Q zekU81GdthBS*LgTYxL=~tatjW-^BF~oGLi_Hvc|%zsd~<&MuYo?2b)qrkhzVmVz^Y zh(;(lOjEW|p&BYkG+HK+Vj_M96mGYn4FE!0f`qbMV~N5XnFdfhB{Bh=z-gr*Vgek5 z5s&tuKD5Bd(Fk*?s==xC)O4^pzk8dAXpP%{Twl!aHzrAIH2Nzq!gBv)Z*HYpbm2OayaV3jMP>w=HU2U+8Jh*acmF zm@Ooo%))T+`t4CiM`f;Xg4t(FpWkiucq{dKm)Yyl%}p`yWajA|Zj)OOYuEK%wL~k) z91E+eA1X1Y(M#@_Ll&4gTX?K*>9jT^IT2mMU-*!3SKk2mEmw(8*9WZ(8%sGAfOK$^ zVW)8tK1g^V0S$mh*bir5ff-D#SoQ|Wr2v5OnTDJdKy7@aEv}6TMuS(&wmOm{R`cS4 zIT#S=$tXVxUwC2&M zQ0cVQX{79u9kdndlJflKHg71rQD=x6ObfrQ8jIB1qS`w?n)CGo zb6P6=)omNT_+Lw_s_YK8W$uBdZF!HE`}NU>+WW76yfec;*>-Gj`nu|d(0bFrffq(r zX{?1e-#_xZyLuZUH5x-pW3WA%`s2g?P&yB0orMoVxm;kL-myQVR`G!99V{AAx)_p@L=zKoMyTj06ZF8}T1No9h4$ zf_gIzG4eP0O=(00r$z`z3`kIk(qTc!7}LNqfHxVCk~V&!1Cbx2k*wRs-B9JYFF#)z^R*0_%eS`Pj$DGxVK^M%42bcUG0wBV6K!I$)zJ=#0| zgKS1~g}(-y!GE)=%3&Yhj7#og54F!Y^?0Yt_eQL9(=@sp>#!ToligraQ+MMJ5BtL% zrQJy8uo>Z0Q`7inbn2Y;xUw4+xd`12X7%`Pn6VoV9P%=c6?Y?q?NG6W_eOutZRAd> z=BQSv{y=?hC}z@gOox~JjXSEBD5_Nsa!pb(%OL_qTu=??3raHeN^$}XBI-#>h~ld0 zL;%P%agr*DJ(C*%(H-9D&)Xk)C0FQ5BbTJC5I+o-N zxJ9n1ow1q;l@koB<$!ele2dTI^;FGamexm8(;QD2v=X!cquzHWKYZJ~tKR;CCFW#h zk?EP!yP$9)yJ)1>Ho7w&T^fI6N!y(5X?>`EsB!w@!oLRV{Jq^BT!k+$8+|UXu2*f9 zZE>wy@-=OJV9}Y6zpU5G#>cJR)bz%b3zEa@H2XZdH5qPC;bg9V<6R?7E0^VyNZe4d zW1XWvS4XkuM%89URQ#-lZg`S2C_6C0p~rQ2^W+C{;s;;>*~T^$XsS_>jH))s7Xj`! zbc2pWQC`y6C zk(}k2;cJ%dBMeks3>a;($Yb2X;##{++-4prV zvkQaF8HzP-kFM7lnTj)oXNm|@LGXp(hpJ}u7yLeM5Ab79^*0LnXH^ntL|U$6)LKSB zFqBkUf$)gKO-Um_N2C?8*m#WwTP}(YnrukQTbh*4n)RQvcfJK-JBH)o1(w@vlbvb z`-2FIu4sL8oEe<8F>h^rVt+2q9Nv0M%j%s4YuI+=2iuSS!}XpRF7Ay-BA4oHo>X?k z;PO}ci4LeAN@gh!Xyg9Q$zq$J*VK&uRJ{ZH)UO&*ty8_4x1m&BTnCe437oaPO;L_l z@hAi)TTp}MTI6k#5G9S$Q31I z@k@?e=VX~j$`N6-uwh`90z!MOtiWU?ys%Y5nl|+?`ajA<=Sp%dL(O~gE#TC+IEvl&siBzut zV&BHY>q2uX>bBI2vZedbNmI@_y{^j}uCfX0S;tqd`1mT;CP9y#YuJBvtE%qq!KXCB$Fi)x!{{smTOV6%e)E}1_E>8X8WB;e{5n_Vh zAZ_6lO0ZXdMErQS)0R7jyW1i@w8WSWv^&N=O4>QlPCQ0^Q1=p4&g-|Xo8MU*_c@WC zLWU)CKzrub${f(1%;7=mpIAUg9-7%QF$d%z6u1thEy+P(o@9|l1wR{{`LLr-)X5Ax zF$ad$rqVeaA0JW9OC$5TYRC+D%mDeKSQoxOWnC)e>pM0s>Mmc`jPZ3PnG3k1n0#Al zIU`@^)3HTZshnfq=dWj8Auwn!_lt zMm+dW+^fp_D0xrfVZy~uUFp4%EFyQF;I z2tSLx?ETZU|6F;zf8n?6b7k*I)r0ixu0?&*YIUVGk};kwYeNDZ!o@b*_!!tLb=mu8 z=^U=Abm>Yk_#=Bx$={$bc#`%{ILrF88_N1$r2SVn+lny6_mLm)t#KH9m%i_4aOyEW zXf!x>NqwvO6g<7RFz`Z>LqeKWqDg`cN{W(AqS{3A={4kPP((8Y0UkEq3{RT`hY!gP zG{|}+S}N|r*%u;ITu$kahO`eOLFt}6*5uwK-@HX7XiZwdw0`O=t~^Tl0MM-dOkZ}= z3_u60zSE#bIGuPmNlHlttGfB7h5U&dV2Aeihql_~1qP|7d>Y^75?P@t{eH$1!djb#*G?XB~`BNIP zG$7TxAo1h20Na!aw=x|Jh9%~Dqhi$C*~bbWTkKjU=<9LCTzieSn-q_ZR1veLk9)6r z<3Q(dC&!z-(y^CjA5X>%Zl}Scw)9qaZ)t?vK{(}f`K;%{6^W5P*R4j|7H9iVjj8Yy zD{%0>2W5i>M{eK{4vnv`W94@CEw!xE^K2p8-K@2Rt&RyF&s$o(Xp&D2`3j{OdwDY& zI_N}A%sAIslDE&AHEU$n;(3EJdQ;(WMHMRe%*3Xn`h1xUrtDj;gUg!31S5!sPk#F| zoQI>f9x>;sO378uj*Rc$KJ4F8n^Vgcz8Y#-8jZV-OywUt-#p#iZb_J3iNzZZ>CGnf zO1m@sU-wV!o7W?Fs(oJOPr!h^W{=nGn#vP^0hwDqye+I9{SU?ys|)8&uKcml$SM0Q zq_NLuR1Y!tD%1_D%!ZQ3<%kQ~VAU#FiFUe?DvJdn24cCH^sFdygH{;mhE5|WMFMS$ zv|a{=I@#7d8ODZGn7?2gBF+#;oIDP`NxrV;iV2fbr*_$F2-L9ySLAQyoN`?JeFm|*@zv{-0aLiPb@W$PmX(M(ja?FEfu8oOu2bE|Jg;!h7G$zo z%?2xRcY%YNOsYYfLQh7cu2a^sYPEULVrR`}12U>4k)$ArG*k#$BFr>9J^<;<*dS*y z&S&UM2=OoaI-$T^>FZ>QWVWoC)^%LIl*=YhNUv38G+9kX>-x_!%#;y8{h!d8yTTlB z1{DIhoq9<`e3ZhE5ZjV`!+`^g>fU?Ko<4BhfoqQ)*}Zf7)-|gZ49^=JnA6dgsjsUg zwMS07&0;np0b)P1pVFJtq4;w?EC2sX2#ctb*2z-hll4vho?N@dI7Nmmv%0iQ;l6Oo z(n#EO)M(o7B3k61A*-Ths`Z)={pYxR8GZee2rR7SE*CxSTDO0a==nSpMHN+z*SLA~ zm2$!4yE1uEUGyXVA)<>i{Ok)P6}|~cdDt|RR%m$$<$lu4JIzw7+S(i>{N@AL$$ zuZ1(sHS4(GoeS3=MqaJKl}KxymZW6Ua|ZsUiu%If=)rPj1>tB&@9KV|V6`L;-gI#A z?xltQ84Q@Mxpd+5<^^$o#kQSCnfF;e;ejSuv^b3+(Oelh8j96HJMr7KRI*J9PS^?f=1Q&P&XV$8Cot7tA+mco%tyLKeB_o<-CX;LlL#NDWwG(jz zCEidLdN9&sN{Ih+_|wPMfVF81il+3LN6rs@wn0VAk`bxvwKQ+&)>oP=a3sd4PM+Aa zbHlpTE0G+tU}$cCUu#Rcrm7+ouvrXx!0c2h>qANWAg)p+5RG_d5b-Li(UN2$lHNi5 z+Dh1>R*1sfB}Pjz&>tX{^QU7JiE%3^h0rWfk`WML7P;EPwUS1yV|99i%V`~~%A~Td zwIv@pv#KH@s`;qXnz1DgR?pK&X4xiLn6=$omvkEg|NPBmU*A;o62Z7v!wt$$)e zDaLW4!D+JA8X}Gc4Gi1^d$*sj;P#*|uQye-nT!TUz>~{HGs!Pk_qCqA&m>ENer2?~ zqH3TsX$m-z8K9AS(>G>j9F1zTzDNG8@5aTCTmc;=P6Lwx_~;DUDg`m&k8ZyCn>)f_ z10*K)Q^@^a@h_=vQr!U`->qjlSma8?iw+5JcQ_4_2`&uRGu+XWj7aiQ$ib@sj|72u zn2;rHM79QIWH^F^2^z4bfkY?C4IoAbwj;$jEtz;jhno0b;oVlFAHvtj&jDjdCP*Zk zPtUzzccy|p`e=~jL-PxdTwfKKok;Xsud(@-$ieDRkKSmrh$h~uW=?8YogA>+!u_mN ze^{T^ioL8}AF8WAQ20F}uU~#7oe1b$CXFK@>tha$!P2GEH3dJKvvND*Hd-uj967bJ z7THO##c+}f3bV`sv9ETe&(oLUEan!g#r;;{5{txB zzrD#8gxD9)-*5I~e3|6tNwsSq$G7 z86rV(80RE4NQvJ5cTk;GHPw#foJ(Ds8V}BIzK;n$ebd%Qm;buMx#y`(%gz&2_6BT4 zR4y{l%TRgO1Keh9b!9_`@5iei+?)0{ZaMJ19l!Wd*3K{SuK)U?&w;9UZAVrQ^*8dr zhWu<&9S5GS%2~(pw4}ZeBm}yG^08>(6@3K8X#{HL+PQII!roXrC!d1R%0^B9KdHS8@I@lLho7#}Vl45EOctDK~|I33)2mv&;k$DJ|9oV?i+ z8FmqmSm!KG>$18|j62WDm6ZOrI`s~r++X6b1|8Pj+`fiRhd-(;YLCSc)aqr;xZ7kh z1WY8Ynd+X3uYNn$*lHzf1d}K^fr~p#)=<6@^Y~^XL!Iz-{NELv&{GkF5~H=)w}W9{ zH&OTRs%=w!%PlusgY4)xO5xU9jxR_eRU~y_vM2e{`z4KzYWv0Zf)#M0Jcjy|;;M6?hXWN(iO51~ z-4t2NC;2SY1P7+9o&58^1LP9lD!NSI>nI~B{=Kqv8Va9V3?)};p~tzI_#{e@`s|?Z zKek|;@R~Z;)D-FN-69L3X*XB!No*#J%5Y#Dh?dW_GwsbK3T2mH-FfN#5`_|+B{{G^ zSDyo2<`Ct-8qu+x3wzsYB$B&0c|Sfw0t%4}Fj6PwC&e%4V2UzX1UHC#3PD)e3uU0c zJd^2@QkX{d6Ws0UFJ|KYp5`L`Pc=~r&u~wTv$rdyIgO{R&>SaTJt@Pfx7;#WHaFf< znJ%%363Q2IYQg#_u5&M|_o?E@fD@l?uXpGsct2z(sQoe~NO(Uuc~L-2G@|T&D*s^j zRL9a$S9E=!XckK=9ddRgECIQ&sO(+6%MUR)dz=aKr@c+GDP(eSg4e$EdC!tXZXzoymb~ z7Ia4JYFm2x=Hrj9Nz9q;%UreU@IQX*mi4{is^!WBzb{)0yoJ##lulzwq`~|F08YEySqyBVWa|uDb22ZH4C! z7#u+oD+H&}4Ae5ME6?rTLwE6SRm-A|4G--dh}i&UA-_Hor_rN7deGj`*xJ%{3%q=2t$x5u_2H*?bTO*@ncO?^P z5+Sa%y-H0r01_Z zt{S&nG}>-ZKPF}DhR7Dt9dO%>;nQ@?(`=h?t13jsB)TW+F-U&5pyYeOZlUBCP!A5g9|TzJuK( zypFxVQ72EkEs!Au29vh~x#z_2Eb58aAAw2gC9*?S?2vcEH(Z4eWXJlDH5S~6{c@@A z@R-Sdd3{!+Yw-~)jPr|W{DW*BQOPKbUc`5ED8>^m;#d#Bh~^*@rcI#J#(K4u3fJhz zlGP>TjM~t2lh)He1-2Hd*Vi(nj-j|?gz*!9L#>+mcoJ|8ny-I!o{>txRXU)@t!pkUQbpS3_gPOnv^rg5uFhdPZE2Av?#h#73X7_H`ZONo!iTX;E z&b&OQqjzMWvuDv4Q<>~=rlGXv6RJzX9_*KJ5%tQz`{EuAukG(&+b`^yv3|yk^~JWM zuQR(;CdhH&B96O3oeQWgqd|EOF~W+8f#Bf*(0Wnu0cUga1Nj9SlP){IW&YA=eaa^D zu9oUhQ@UObiLSth2J^cov{v4^s;hqav{=F$jvM5Pit6ctlkUmr8_Y-eq95O+bZMpT zG8P{;@Nkt8TFB9Q>a&>D)~*@5 zTAF6Sr@*QOUH6^XJAI*_Svz)8^;u{(ZlyF^=r7W&H#Rg*v!!wVEdKyL68=%>r~G3m zRzyKcJB2;$#<79Pw8MWY?L1kc8_*Lr2{?kg3pQdslW@fO8FA#q?2bN)B6p-3n+S@4 z*A_OiE63KVynw%%@N_eCH0P>xsY(LiYS!9Ff?>)}BkPXKYo>x7tl*E3dD6hYV9TwhES&=&{J{@qF)Px_E{Dp8|6lI^o zNi{n_zvPomCSG=o$XZ=oavlpt`|EAq!^4#}kI}x=DC%P5$GC7I@l&H@aOpiqyg`RM zWJx+B7I*A*hmrmiCi7N5&I}9Jfg?c=Aac?QmLNdp>~Fuk++PMWYP#ZQwv8 zR97Q>9p7V8yEOF)Jx0Eu_R;SI_zT9MK2fjPWfM$k)*;JUqjW>*L#ILMvV%<*{X-J( zR^6?Bkn&pzh4|Pr5Fo)eQXT|w!4Qy!!)B==;ItW01<@Ux9tvb1>dd6sAxmmCfeVtI znIH1+(=kjGF5{>M93yHLYD#x4|4l%w4;y_Q|KisAjlAfYWme11Hog05uU?CdD~;_tBxblf(oNw^!}zPAcyO z#ld3ehswlg9eF$M(GS7|NhwM>)`2=s7 ztW(|1{6uSs`3n6yi-%us)JC&Lt+R{vyTJDGkEraJ3re>rUn#|RO8O4)=7PMsrERUO zb{V{Pu3LM(*Wlw%#Hy=fCU1SmnVUN6ePj;1xZg1Y{Ldj1p_fm>Ijm;a^i*|kZ*^)q zzgqcwI_Ax)TDWJJbNq)WsIwDYOQyOObfeO@ouv%^i164o8}{ZqH?2tHny>nS2>;=N zY59#S+NU*Cwl&65nYu~)(L8m9hLhHCfu^bjqu-fP15X= zEBZl;9&+F!b)r#xCxt0o)DE}7=l4rmYP+`$&1|dFx-4a>Sa71-=X6@!ZCR44Rq~-z zufbp7iZmhJO>NcNCejm$yWO&f+n1i^%TMvP6I=`;mcJZu?o}Pd{%sq^iLl_&4FM7+ zEm8XC3Qwy)zekeWp?6w5azKiz5to)2#HBqB>%i2ww1dQ@4^70S*L83G%IJpA>Xqd7 zusCT`r#7+(OD8(b27}Y@ZTbvh>1}u3{CQlGpdHkouwP(bRd64*EmxD1d{r?}0P0FM zt)6Q|h_+Z)qKb_Dp1nb7i~GP5A}#_!TVg<=Z948kzL(M#zGT(d@6`tNGpam%$vC$C z*|{@&yDS{_CEHZQ!AG*0OM217F#zzek*G(<_-pXCiMLbjXZ~p6gI{gGe`DTVvo>7g ztz6K*BpJTdalrE}IXHPY#V<(dow^5h6yt9E4-MGVanNq0SXm(*!ps+M3!go|#Rxj_odplgl&ntP7h124O9(XS(GERxbh z3;}yUwMm-bL3kof@PJ^z9#X2C)HKT;Z>~tk9lkw-2%VbD#=J(f#Qo1XbS4&#WOb}> z|I$SVnp0tCWTX|bg}L2}4zhRq+XOLaMxuyW?}&`<3%m4ADP%P?*|fSQIp}Xn+`E#w zbJ%XAaRs{pZ7vP$UP{yotU(z26}N|5t7^ji_)wGH~N(m1FD&pGO)|ydW~Z6Z6}Q`L$OP+{V!w#kdR5x8(jMahIt%i#Di`(1JcS zF-@IjA$l5N9AE%o&id9RjbUASdarBgcr>h-5>T~p@BJe?{wf^1+hj6l-^!)FedDC0 zv#R+>Iy2ZLt@gTDWyaOqJ`%9sq;|F}KDcw*=)tRGTX>?jW^#_e{w0~d)H7CF+*Dcr zVtSzUwx`ST%tk8pZoA85lQiPKP(@qL+rF}USG>2rb1vY+s#cEf(I@%0AXjnz^wYyL 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 literal 231904 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 T4s-SWr@ZM*DnIDIeBu8Aczgq4 From 1f5bc229e0ef5c7e5d72b1f721bc0eb7c5e90e75 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Tue, 14 Aug 2012 18:04:58 -0400 Subject: [PATCH 418/688] Separate adjustRigidBody function --- apps/openmw/mwworld/physicssystem.cpp | 24 ++++++++++--------- apps/openmw/mwworld/physicssystem.hpp | 6 ++--- apps/openmw/mwworld/worldimp.cpp | 6 ++--- components/nifbullet/bullet_nif_loader.cpp | 2 +- libs/openengine/bullet/physic.cpp | 28 +++++++++++++--------- libs/openengine/bullet/physic.hpp | 2 ++ libs/openengine/bullet/trace.cpp | 5 +++- 7 files changed, 43 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 3bdf76bcd..faacc5b06 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -193,8 +193,8 @@ namespace MWWorld Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); + //playerphysics->ps.snappingImplemented = false; - playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); @@ -280,19 +280,20 @@ namespace MWWorld mEngine->deleteRigidBody(handle); } - void PhysicsSystem::moveObject (const std::string& handle, const Ogre::Vector3& position) + void PhysicsSystem::moveObject (const std::string& handle, Ogre::SceneNode* node) { if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) { // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow // start positions others than 0, 0, 0 btTransform tr = body->getWorldTransform(); + Ogre::Vector3 position = node->getPosition(); tr.setOrigin(btVector3(position.x,position.y,position.z)); body->setWorldTransform(tr); } if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow + /*// TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow // start positions others than 0, 0, 0 if (handle == "player") { @@ -301,31 +302,32 @@ namespace MWWorld else { act->setPosition(btVector3(position.x,position.y,position.z)); - } + }*/ } } - void PhysicsSystem::rotateObject (const std::string& handle, const Ogre::Quaternion& rotation) + void PhysicsSystem::rotateObject (const std::string& handle, Ogre::SceneNode* node) { - if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + /*if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); - } + }*/ if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) { + Ogre::Quaternion rotation = node->getOrientation(); body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); } } - void PhysicsSystem::scaleObject (const std::string& handle, float scale) + void PhysicsSystem::scaleObject (const std::string& handle, Ogre::SceneNode* node) { if(handleToMesh.find(handle) != handleToMesh.end()) { - btTransform transform = mEngine->getRigidBody(handle)->getWorldTransform(); removeObject(handle); - Ogre::Quaternion quat = Ogre::Quaternion(transform.getRotation().getW(), transform.getRotation().getX(), transform.getRotation().getY(), transform.getRotation().getZ()); - Ogre::Vector3 vec = Ogre::Vector3(transform.getOrigin().getX(), transform.getOrigin().getY(), transform.getOrigin().getZ()); + float scale = node->getScale().x; + Ogre::Quaternion quat = node->getOrientation(); + Ogre::Vector3 vec = node->getPosition(); addObject(handle, handleToMesh[handle], quat, scale, vec); } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index e42fa536b..4ed8d59f6 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -34,11 +34,11 @@ namespace MWWorld void removeObject (const std::string& handle); - void moveObject (const std::string& handle, const Ogre::Vector3& position); + void moveObject (const std::string& handle, Ogre::SceneNode* node); - void rotateObject (const std::string& handle, const Ogre::Quaternion& rotation); + void rotateObject (const std::string& handle, Ogre::SceneNode* node); - void scaleObject (const std::string& handle, float scale); + void scaleObject (const std::string& handle, Ogre::SceneNode* node); bool toggleCollisionMode(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8fb7b1ba7..d5046db2d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -585,7 +585,7 @@ namespace MWWorld { moveObjectImp(ptr, x, y, z); - mPhysics->moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z)); + mPhysics->moveObject (ptr.getRefData().getHandle(), ptr.getRefData().getBaseNode()); } void World::scaleObject (const Ptr& ptr, float scale) @@ -595,7 +595,7 @@ namespace MWWorld ptr.getCellRef().scale = scale; //scale = scale/ptr.getRefData().getBaseNode()->getScale().x; ptr.getRefData().getBaseNode()->setScale(scale,scale,scale); - mPhysics->scaleObject( ptr.getRefData().getHandle(), scale ); + mPhysics->scaleObject( ptr.getRefData().getHandle(), ptr.getRefData().getBaseNode()); } void World::rotateObject (const Ptr& ptr,float x,float y,float z) @@ -610,7 +610,7 @@ namespace MWWorld Ogre::Quaternion roty(Ogre::Degree(-y),Ogre::Vector3::UNIT_Y); Ogre::Quaternion rotz(Ogre::Degree(-z),Ogre::Vector3::UNIT_Z); ptr.getRefData().getBaseNode()->setOrientation(rotx*roty*rotz); - mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()->getOrientation()); + mPhysics->rotateObject(ptr.getRefData().getHandle(),ptr.getRefData().getBaseNode()); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 7af56d04c..fc7fb5ec2 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -129,7 +129,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) }; cShape->boxTranslation = boxTranslation; if(mBoundingBox != NULL) - cShape->Shape = mBoundingBox; + cShape->Shape = mBoundingBox; else { diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 9d0a290e9..af73a42a6 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -332,6 +332,20 @@ namespace Physic mHeightFieldMap.erase(name); } + void PhysicEngine::adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation){ + btTransform tr; + btBoxShape* box = dynamic_cast(body->getCollisionShape()); + if(box != NULL){ + Ogre::Vector3 transrot = rotation * (shape->boxTranslation * scale); + Ogre::Vector3 newPosition = transrot + position; + tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); + } + else + tr.setOrigin(btVector3(position.x,position.y,position.z)); + tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); + body->setWorldTransform(tr); + } + RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation) { std::string sid = (boost::format("%07.3f") % scale).str(); @@ -355,17 +369,9 @@ namespace Physic RigidBody* body = new RigidBody(CI,name); body->collide = shape->collide; - btTransform tr; - btBoxShape* box = dynamic_cast(body->getCollisionShape()); - if(box != NULL){ - Ogre::Vector3 transrot = rotation * (shape->boxTranslation * scale); - Ogre::Vector3 newPosition = transrot + position; - tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); - } - else - tr.setOrigin(btVector3(position.x,position.y,position.z)); - tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); - body->setWorldTransform(tr); + //Pass in BulletShape, RigidBody, scale, position, rotation + + adjustRigidBody(shape, body, scale, position, rotation); return body; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 42f648766..fc52ad127 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -148,6 +148,8 @@ namespace Physic */ RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); + void adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); + /** * Add a HeightField to the simulation */ diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 6d2166baa..2329ed652 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -21,6 +21,7 @@ void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogr //if (!traceobj->incellptr) // return; + const Ogre::Vector3 rayDir = end - start; @@ -34,6 +35,7 @@ void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogr //Ogre::Vector3 startReplace = Ogre::Vector3(650,950, 45); //Ogre::Vector3 endReplace = startReplace; //endReplace.z -= .25; + const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0f,0.0f), isInterior, enginePass); @@ -104,7 +106,8 @@ const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& const btVector3 btend(end.x, end.y, end.z); const btQuaternion btrot(rotation.y, rotation.x, rotation.z); //y, x, z - const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); + const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); + //const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2); const btTransform from(btrot, btstart); const btTransform to(btrot, btend); From 7b8b4c366d5de40e148b780b86e409544975385d Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Tue, 14 Aug 2012 20:29:48 -0400 Subject: [PATCH 419/688] Set angle now working for npcs and creatures --- apps/openmw/mwclass/npc.cpp | 3 +-- apps/openmw/mwworld/physicssystem.cpp | 16 ++++++++++++---- libs/openengine/bullet/physic.cpp | 12 ++++++++++++ libs/openengine/bullet/physic.hpp | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 72501e8ac..217d92025 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -379,8 +379,7 @@ namespace MWClass void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const { - y = 0; - x = 0; + } MWWorld::Ptr diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index faacc5b06..3d4a08c69 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -286,10 +286,15 @@ namespace MWWorld { // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow // start positions others than 0, 0, 0 - btTransform tr = body->getWorldTransform(); Ogre::Vector3 position = node->getPosition(); - tr.setOrigin(btVector3(position.x,position.y,position.z)); - body->setWorldTransform(tr); + + if(dynamic_cast(body->getCollisionShape()) == NULL){ + btTransform tr = body->getWorldTransform(); + tr.setOrigin(btVector3(position.x,position.y,position.z)); + body->setWorldTransform(tr); + } + else + mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, position, node->getOrientation()); } if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { @@ -315,7 +320,10 @@ namespace MWWorld if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) { Ogre::Quaternion rotation = node->getOrientation(); - body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); + if(dynamic_cast(body->getCollisionShape()) == NULL) + body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); + else + mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, node->getPosition(), rotation); } } diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index af73a42a6..2f0624470 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -345,6 +345,18 @@ namespace Physic tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); body->setWorldTransform(tr); } + void PhysicEngine::boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation){ + std::string sid = (boost::format("%07.3f") % scale).str(); + std::string outputstring = mesh + sid; + //std::cout << "The string" << outputstring << "\n"; + + //get the shape from the .nif + mShapeLoader->load(outputstring,"General"); + BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); + BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); + + adjustRigidBody(shape, body, scale, position, rotation); + } RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation) { diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index fc52ad127..088bbc465 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -149,7 +149,7 @@ namespace Physic RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); void adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); - + void boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); /** * Add a HeightField to the simulation */ From 4ae0a5c02e1b4721da6041b8c00e2633942320c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Aug 2012 19:29:00 +0200 Subject: [PATCH 420/688] fix silhouettes around objects above the water --- files/materials/water.shader | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/files/materials/water.shader b/files/materials/water.shader index 08a19ace9..8f46aed34 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -118,7 +118,6 @@ #define WAVE_CHOPPYNESS 0.15 // wave choppyness #define WAVE_SCALE 75 // overall wave scale - #define ABBERATION 0.001 // chromatic abberation amount #define BUMP 1.5 // overall water surface bumpiness #define REFL_BUMP 0.08 // reflection distortion amount #define REFR_BUMP 0.06 // refraction distortion amount @@ -256,11 +255,15 @@ // refraction float3 R = reflect(vVec, normal); + + // check the depth at the refracted coords, and don't do any normal distortion for the refraction if the object to refract + // is actually above the water (objectDepth < waterDepth) + // this solves silhouettes around objects above the water + float refractDepth = shSample(depthMap, screenCoords-(shoreFade * normal.xz*REFR_BUMP)).x * far - depthPassthrough; + float doRefraction = (refractDepth < 0) ? 0.f : 1.f; float3 refraction = float3(0,0,0); - refraction.r = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0).r; - refraction.g = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION)).g; - refraction.b = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP))*1.0-(R.xy*ABBERATION*2.0)).b; + refraction.rgb = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP * doRefraction))*1.0).rgb; // brighten up the refraction underwater refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; From 8762f4a47abfd873acc036e70f24859105d0a6f7 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 15 Aug 2012 16:47:26 -0400 Subject: [PATCH 421/688] boxrotation nif field; commented functions --- apps/openmw/mwworld/physicssystem.cpp | 2 +- components/nifbullet/bullet_nif_loader.cpp | 6 +++--- components/nifbullet/bullet_nif_loader.hpp | 2 +- libs/openengine/bullet/BulletShapeLoader.h | 1 + libs/openengine/bullet/physic.cpp | 3 ++- libs/openengine/bullet/physic.hpp | 9 +++++++++ libs/openengine/bullet/pmove.cpp | 5 ++++- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 3d4a08c69..d1e39c8b7 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -194,7 +194,7 @@ namespace MWWorld //playerphysics->ps.snappingImplemented = false; - + //playerphysics->ps.speed = 240; playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index fc7fb5ec2..6fec6241f 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -73,7 +73,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) resourceName = cShape->getName(); cShape->collide = false; mBoundingBox = NULL; - boxTranslation = Ogre::Vector3(0,0,0); + cShape->boxTranslation = Ogre::Vector3(0,0,0); mTriMesh = new btTriangleMesh(); @@ -127,7 +127,6 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) delete m_meshInterface; } }; - cShape->boxTranslation = boxTranslation; if(mBoundingBox != NULL) cShape->Shape = mBoundingBox; @@ -226,7 +225,8 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, btVector3 boxsize = getbtVector((node->boundXYZ)); - boxTranslation = node->boundPos; + cShape->boxTranslation = node->boundPos; + cShape->boxRotation = node->boundRot; mBoundingBox = new btBoxShape(boxsize); } diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index beb4274b0..82ac227a0 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -102,7 +102,7 @@ private: std::string resourceName; std::string resourceGroup; - Ogre::Vector3 boxTranslation; + BulletShape* cShape;//current shape btTriangleMesh *mTriMesh; diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index c09f0dc7e..8640fd54f 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -32,6 +32,7 @@ public: btCollisionShape* Shape; Ogre::Vector3 boxTranslation; + Ogre::Quaternion boxRotation; //this flag indicate if the shape is used for collision or if it's for raycasting only. bool collide; }; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 2f0624470..0845864ef 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -336,9 +336,10 @@ namespace Physic btTransform tr; btBoxShape* box = dynamic_cast(body->getCollisionShape()); if(box != NULL){ - Ogre::Vector3 transrot = rotation * (shape->boxTranslation * scale); + Ogre::Vector3 transrot = rotation * shape->boxRotation * (shape->boxTranslation * scale); Ogre::Vector3 newPosition = transrot + position; tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); + rotation = rotation * shape->boxRotation; } else tr.setOrigin(btVector3(position.x,position.y,position.z)); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 088bbc465..403af6c6c 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -145,10 +145,19 @@ namespace Physic /** * Create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map, * so you can get it with the getRigidBody function. + + After created, the body is set to the correct rotation, position, and scale */ RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); + /** + * Adjusts a rigid body to the right position and rotation + */ + void adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); + /** + Mainly used to (but not limited to) adjust rigid bodies based on box shapes to the right position and rotation. + */ void boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); /** * Add a HeightField to the simulation diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 4c70971a3..32a11179f 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -657,10 +657,11 @@ static void PM_Accelerate( Ogre::Vector3& wishdir, float wishspeed, float accel // int i; float addspeed, accelspeed, currentspeed; + // currentspeed = pm->ps->velocity dot wishdir //currentspeed = DotProduct (pm->ps->velocity, wishdir); currentspeed = pm->ps.velocity.dotProduct(wishdir); - + addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; @@ -675,6 +676,8 @@ static void PM_Accelerate( Ogre::Vector3& wishdir, float wishspeed, float accel //for (i=0 ; i<3 ; i++) //pm->ps->velocity[i] += accelspeed * wishdir[i]; pm->ps.velocity += (wishdir * accelspeed); + //pm->ps.velocity = wishdir * wishspeed; //New, for instant acceleration + } static bool PM_CheckJump(void) From b0506e222092c62c63b1cb7543414b7e1c907e21 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Aug 2012 12:59:28 +0200 Subject: [PATCH 422/688] re-enabling support for multiple data directories --- apps/openmw/main.cpp | 10 +--------- files/openmw.cfg | 2 ++ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 0ca9dd6d9..5c2ba2f80 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -207,15 +207,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat std::string local(variables["data-local"].as()); if (!local.empty()) { - std::cout << "Ignoring data-local (currently not supported)" << std::endl; -// dataDirs.push_back(Files::PathContainer::value_type(local)); - } - - if (dataDirs.size()>1) - { - dataDirs.resize (1); - std::cout << "Ignoring all but the first data path (multiple data paths currently not supported)" - << std::endl; + dataDirs.push_back(Files::PathContainer::value_type(local)); } cfgMgr.processPaths(dataDirs); diff --git a/files/openmw.cfg b/files/openmw.cfg index 0af096e6a..c79369d05 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,2 +1,4 @@ +data="?global?"data" data="?mw?Data Files" +data-local="?local?data" resources=${MORROWIND_RESOURCE_FILES} From e97d23e626ca688ed503c31d410cf5543410b762 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Aug 2012 15:42:37 +0200 Subject: [PATCH 423/688] Issue #361: reset skill level in race selection stage --- .../mwmechanics/mechanicsmanagerimp.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7fd0b29c3..2ac146c2f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -66,15 +66,18 @@ namespace MWMechanics static_cast (male ? attribute->male : attribute->female)); } - for (int i=0; i<7; ++i) + for (int i=0; i<27; ++i) { - int index = race->data.bonus[i].skill; - - if (index>=0 && index<27) - { - npcStats.getSkill (index).setBase ( - npcStats.getSkill (index).getBase() + race->data.bonus[i].bonus); - } + int bonus = 0; + + for (int i2=0; i2<7; ++i2) + if (race->data.bonus[i2].skill==i) + { + bonus = race->data.bonus[i2].bonus; + break; + } + + npcStats.getSkill (i).setBase (5 + bonus); } for (std::vector::const_iterator iter (race->powers.list.begin()); From ebe131b3268b7d4a301e010234639763caab6eec Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Tue, 14 Aug 2012 14:45:16 -0400 Subject: [PATCH 424/688] Use debug dlls when debugging in vs2010 Using the Debug build in vs2010 is not working because the debug dlls are not loaded when debugging. The reason they are not loaded is that CMAKE_BUILD_TYPE is not defined when doing multiple builds. This in turns causes OGRE_PLUGIN_DEBUG_SUFFIX not to be set. This patch makes sure that OGRE_PLUGIN_DEBUG_SUFFIX is always set but only used when debugging. There are still other bugs that have broken Debug mode in vs2010 but those will be addressed in other patches. --- CMakeLists.txt | 8 ++++---- components/files/ogreplugin.cpp | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb98..e3cc8df3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,11 +259,11 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix -# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) -if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") -else() +if (APPLE) + # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") +else () + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index c434114b3..85fe661de 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,7 +6,11 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { + // Append plugin suffix if debugging. +#if defined(DEBUG) || defined(_DEBUG) pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; +#endif + #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; From d57698501905a8e2dc987343de6d38f199a27991 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Thu, 16 Aug 2012 20:52:08 +0200 Subject: [PATCH 425/688] credits.txt: fix name --- credits.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credits.txt b/credits.txt index ca0ff7323..c7ec4936e 100644 --- a/credits.txt +++ b/credits.txt @@ -35,7 +35,7 @@ Sylvain T. (Garvek) Packagers: Alexander Olofsson (Ace) - Windows BrotherBrick - Ubuntu Linux -edmundo - Gentoo Linux +Edmondo Tommasina - Gentoo Linux Kenny Armstrong (artorius) - Fedora Linux Nikolay Kasyanov (corristo) - Mac OS X Sandy Carter (bwrsandman) - Arch Linux From 63a40e9ba320b4ca44413c72d4aabcafe482b465 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Thu, 16 Aug 2012 15:41:42 -0400 Subject: [PATCH 426/688] npc xy disabled --- apps/openmw/mwclass/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 217d92025..72501e8ac 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -379,7 +379,8 @@ namespace MWClass void Npc::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const { - + y = 0; + x = 0; } MWWorld::Ptr From 2a11a28e81e4a9bded90f5132a6b837b068a8701 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 10:05:29 +0200 Subject: [PATCH 427/688] Revert "Use debug dlls when debugging in vs2010" This reverts commit ebe131b3268b7d4a301e010234639763caab6eec. --- CMakeLists.txt | 8 ++++---- components/files/ogreplugin.cpp | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3cc8df3b..543d9cb98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,11 +259,11 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix -if (APPLE) - # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") -else () +# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) +if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") +else() + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index 85fe661de..c434114b3 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,11 +6,7 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { - // Append plugin suffix if debugging. -#if defined(DEBUG) || defined(_DEBUG) pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; -#endif - #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; From c46eeaa100687d9af473c83a638df1dd87966fda Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 12 Aug 2012 15:50:37 +0400 Subject: [PATCH 428/688] initial 3d-person camera support --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 31 +++- apps/openmw/mwinput/inputmanagerimp.hpp | 2 +- apps/openmw/mwrender/player.cpp | 172 +++++++++++++++------- apps/openmw/mwrender/player.hpp | 32 ++-- apps/openmw/mwrender/renderingmanager.cpp | 14 +- apps/openmw/mwrender/renderingmanager.hpp | 4 + apps/openmw/mwworld/worldimp.cpp | 1 - apps/openmw/mwworld/worldimp.hpp | 3 + 10 files changed, 184 insertions(+), 79 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 75835120f..b8421cc62 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -67,7 +67,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.setFrameDuration (evt.timeSinceLastFrame); // update input - MWBase::Environment::get().getInputManager()->update(); + MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame); // sound if (mUseSound) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8b809d399..c2db2acf1 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -248,6 +248,8 @@ namespace MWBase virtual bool isSwimming(const MWWorld::Ptr &object) = 0; virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; + + virtual void togglePOV() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8def5c74d..6bdd87d2a 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -62,6 +62,7 @@ namespace MWInput A_ToggleSneak, //Toggles Sneak, add Push-Sneak later A_ToggleWalk, //Toggle Walking/Running A_Crouch, + A_TogglePOV, A_QuickSave, A_QuickLoad, @@ -90,6 +91,8 @@ namespace MWInput std::map mControlSwitch; + float mPreviewPOVDelay; + /* InputImpl Methods */ public: void adjustMouseRegion(int width, int height) @@ -340,6 +343,8 @@ private: poller.bind(A_Jump, KC_E); poller.bind(A_Crouch, KC_LCONTROL); + + poller.bind(A_TogglePOV, KC_TAB); } void setDragDrop(bool dragDrop) @@ -348,7 +353,7 @@ private: } //NOTE: Used to check for movement keys - void update () + void update (float duration) { // Tell OIS to handle all input events input.capture(); @@ -400,6 +405,21 @@ private: player.setUpDown (-1); else player.setUpDown (0); + + if (poller.isDown(A_TogglePOV)) { + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += duration) > 0.5) + { + // enable preview mode + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + } else if (mPreviewPOVDelay > 0.f) { + togglePOV(); + } + mPreviewPOVDelay = 0.f; + } } } @@ -452,6 +472,11 @@ private: mControlSwitch[sw] = value; } + void togglePOV() + { + MWBase::Environment::get().getWorld()->togglePOV(); + } + }; /***CONSTRUCTOR***/ @@ -470,9 +495,9 @@ private: delete impl; } - void MWInputManager::update() + void MWInputManager::update(float duration) { - impl->update(); + impl->update(duration); } void MWInputManager::setDragDrop(bool dragDrop) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 5092198da..70436e207 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -52,7 +52,7 @@ namespace MWInput OMW::Engine& engine); virtual ~MWInputManager(); - virtual void update(); + void update(float duration); virtual void changeInputMode(bool guiMode); diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 94a3f71c8..a99d69945 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -12,82 +13,119 @@ namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) - : mCamera (camera), - mNode (node), + : mCamera(camera), + mPlayerNode(node), + mCameraNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), - mVanityModeEnabled(false) - {} - - bool Player::setRotation(const Ogre::Vector3 &rot) + mVanityMode(false), + mPreviewMode(false) { - Ogre::SceneNode *sceneNode = mNode; - Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); - Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); + Ogre::SceneNode *pitchNode = mCameraNode->createChildSceneNode(); + pitchNode->attachObject(mCamera); + } + + bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) + { + bool force = !mVanityMode && !mPreviewMode; + Ogre::Vector3 newRot = rot; - // we are only interested in X and Y rotation + rotateCamera(newRot, adjust); - // Rotate around X axis - Ogre::Radian radx(rot.x); - if (radx.valueDegrees() > 89.5f) { - radx = Ogre::Degree(89.5f); - } else if (radx.valueDegrees() < -89.5f) { - radx = Ogre::Degree(-89.5f); + if (!force || !mFirstPersonView) { + moveCamera(400.f, 1600.f); } - Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); - - // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); - - pitchNode->setOrientation(xr); - yawNode->setOrientation(yr); - updateListener(); - return !mVanityModeEnabled; + return force; } - std::string Player::getHandle() const - { - return mNode->getName(); - } - - void Player::attachTo(const MWWorld::Ptr &ptr) - { - ptr.getRefData().setBaseNode(mNode); - } - - bool Player::adjustRotation(const Ogre::Vector3 &rot) + void Player::rotateCamera(Ogre::Vector3 &rot, bool adjust) { Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - float f = controlFlip(Ogre::Radian(rot.x).valueDegrees()); - if (f != 0.0) { - pitchNode->pitch(Ogre::Degree(f)); + if (adjust) { + float f = + limitPitchAngle(89.5f, Ogre::Radian(rot.x).valueDegrees()); + + if (f != 0.0) { + pitchNode->pitch(Ogre::Degree(f)); + } + yawNode->yaw(Ogre::Radian(-rot.z)); + } else { + Ogre::Radian radx(rot.x); + if (radx.valueDegrees() > 89.5f) { + radx = Ogre::Degree(89.5f); + } else if (radx.valueDegrees() < -89.5f) { + radx = Ogre::Degree(-89.5f); + } + Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); + + pitchNode->setOrientation(xr); + yawNode->setOrientation(yr); } - yawNode->yaw(Ogre::Radian(-rot.z)); - - updateListener(); - - return !mVanityModeEnabled; } - float Player::controlFlip(float shift) + void Player::moveCamera(float rsq, float hsq) { - Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); - Ogre::Quaternion orient = pitchNode->getOrientation(); +/* + Ogre::Quaternion orient = + mCamera->getParentSceneNode()->getOrientation(); float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - if (pitchAngle + shift < 89.5f && pitchAngle + shift > -89.5f) { + orient = mCameraNode->getOrientation(); + float yawAngle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); + + float tana = Ogre::Math::Tan(Ogre::Degree(pitchAngle)); + float tansq = tana * tana; + + float r1 = hsq * rsq / (hsq + rsq * tansq); + float zsq = r1 * tansq; + r1 = Ogre::Math::Sqrt(r1); + + Ogre::Vector3 pos; + pos.y = -Ogre::Math::Sqrt(zsq); + pos.z = r1 * Ogre::Math::Sin(Ogre::Degree(yawAngle).valueDegrees()); + pos.x = r1 * Ogre::Math::Cos(Ogre::Degree(yawAngle).valueDegrees()); +*/ + Ogre::Vector3 dir = mCamera->getRealDirection(); + dir.x = -dir.x, dir.y = -dir.y, dir.z = -dir.z; + + Ogre::Ray ray(Ogre::Vector3(0, 0, 0), dir); + + mCameraNode->setPosition(ray.getPoint(800.f)); + } + + std::string Player::getHandle() const + { + return mPlayerNode->getName(); + } + + void Player::attachTo(const MWWorld::Ptr &ptr) + { + ptr.getRefData().setBaseNode(mPlayerNode); + } + + float Player::limitPitchAngle(float limitAbs, float shift) + { + Ogre::Quaternion orient = + mCamera->getParentSceneNode()->getOrientation(); + + float pitchAngle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); + + if (pitchAngle + shift < limitAbs && pitchAngle + shift > -limitAbs) { return shift; } if (pitchAngle > 0) { - float f = 89.5f - pitchAngle - shift; + float f = limitAbs - pitchAngle - shift; return (f > 0.f) ? f : 0.f; } else if (pitchAngle < 0) { - float f = -89.5 - pitchAngle - shift; + float f = -limitAbs - pitchAngle - shift; return (f < 0.f) ? f : 0.f; } return 0.f; @@ -104,4 +142,38 @@ namespace MWRender MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir); } + + void Player::update(float duration) + { + if (mFirstPersonView && !mVanityMode) { + return; + } + if (mVanityMode) { + /// \todo adjust rotation constantly + } else { + /// \todo move camera closer or change view mode if needed + } + } + + void Player::toggleViewMode() + { + mFirstPersonView = !mFirstPersonView; + if (mFirstPersonView) { + mCameraNode->setPosition(0.f, 0.f, 0.f); + } else { + moveCamera(400.f, 1600.f); + } + } + + void Player::toggleVanityMode() + { + /// \todo move camera + mVanityMode = !mVanityMode; + } + + void Player::togglePreviewMode() + { + /// \todo move camera + mPreviewMode = !mPreviewMode; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 981ecfe0b..739b4cd8b 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -22,16 +22,24 @@ namespace MWRender class Player { Ogre::Camera *mCamera; - Ogre::SceneNode* mNode; + + Ogre::SceneNode *mPlayerNode; + Ogre::SceneNode *mCameraNode; bool mFirstPersonView; - bool mVanityModeEnabled; + bool mVanityMode; + bool mPreviewMode; - float controlFlip(float shift = 0.f); + float mTimeIdle; + + float limitPitchAngle(float limitAbs, float shift = 0.f); /// Updates sound manager listener data void updateListener(); + void rotateCamera(Ogre::Vector3 &rot, bool adjust); + void moveCamera(float r, float h); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); @@ -39,11 +47,7 @@ namespace MWRender /// Set where the player is looking at. Uses Morrowind (euler) angles /// \param rot Rotation angles in radians /// \return true if player object needs to bo rotated physically - bool setRotation(const Ogre::Vector3 &rot); - - /// \param rot Rotation angles in radians - /// \return true if player object needs to bo rotated physically - bool adjustRotation(const Ogre::Vector3 &rot); + bool rotate(const Ogre::Vector3 &rot, bool adjust); std::string getHandle() const; @@ -52,13 +56,13 @@ namespace MWRender /// several different objects void attachTo(const MWWorld::Ptr &); - void toggleViewMode() { - mFirstPersonView = !mFirstPersonView; - } + void toggleViewMode(); - void toggleVanityMode() { - mVanityModeEnabled = !mVanityModeEnabled; - } + void toggleVanityMode(); + + void togglePreviewMode(); + + void update(float duration); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d0019c6b8..91d83caf3 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -133,11 +133,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mObjects.setMwRoot(mMwRoot); mActors.setMwRoot(mMwRoot); + Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); playerNode->pitch(Degree(90)); - Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode(); - Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode(); - cameraPitchNode->attachObject(mRendering.getCamera()); + mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mShadows = new Shadows(&mRendering); @@ -147,7 +146,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mSun = 0; mDebugging = new Debugging(mMwRoot, engine); @@ -259,11 +257,7 @@ RenderingManager::rotateObject( bool force = true; if (isPlayer) { - if (adjust) { - force = mPlayer->adjustRotation(rot); - } else { - force = mPlayer->setRotation(rot); - } + force = mPlayer->rotate(rot, adjust); } MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); @@ -329,6 +323,8 @@ void RenderingManager::update (float duration){ ); mWater->update(duration); } + + mPlayer->update(duration); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ef6f18a75..f100fdd59 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -56,6 +56,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine); virtual ~RenderingManager(); + void togglePOV() { + mPlayer->toggleViewMode(); + } + void attachCameraTo(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8ace54378..20706438d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1151,5 +1151,4 @@ namespace MWWorld } return pos.z < cell.water; } - } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4031a180a..2e5f73702 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -277,6 +277,9 @@ namespace MWWorld virtual bool isSwimming(const MWWorld::Ptr &object); virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); + virtual void togglePOV() { + mRendering->togglePOV(); + } }; } From a7aeda9a3b78d8939745a07fb55c7adb0f5b38cd Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 12 Aug 2012 18:35:35 +0400 Subject: [PATCH 429/688] initial vanity mode support --- apps/openmw/mwrender/player.cpp | 85 +++++++++++++++++++---- apps/openmw/mwrender/player.hpp | 12 +++- apps/openmw/mwrender/renderingmanager.cpp | 13 +++- 3 files changed, 90 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index a99d69945..53642d615 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -24,19 +24,29 @@ namespace MWRender pitchNode->attachObject(mCamera); } - bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) + void Player::rotateImpl(Ogre::Vector3 &rot, bool adjust, float r) { - bool force = !mVanityMode && !mPreviewMode; - Ogre::Vector3 newRot = rot; + rotateCamera(rot, adjust); - rotateCamera(newRot, adjust); - - if (!force || !mFirstPersonView) { - moveCamera(400.f, 1600.f); + if (mVanityMode || mPreviewMode || !mFirstPersonView) { + moveCamera(r); } updateListener(); + } - return force; + bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) + { + Ogre::Vector3 _rot = rot; + rotateImpl(_rot, adjust, 400.f); + + mUpdates = 0; + mTimeIdle = 0.f; + + if (mVanityMode) { + toggleVanityMode(); + } + + return !mVanityMode && !mPreviewMode; } void Player::rotateCamera(Ogre::Vector3 &rot, bool adjust) @@ -67,7 +77,7 @@ namespace MWRender } } - void Player::moveCamera(float rsq, float hsq) + void Player::moveCamera(float r) { /* Ogre::Quaternion orient = @@ -97,7 +107,7 @@ namespace MWRender Ogre::Ray ray(Ogre::Vector3(0, 0, 0), dir); - mCameraNode->setPosition(ray.getPoint(800.f)); + mCameraNode->setPosition(ray.getPoint(r)); } std::string Player::getHandle() const @@ -145,13 +155,20 @@ namespace MWRender void Player::update(float duration) { + if (!mVanityMode) { + ++mUpdates; + mTimeIdle += duration; + if (mTimeIdle > 30.f) { + toggleVanityMode(); + } + } if (mFirstPersonView && !mVanityMode) { return; } if (mVanityMode) { - /// \todo adjust rotation constantly - } else { - /// \todo move camera closer or change view mode if needed + Ogre::Vector3 rot(0.f, 0.f, 0.f); + rot.z = Ogre::Degree(3.f * duration).valueRadians(); + rotateImpl(rot, true, 300.f); } } @@ -161,14 +178,28 @@ namespace MWRender if (mFirstPersonView) { mCameraNode->setPosition(0.f, 0.f, 0.f); } else { - moveCamera(400.f, 1600.f); + moveCamera(400.f); } } void Player::toggleVanityMode() { - /// \todo move camera mVanityMode = !mVanityMode; + + float r = 400.f; + Ogre::Vector3 rot(0.f, 0.f, 0.f); + if (mVanityMode) { + mPitch = getPitchAngle(); + mYaw = getYawAngle(); + + rot.x = Ogre::Degree(-30.f).valueRadians(); + rot.z = 0; + r = 300.f; + } else { + rot.x = Ogre::Degree(mPitch).valueRadians(); + rot.z = Ogre::Degree(mYaw).valueRadians(); + } + rotateImpl(rot, false, r); } void Player::togglePreviewMode() @@ -176,4 +207,28 @@ namespace MWRender /// \todo move camera mPreviewMode = !mPreviewMode; } + + float Player::getPitchAngle() + { + Ogre::Quaternion orient + = mCamera->getParentSceneNode()->getOrientation(); + + float angle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); + + return angle; + } + + float Player::getYawAngle() + { + Ogre::Quaternion orient + = mCameraNode->getOrientation(); + + float angle = + (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); + if (orient.w < 0) { + angle = -angle; + } + return -angle; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 739b4cd8b..d4c0eab5d 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -3,9 +3,8 @@ #include - namespace Ogre -{ +{ class Vector3; class Camera; class SceneNode; @@ -30,15 +29,22 @@ namespace MWRender bool mVanityMode; bool mPreviewMode; + float mPitch, mYaw; + float mTimeIdle; + int mUpdates; float limitPitchAngle(float limitAbs, float shift = 0.f); /// Updates sound manager listener data void updateListener(); + void rotateImpl(Ogre::Vector3 &rot, bool adjust, float r); void rotateCamera(Ogre::Vector3 &rot, bool adjust); - void moveCamera(float r, float h); + void moveCamera(float r); + + float getYawAngle(); + float getPitchAngle(); public: diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 91d83caf3..d5e28e439 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -309,7 +309,17 @@ void RenderingManager::update (float duration){ mRendering.update(duration); - mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealOrientation() ); + float *fpos = + MWBase::Environment::get() + .getWorld() + ->getPlayer() + .getPlayer() + .getRefData() + .getPosition() + .pos; + Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + + mLocalMap->updatePlayer(pos, mRendering.getCamera()->getRealOrientation() ); if (mWater) { Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); @@ -323,7 +333,6 @@ void RenderingManager::update (float duration){ ); mWater->update(duration); } - mPlayer->update(duration); } From db94018865bef15fe5e3df7f06abbf18edceaf3a Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 13 Aug 2012 08:37:32 +0400 Subject: [PATCH 430/688] far better camera movement --- apps/openmw/mwrender/player.cpp | 66 ++++++--------------------------- apps/openmw/mwrender/player.hpp | 6 +-- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 53642d615..8753d7c5d 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -24,20 +24,9 @@ namespace MWRender pitchNode->attachObject(mCamera); } - void Player::rotateImpl(Ogre::Vector3 &rot, bool adjust, float r) - { - rotateCamera(rot, adjust); - - if (mVanityMode || mPreviewMode || !mFirstPersonView) { - moveCamera(r); - } - updateListener(); - } - bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - Ogre::Vector3 _rot = rot; - rotateImpl(_rot, adjust, 400.f); + rotateCamera(rot, adjust); mUpdates = 0; mTimeIdle = 0.f; @@ -49,7 +38,7 @@ namespace MWRender return !mVanityMode && !mPreviewMode; } - void Player::rotateCamera(Ogre::Vector3 &rot, bool adjust) + void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) { Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); @@ -75,39 +64,7 @@ namespace MWRender pitchNode->setOrientation(xr); yawNode->setOrientation(yr); } - } - - void Player::moveCamera(float r) - { -/* - Ogre::Quaternion orient = - mCamera->getParentSceneNode()->getOrientation(); - - float pitchAngle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - - orient = mCameraNode->getOrientation(); - float yawAngle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); - - float tana = Ogre::Math::Tan(Ogre::Degree(pitchAngle)); - float tansq = tana * tana; - - float r1 = hsq * rsq / (hsq + rsq * tansq); - float zsq = r1 * tansq; - r1 = Ogre::Math::Sqrt(r1); - - Ogre::Vector3 pos; - pos.y = -Ogre::Math::Sqrt(zsq); - pos.z = r1 * Ogre::Math::Sin(Ogre::Degree(yawAngle).valueDegrees()); - pos.x = r1 * Ogre::Math::Cos(Ogre::Degree(yawAngle).valueDegrees()); -*/ - Ogre::Vector3 dir = mCamera->getRealDirection(); - dir.x = -dir.x, dir.y = -dir.y, dir.z = -dir.z; - - Ogre::Ray ray(Ogre::Vector3(0, 0, 0), dir); - - mCameraNode->setPosition(ray.getPoint(r)); + updateListener(); } std::string Player::getHandle() const @@ -168,7 +125,7 @@ namespace MWRender if (mVanityMode) { Ogre::Vector3 rot(0.f, 0.f, 0.f); rot.z = Ogre::Degree(3.f * duration).valueRadians(); - rotateImpl(rot, true, 300.f); + rotateCamera(rot, true); } } @@ -176,9 +133,9 @@ namespace MWRender { mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { - mCameraNode->setPosition(0.f, 0.f, 0.f); + mCamera->setPosition(0.f, 0.f, 0.f); } else { - moveCamera(400.f); + mCamera->setPosition(0.f, 0.f, 400.f); } } @@ -186,20 +143,21 @@ namespace MWRender { mVanityMode = !mVanityMode; - float r = 400.f; + float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanityMode) { - mPitch = getPitchAngle(); mYaw = getYawAngle(); + mPitch = getPitchAngle(); + mOffset = mCamera->getPosition().z; rot.x = Ogre::Degree(-30.f).valueRadians(); - rot.z = 0; - r = 300.f; } else { rot.x = Ogre::Degree(mPitch).valueRadians(); rot.z = Ogre::Degree(mYaw).valueRadians(); + offset = mOffset; } - rotateImpl(rot, false, r); + mCamera->setPosition(0.f, 0.f, offset); + rotateCamera(rot, false); } void Player::togglePreviewMode() diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index d4c0eab5d..ed4923817 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -29,7 +29,7 @@ namespace MWRender bool mVanityMode; bool mPreviewMode; - float mPitch, mYaw; + float mPitch, mYaw, mOffset; float mTimeIdle; int mUpdates; @@ -39,9 +39,7 @@ namespace MWRender /// Updates sound manager listener data void updateListener(); - void rotateImpl(Ogre::Vector3 &rot, bool adjust, float r); - void rotateCamera(Ogre::Vector3 &rot, bool adjust); - void moveCamera(float r); + void rotateCamera(const Ogre::Vector3 &rot, bool adjust); float getYawAngle(); float getPitchAngle(); From fe1a9ac3c575306ec2ef8a5ab866792d228f61e5 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 02:36:18 +0400 Subject: [PATCH 431/688] poor camera with some fixes --- apps/openmw/mwinput/mouselookevent.cpp | 2 +- apps/openmw/mwrender/localmap.cpp | 2 +- apps/openmw/mwrender/player.cpp | 163 +++++++++++----------- apps/openmw/mwrender/player.hpp | 21 ++- apps/openmw/mwrender/renderingmanager.cpp | 19 ++- apps/openmw/mwworld/physicssystem.cpp | 42 +++--- 6 files changed, 129 insertions(+), 120 deletions(-) diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index f318ce666..4138c481c 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -24,5 +24,5 @@ void MouseLookEvent::event(Type type, int index, const void *p) float y = arg->state.Y.rel * sensY; MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 704a10cfe..0e85d32e0 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -290,7 +290,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } - Vector3 playerdirection = -mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); + Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8753d7c5d..5b74b9903 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -16,12 +16,16 @@ namespace MWRender : mCamera(camera), mPlayerNode(node), mCameraNode(mPlayerNode->createChildSceneNode()), + mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mVanityMode(false), - mPreviewMode(false) + mPreviewMode(false), + mHeight(40.f) { - Ogre::SceneNode *pitchNode = mCameraNode->createChildSceneNode(); - pitchNode->attachObject(mCamera); + mCameraNode->attachObject(mCamera); + mCameraNode->setPosition(0.f, 0.f, mHeight); + + mPreviewCam.yaw = 0.f; } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) @@ -32,7 +36,7 @@ namespace MWRender mTimeIdle = 0.f; if (mVanityMode) { - toggleVanityMode(); + toggleVanityMode(false); } return !mVanityMode && !mPreviewMode; @@ -44,26 +48,21 @@ namespace MWRender Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); if (adjust) { - float f = - limitPitchAngle(89.5f, Ogre::Radian(rot.x).valueDegrees()); - - if (f != 0.0) { - pitchNode->pitch(Ogre::Degree(f)); - } - yawNode->yaw(Ogre::Radian(-rot.z)); + setYaw(getYaw() + rot.z); + setPitch(getPitch() + rot.x); } else { - Ogre::Radian radx(rot.x); - if (radx.valueDegrees() > 89.5f) { - radx = Ogre::Degree(89.5f); - } else if (radx.valueDegrees() < -89.5f) { - radx = Ogre::Degree(-89.5f); - } - Ogre::Quaternion xr(radx, Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Y); - - pitchNode->setOrientation(xr); - yawNode->setOrientation(yr); + setYaw(rot.z); + setPitch(rot.x); } + Ogre::Quaternion xr( + Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), + Ogre::Vector3::UNIT_X + ); + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z); + + pitchNode->setOrientation(xr); + yawNode->setOrientation(zr); + updateListener(); } @@ -77,27 +76,6 @@ namespace MWRender ptr.getRefData().setBaseNode(mPlayerNode); } - float Player::limitPitchAngle(float limitAbs, float shift) - { - Ogre::Quaternion orient = - mCamera->getParentSceneNode()->getOrientation(); - - float pitchAngle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - - if (pitchAngle + shift < limitAbs && pitchAngle + shift > -limitAbs) { - return shift; - } - if (pitchAngle > 0) { - float f = limitAbs - pitchAngle - shift; - return (f > 0.f) ? f : 0.f; - } else if (pitchAngle < 0) { - float f = -limitAbs - pitchAngle - shift; - return (f < 0.f) ? f : 0.f; - } - return 0.f; - } - void Player::updateListener() { Ogre::Vector3 pos = mCamera->getRealPosition(); @@ -116,7 +94,7 @@ namespace MWRender ++mUpdates; mTimeIdle += duration; if (mTimeIdle > 30.f) { - toggleVanityMode(); + toggleVanityMode(true); } } if (mFirstPersonView && !mVanityMode) { @@ -139,54 +117,83 @@ namespace MWRender } } - void Player::toggleVanityMode() + void Player::toggleVanityMode(bool enable, bool force) { - mVanityMode = !mVanityMode; + if (mVanityMode == enable) { + return; + } + mVanityMode = enable; float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanityMode) { - mYaw = getYawAngle(); - mPitch = getPitchAngle(); - mOffset = mCamera->getPosition().z; - rot.x = Ogre::Degree(-30.f).valueRadians(); + mMainCam.offset = mCamera->getPosition().z; + + mPlayerNode->removeChild(mCameraNode); + mVanityNode->addChild(mCameraNode); } else { - rot.x = Ogre::Degree(mPitch).valueRadians(); - rot.z = Ogre::Degree(mYaw).valueRadians(); - offset = mOffset; + rot.x = getPitch(); + offset = mMainCam.offset; + + mVanityNode->removeChild(mCameraNode); + mPlayerNode->addChild(mCameraNode); } + rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); } - void Player::togglePreviewMode() + void Player::togglePreviewMode(bool enable) { /// \todo move camera - mPreviewMode = !mPreviewMode; - } - - float Player::getPitchAngle() - { - Ogre::Quaternion orient - = mCamera->getParentSceneNode()->getOrientation(); - - float angle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.x)).valueDegrees()); - - return angle; - } - - float Player::getYawAngle() - { - Ogre::Quaternion orient - = mCameraNode->getOrientation(); - - float angle = - (2 * Ogre::Degree(Ogre::Math::ASin(orient.y)).valueDegrees()); - if (orient.w < 0) { - angle = -angle; + if (mPreviewMode == enable) { + return; + } + mPreviewMode = enable; + } + + float Player::getYaw() + { + if (mVanityMode || mPreviewMode) { + return mPreviewCam.yaw; + } + return mMainCam.yaw; + } + + void Player::setYaw(float angle) + { + if (angle > Ogre::Math::PI) { + angle -= Ogre::Math::TWO_PI; + } else if (angle < -Ogre::Math::PI) { + angle += Ogre::Math::TWO_PI; + } + if (mVanityMode || mPreviewMode) { + mPreviewCam.yaw = angle; + } else { + mMainCam.yaw = angle; + } + } + + float Player::getPitch() + { + if (mVanityMode || mPreviewMode) { + return mPreviewCam.pitch; + } + return mMainCam.pitch; + } + + void Player::setPitch(float angle) + { + if (angle > Ogre::Math::HALF_PI) { + angle = Ogre::Math::HALF_PI - 0.01; + } else if (angle < -Ogre::Math::HALF_PI) { + angle = -Ogre::Math::HALF_PI + 0.01; + } + if (mVanityMode || mPreviewMode) { + mPreviewCam.pitch = angle; + } else { + mMainCam.pitch = angle; } - return -angle; } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index ed4923817..060d62fa0 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -20,29 +20,36 @@ namespace MWRender /// \brief Player character rendering and camera control class Player { + struct CamData { + float pitch, yaw, offset; + }; + Ogre::Camera *mCamera; Ogre::SceneNode *mPlayerNode; Ogre::SceneNode *mCameraNode; + Ogre::SceneNode *mVanityNode; bool mFirstPersonView; bool mVanityMode; bool mPreviewMode; - float mPitch, mYaw, mOffset; + float mHeight; + CamData mMainCam, mPreviewCam; float mTimeIdle; int mUpdates; - float limitPitchAngle(float limitAbs, float shift = 0.f); - /// Updates sound manager listener data void updateListener(); void rotateCamera(const Ogre::Vector3 &rot, bool adjust); - float getYawAngle(); - float getPitchAngle(); + float getYaw(); + void setYaw(float angle); + + float getPitch(); + void setPitch(float angle); public: @@ -62,9 +69,9 @@ namespace MWRender void toggleViewMode(); - void toggleVanityMode(); + void toggleVanityMode(bool enable, bool force = false); - void togglePreviewMode(); + void togglePreviewMode(bool enable); void update(float duration); }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d5e28e439..cc08eae91 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -135,7 +135,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); - playerNode->pitch(Degree(90)); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mShadows = new Shadows(&mRendering); @@ -309,17 +308,23 @@ void RenderingManager::update (float duration){ mRendering.update(duration); - float *fpos = + MWWorld::RefData &data = MWBase::Environment::get() .getWorld() ->getPlayer() .getPlayer() - .getRefData() - .getPosition() - .pos; - Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + .getRefData(); - mLocalMap->updatePlayer(pos, mRendering.getCamera()->getRealOrientation() ); + float *fpos = data.getPosition().pos; + + /// \note only for LocalMap::updatePlayer() + Ogre::Vector3 pos(fpos[0], -fpos[2], fpos[1]); + + Ogre::SceneNode *node = data.getBaseNode(); + Ogre::Quaternion orient = + node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); + + mLocalMap->updatePlayer(mRendering.getCamera()->getRealPosition(), orient); if (mWater) { Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 45cfdd123..1bbfe33fd 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -11,7 +11,7 @@ #include -#include "../mwbase/world.hpp" // FIXME +//#include "../mwbase/world.hpp" // FIXME #include "ptr.hpp" #include "class.hpp" @@ -172,7 +172,7 @@ namespace MWWorld OEngine::Physic::PhysicActor* act = it->second; act->setWalkDirection(btVector3(0,0,0)); } - playerMove::playercmd& pm_ref = playerphysics->cmd; + playerMove::playercmd& pm_ref = playerphysics->cmd; pm_ref.rightmove = 0; pm_ref.forwardmove = 0; @@ -183,35 +183,25 @@ namespace MWWorld iter!=actors.end(); ++iter) { //dirty stuff to get the camera orientation. Must be changed! + Ogre::Quaternion orient = + mRender.getScene()->getSceneNode(iter->first)->getOrientation(); - Ogre::SceneNode *sceneNode = mRender.getScene()->getSceneNode (iter->first); - Ogre::Vector3 dir; - Ogre::Node* yawNode = sceneNode->getChildIterator().getNext(); - Ogre::Node* pitchNode = yawNode->getChildIterator().getNext(); - Ogre::Quaternion yawQuat = yawNode->getOrientation(); - Ogre::Quaternion pitchQuat = pitchNode->getOrientation(); - - - - playerphysics->ps.viewangles.x = pitchQuat.getPitch().valueDegrees(); - - playerphysics->ps.viewangles.y = yawQuat.getYaw().valueDegrees() *-1 + 90; - - - Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); - - pm_ref.rightmove = -iter->second.x; - pm_ref.forwardmove = -iter->second.y; - pm_ref.upmove = iter->second.z; + float yaw = orient.getRoll().valueDegrees(); + float pitch = 0.f; + if (iter->first == "player") { + /// \fixme should not rely on player camera, real pitch needed + Ogre::SceneNode *node = mRender.getCamera()->getParentSceneNode(); + pitch = node->getOrientation().getPitch().valueDegrees(); + playerphysics->ps.viewangles.x = pitch - 90; + playerphysics->ps.viewangles.y = -yaw + 90; + pm_ref.rightmove = -iter->second.x; + pm_ref.forwardmove = -iter->second.y; + pm_ref.upmove = iter->second.z; + } } - - - - - mEngine->stepSimulation(dt); } From 6f87c0c36d912de51b943e7989d6c4117d568760 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 14:37:48 +0400 Subject: [PATCH 432/688] preview mode, advanced vanity support --- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwinput/inputmanagerimp.cpp | 27 ++-- apps/openmw/mwrender/player.cpp | 145 +++++++++++++++++----- apps/openmw/mwrender/player.hpp | 14 ++- apps/openmw/mwrender/renderingmanager.cpp | 4 +- apps/openmw/mwrender/renderingmanager.hpp | 12 ++ apps/openmw/mwworld/worldimp.hpp | 12 ++ 7 files changed, 169 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index c2db2acf1..819a242ec 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -250,6 +250,9 @@ namespace MWBase virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; virtual void togglePOV() = 0; + virtual void togglePreviewMode(bool enable) = 0; + virtual bool toggleVanityMode(bool enable, bool force) = 0; + virtual void allowVanityMode(bool allow) = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 6bdd87d2a..f0ca510ea 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -406,19 +406,22 @@ private: else player.setUpDown (0); - if (poller.isDown(A_TogglePOV)) { - if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += duration) > 0.5) - { - // enable preview mode + if (mControlSwitch["playerviewswitch"]) { + if (poller.isDown(A_TogglePOV)) { + if (mPreviewPOVDelay <= 0.5 && + (mPreviewPOVDelay += duration) > 0.5) + { + MWBase::Environment::get().getWorld()->togglePreviewMode(true); + } + } else { + if (mPreviewPOVDelay > 0.5) { + //disable preview mode + MWBase::Environment::get().getWorld()->togglePreviewMode(false); + } else if (mPreviewPOVDelay > 0.f) { + togglePOV(); + } + mPreviewPOVDelay = 0.f; } - } else { - if (mPreviewPOVDelay > 0.5) { - //disable preview mode - } else if (mPreviewPOVDelay > 0.f) { - togglePOV(); - } - mPreviewPOVDelay = 0.f; } } } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 5b74b9903..2a1feacad 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -18,28 +18,49 @@ namespace MWRender mCameraNode(mPlayerNode->createChildSceneNode()), mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), - mVanityMode(false), mPreviewMode(false), - mHeight(40.f) + mHeight(40.f), + mCameraDistance(400.f) { + mVanity.enabled = false; + mVanity.allowed = true; + mVanity.forced = false; + mCameraNode->attachObject(mCamera); mCameraNode->setPosition(0.f, 0.f, mHeight); mPreviewCam.yaw = 0.f; + mPreviewCam.offset = 600.f; } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - rotateCamera(rot, adjust); - mUpdates = 0; mTimeIdle = 0.f; - if (mVanityMode) { + if (mVanity.enabled) { toggleVanityMode(false); } - return !mVanityMode && !mPreviewMode; + Ogre::Vector3 trueRot = rot; + + /// \note rotate player on forced vanity + if (mVanity.forced) { + moveCameraNode(mPlayerNode); + mVanity.enabled = false; + + rotateCamera(rot, adjust); + + moveCameraNode(mVanityNode); + mVanity.enabled = true; + + trueRot.z = 0.f; + } + + rotateCamera(trueRot, adjust); + + /// \note if vanity mode is forced by TVM then rotate player + return (!mVanity.enabled && !mPreviewMode) || mVanity.forced; } void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) @@ -90,19 +111,19 @@ namespace MWRender void Player::update(float duration) { - if (!mVanityMode) { + if (!mVanity.enabled) { ++mUpdates; mTimeIdle += duration; if (mTimeIdle > 30.f) { toggleVanityMode(true); } } - if (mFirstPersonView && !mVanityMode) { + if (mFirstPersonView && !mVanity.enabled) { return; } - if (mVanityMode) { + if (mVanity.enabled) { Ogre::Vector3 rot(0.f, 0.f, 0.f); - rot.z = Ogre::Degree(3.f * duration).valueRadians(); + rot.z = Ogre::Degree(-3.f * duration).valueRadians(); rotateCamera(rot, true); } } @@ -113,49 +134,75 @@ namespace MWRender if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); } else { - mCamera->setPosition(0.f, 0.f, 400.f); + mCamera->setPosition(0.f, 0.f, mCameraDistance); } } - - void Player::toggleVanityMode(bool enable, bool force) + + void Player::allowVanityMode(bool allow) { - if (mVanityMode == enable) { - return; + if (!allow && mVanity.enabled && !mVanity.forced) { + toggleVanityMode(false); } - mVanityMode = enable; + mVanity.allowed = allow; + } + + bool Player::toggleVanityMode(bool enable, bool force) + { + if ((mVanity.forced && !force) || + (!mVanity.allowed && (force || enable))) + { + return false; + } else if (mVanity.enabled == enable) { + return true; + } + mVanity.enabled = enable; + mVanity.forced = force && enable; float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); - if (mVanityMode) { + if (mVanity.enabled) { rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - mPlayerNode->removeChild(mCameraNode); - mVanityNode->addChild(mCameraNode); + moveCameraNode(mVanityNode); } else { rot.x = getPitch(); offset = mMainCam.offset; - mVanityNode->removeChild(mCameraNode); - mPlayerNode->addChild(mCameraNode); + moveCameraNode(mPlayerNode); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); + + return true; } void Player::togglePreviewMode(bool enable) { - /// \todo move camera if (mPreviewMode == enable) { return; } mPreviewMode = enable; + float offset = mCamera->getPosition().z; + if (mPreviewMode) { + mMainCam.offset = offset; + offset = mPreviewCam.offset; + + moveCameraNode(mVanityNode); + } else { + mPreviewCam.offset = offset; + offset = mMainCam.offset; + + moveCameraNode(mPlayerNode); + } + mCamera->setPosition(0.f, 0.f, mPreviewCam.offset); + rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } float Player::getYaw() { - if (mVanityMode || mPreviewMode) { + if (mVanity.enabled || mPreviewMode) { return mPreviewCam.yaw; } return mMainCam.yaw; @@ -168,7 +215,7 @@ namespace MWRender } else if (angle < -Ogre::Math::PI) { angle += Ogre::Math::TWO_PI; } - if (mVanityMode || mPreviewMode) { + if (mVanity.enabled || mPreviewMode) { mPreviewCam.yaw = angle; } else { mMainCam.yaw = angle; @@ -177,23 +224,59 @@ namespace MWRender float Player::getPitch() { - if (mVanityMode || mPreviewMode) { + if (mVanity.enabled || mPreviewMode) { return mPreviewCam.pitch; } return mMainCam.pitch; } void Player::setPitch(float angle) - { - if (angle > Ogre::Math::HALF_PI) { - angle = Ogre::Math::HALF_PI - 0.01; - } else if (angle < -Ogre::Math::HALF_PI) { - angle = -Ogre::Math::HALF_PI + 0.01; + { + float limit = Ogre::Math::HALF_PI; + if (mVanity.forced || mPreviewMode) { + limit /= 2; } - if (mVanityMode || mPreviewMode) { + if (angle > limit) { + angle = limit - 0.01; + } else if (angle < -limit) { + angle = -limit + 0.01; + } + if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; } else { mMainCam.pitch = angle; } } + + void Player::moveCameraNode(Ogre::SceneNode *node) + { + mCameraNode->getParentSceneNode()->removeChild(mCameraNode); + node->addChild(mCameraNode); + } + + void Player::setCameraDistance(float dist, bool adjust) + { + /// \note non-Morrowind feature: allow to change camera distance + /// int 3d-person mode + /// \todo review and simplify condition if possible + if (mPreviewMode || + mVanity.forced || + (!mVanity.enabled && !mFirstPersonView)) + { + Ogre::Vector3 v(0.f, 0.f, dist); + if (adjust) { + v += mCamera->getPosition(); + } + if (v.z > 800.f) { + v.z = 800.f; + } else if (v.z < 100.f) { + v.z = 100.f; + } + mCamera->setPosition(v); + + if (!mVanity.enabled && !mFirstPersonView) { + mCameraDistance = v.z; + } + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 060d62fa0..1ca9bfc21 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -31,10 +31,13 @@ namespace MWRender Ogre::SceneNode *mVanityNode; bool mFirstPersonView; - bool mVanityMode; bool mPreviewMode; - float mHeight; + struct { + bool enabled, allowed, forced; + } mVanity; + + float mHeight, mCameraDistance; CamData mMainCam, mPreviewCam; float mTimeIdle; @@ -51,6 +54,8 @@ namespace MWRender float getPitch(); void setPitch(float angle); + void moveCameraNode(Ogre::SceneNode *node); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); @@ -69,11 +74,14 @@ namespace MWRender void toggleViewMode(); - void toggleVanityMode(bool enable, bool force = false); + bool toggleVanityMode(bool enable, bool force = false); + void allowVanityMode(bool allow); void togglePreviewMode(bool enable); void update(float duration); + + void setCameraDistance(float dist, bool adjust = false); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index cc08eae91..05902d662 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -318,13 +318,13 @@ void RenderingManager::update (float duration){ float *fpos = data.getPosition().pos; /// \note only for LocalMap::updatePlayer() - Ogre::Vector3 pos(fpos[0], -fpos[2], fpos[1]); + Ogre::Vector3 pos(fpos[0], -fpos[2], -fpos[1]); Ogre::SceneNode *node = data.getBaseNode(); Ogre::Quaternion orient = node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); - mLocalMap->updatePlayer(mRendering.getCamera()->getRealPosition(), orient); + mLocalMap->updatePlayer(pos, orient); if (mWater) { Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f100fdd59..741ccc559 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -60,6 +60,18 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->toggleViewMode(); } + void togglePreviewMode(bool enable) { + mPlayer->togglePreviewMode(enable); + } + + virtual bool toggleVanityMode(bool enable, bool force) { + return mPlayer->toggleVanityMode(enable, force); + } + + virtual void allowVanityMode(bool allow) { + mPlayer->allowVanityMode(allow); + } + void attachCameraTo(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2e5f73702..26824e763 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -280,6 +280,18 @@ namespace MWWorld virtual void togglePOV() { mRendering->togglePOV(); } + + virtual void togglePreviewMode(bool enable) { + mRendering->togglePreviewMode(enable); + } + + virtual bool toggleVanityMode(bool enable, bool force) { + return mRendering->toggleVanityMode(enable, force); + } + + virtual void allowVanityMode(bool allow) { + mRendering->allowVanityMode(allow); + } }; } From 392e6efcb52e5de6f0acadcf9861f48f77c26590 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 20:33:29 +0400 Subject: [PATCH 433/688] initial player rendering --- apps/openmw/engine.cpp | 2 ++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwinput/inputmanagerimp.cpp | 1 + apps/openmw/mwrender/player.cpp | 21 ++++++++++++++++++++- apps/openmw/mwrender/player.hpp | 10 ++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 12 ++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 9 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b8421cc62..b818477e6 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -352,6 +352,8 @@ void OMW::Engine::go() pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.pos[2] = 0; + mEnvironment.getWorld()->renderPlayer(); + if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) { MWBase::Environment::get().getWorld()->indexToPosition (exterior->data.gridX, exterior->data.gridY, diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 819a242ec..fbc57b487 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -253,6 +253,8 @@ namespace MWBase virtual void togglePreviewMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; + + virtual void renderPlayer() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f0ca510ea..9295233de 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -411,6 +411,7 @@ private: if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += duration) > 0.5) { + mPreviewPOVDelay = 1.f; MWBase::Environment::get().getWorld()->togglePreviewMode(true); } } else { diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 2a1feacad..c20ffad55 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -10,6 +10,8 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/refdata.hpp" +#include "npcanimation.hpp" + namespace MWRender { Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) @@ -19,7 +21,7 @@ namespace MWRender mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mPreviewMode(false), - mHeight(40.f), + mHeight(128.f), mCameraDistance(400.f) { mVanity.enabled = false; @@ -111,6 +113,7 @@ namespace MWRender void Player::update(float duration) { + Ogre::Vector3 pos = mPlayerNode->getPosition(); if (!mVanity.enabled) { ++mUpdates; mTimeIdle += duration; @@ -118,6 +121,9 @@ namespace MWRender toggleVanityMode(true); } } + if (mAnimation) { + mAnimation->runAnimation(duration); + } if (mFirstPersonView && !mVanity.enabled) { return; } @@ -133,8 +139,10 @@ namespace MWRender mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); + mCameraNode->setPosition(0.f, 0.f, 128.f); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); + mCameraNode->setPosition(0.f, 0.f, 104.f); } } @@ -279,4 +287,15 @@ namespace MWRender } } } + + void Player::setHeight(float height) + { + mHeight = height; + mCameraNode->setPosition(0.f, 0.f, mHeight); + } + + float Player::getHeight() + { + return mHeight; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 1ca9bfc21..82af243bc 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -17,6 +17,7 @@ namespace MWWorld namespace MWRender { + class NpcAnimation; /// \brief Player character rendering and camera control class Player { @@ -30,6 +31,8 @@ namespace MWRender Ogre::SceneNode *mCameraNode; Ogre::SceneNode *mVanityNode; + NpcAnimation *mAnimation; + bool mFirstPersonView; bool mPreviewMode; @@ -82,6 +85,13 @@ namespace MWRender void update(float duration); void setCameraDistance(float dist, bool adjust = false); + + void setAnimation(MWRender::NpcAnimation *anim) { + mAnimation = anim; + } + + void setHeight(float height); + float getHeight(); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 05902d662..8b6f483ca 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -33,6 +33,7 @@ #include "localmap.hpp" #include "water.hpp" #include "compositors.hpp" +#include "npcanimation.hpp" using namespace MWRender; using namespace Ogre; @@ -830,4 +831,15 @@ void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) mPlayer->attachTo(ptr); } +void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) +{ + MWRender::NpcAnimation *anim = + new MWRender::NpcAnimation( + ptr, + mRendering, + MWWorld::Class::get(ptr).getInventoryStore(ptr) + ); + mPlayer->setAnimation(anim); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 741ccc559..aa07bbc22 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -73,6 +73,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList } void attachCameraTo(const MWWorld::Ptr &ptr); + void renderPlayer(const MWWorld::Ptr &ptr); SkyManager* getSkyManager(); Compositors* getCompositors(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 20706438d..7a8f73034 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1151,4 +1151,9 @@ namespace MWWorld } return pos.z < cell.water; } + + void World::renderPlayer() + { + mRendering->renderPlayer(mPlayer->getPlayer()); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 26824e763..717c43cfa 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -292,6 +292,8 @@ namespace MWWorld virtual void allowVanityMode(bool allow) { mRendering->allowVanityMode(allow); } + + virtual void renderPlayer(); }; } From 783e81afc3fcd296c8f9366a4ab3812479216c04 Mon Sep 17 00:00:00 2001 From: greye Date: Tue, 14 Aug 2012 22:39:42 +0400 Subject: [PATCH 434/688] fix 1st/preview/1st change; 1st invisible model --- apps/openmw/mwrender/player.cpp | 14 +++++++++++++- apps/openmw/mwrender/renderingmanager.cpp | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index c20ffad55..4a6ce0370 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -31,6 +31,8 @@ namespace MWRender mCameraNode->attachObject(mCamera); mCameraNode->setPosition(0.f, 0.f, mHeight); + mPlayerNode->setVisible(false); + mPreviewCam.yaw = 0.f; mPreviewCam.offset = 600.f; } @@ -140,9 +142,11 @@ namespace MWRender if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); mCameraNode->setPosition(0.f, 0.f, 128.f); + mPlayerNode->setVisible(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); mCameraNode->setPosition(0.f, 0.f, 104.f); + mPlayerNode->setVisible(true); } } @@ -169,6 +173,7 @@ namespace MWRender float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { + mPlayerNode->setVisible(true); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; @@ -179,6 +184,9 @@ namespace MWRender moveCameraNode(mPlayerNode); } + if (offset == 0.f) { + mPlayerNode->setVisible(false); + } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); @@ -194,6 +202,7 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { + mPlayerNode->setVisible(true); mMainCam.offset = offset; offset = mPreviewCam.offset; @@ -204,7 +213,10 @@ namespace MWRender moveCameraNode(mPlayerNode); } - mCamera->setPosition(0.f, 0.f, mPreviewCam.offset); + if (offset == 0.f) { + mPlayerNode->setVisible(false); + } + mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8b6f483ca..87faba3e7 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -131,10 +131,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const SceneNode *rt = mRendering.getScene()->getRootSceneNode(); mMwRoot = rt->createChildSceneNode(); mMwRoot->pitch(Degree(-90)); + mObjects.setMwRoot(mMwRoot); mActors.setMwRoot(mMwRoot); - Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); From 575244bd35898c146c2e9cf81879504f8f8a4937 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 15 Aug 2012 15:17:35 +0400 Subject: [PATCH 435/688] fix SkaManager issue --- apps/openmw/mwrender/player.cpp | 23 +++++++++++------------ apps/openmw/mwrender/player.hpp | 4 +--- apps/openmw/mwrender/sky.cpp | 2 ++ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 4a6ce0370..6e0012d43 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -31,8 +31,6 @@ namespace MWRender mCameraNode->attachObject(mCamera); mCameraNode->setPosition(0.f, 0.f, mHeight); - mPlayerNode->setVisible(false); - mPreviewCam.yaw = 0.f; mPreviewCam.offset = 600.f; } @@ -142,12 +140,11 @@ namespace MWRender if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); mCameraNode->setPosition(0.f, 0.f, 128.f); - mPlayerNode->setVisible(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); mCameraNode->setPosition(0.f, 0.f, 104.f); - mPlayerNode->setVisible(true); } + mPlayerNode->setVisible(!mFirstPersonView, false); } void Player::allowVanityMode(bool allow) @@ -173,7 +170,7 @@ namespace MWRender float offset = 300.f; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { - mPlayerNode->setVisible(true); + mPlayerNode->setVisible(true, false); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; @@ -183,9 +180,7 @@ namespace MWRender offset = mMainCam.offset; moveCameraNode(mPlayerNode); - } - if (offset == 0.f) { - mPlayerNode->setVisible(false); + mPlayerNode->setVisible(!mFirstPersonView, false); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); @@ -202,7 +197,7 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { - mPlayerNode->setVisible(true); + mPlayerNode->setVisible(true, false); mMainCam.offset = offset; offset = mPreviewCam.offset; @@ -212,9 +207,7 @@ namespace MWRender offset = mMainCam.offset; moveCameraNode(mPlayerNode); - } - if (offset == 0.f) { - mPlayerNode->setVisible(false); + mPlayerNode->setVisible(!mFirstPersonView, false); } mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); @@ -300,6 +293,12 @@ namespace MWRender } } + void Player::setAnimation(NpcAnimation *anim) + { + mAnimation = anim; + mPlayerNode->setVisible(!mFirstPersonView, false); + } + void Player::setHeight(float height) { mHeight = height; diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 82af243bc..8c1b3ed9f 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -86,9 +86,7 @@ namespace MWRender void setCameraDistance(float dist, bool adjust = false); - void setAnimation(MWRender::NpcAnimation *anim) { - mAnimation = anim; - } + void setAnimation(MWRender::NpcAnimation *anim); void setHeight(float height); float getHeight(); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 9e551ba2a..3eb42a05e 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -405,6 +405,8 @@ void SkyManager::update(float duration) { if (!mEnabled) return; + mRootNode->setPosition(mCamera->getPosition()); + // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed * (MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", From b97b3d7b71b07538657edd924976e5b294c4bf01 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Aug 2012 17:56:24 +0200 Subject: [PATCH 436/688] fix sky position when reflection is enabled, fix delay in sky reflection --- apps/openmw/mwrender/sky.cpp | 10 ++++++---- apps/openmw/mwrender/water.cpp | 4 +--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 3eb42a05e..eba605d0c 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -280,7 +280,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mMoonRed(false) { mSceneMgr = pMwRoot->getCreator(); - mRootNode = mCamera->getParentSceneNode()->createChildSceneNode(); + mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates mRootNode->setInheritOrientation(false); } @@ -405,7 +405,8 @@ void SkyManager::update(float duration) { if (!mEnabled) return; - mRootNode->setPosition(mCamera->getPosition()); + mCamera->getParentSceneNode ()->needUpdate (); + mRootNode->setPosition(mCamera->getDerivedPosition()); // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed * (MWBase::Environment::get().getWorld()->getTimeScaleFactor()/30.f); @@ -668,12 +669,13 @@ Ogre::SceneNode* SkyManager::getSunNode() void SkyManager::setSkyPosition(const Ogre::Vector3& position) { - mRootNode->_setDerivedPosition(position); + mRootNode->setPosition(position); } void SkyManager::resetSkyPosition() { - mRootNode->setPosition(0,0,0); + mCamera->getParentSceneNode ()->needUpdate (); + mRootNode->setPosition(mCamera->getDerivedPosition()); } void SkyManager::scaleSky(float scale) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index d5b93b7cb..46678f2bc 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -207,6 +207,7 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) { if (evt.source == mReflectionTarget) { + mCamera->getParentSceneNode ()->needUpdate (); mReflectionCamera->setOrientation(mCamera->getDerivedOrientation()); mReflectionCamera->setPosition(mCamera->getDerivedPosition()); mReflectionCamera->setNearClipDistance(mCamera->getNearClipDistance()); @@ -215,11 +216,9 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) mReflectionCamera->setFOVy(mCamera->getFOVy()); mReflectionRenderActive = true; - /// \todo the reflection render (and probably all renderingmanager-updates) lag behind 1 camera frame for some reason Vector3 pos = mCamera->getRealPosition(); pos.y = mTop*2 - pos.y; mSky->setSkyPosition(pos); - mSky->scaleSky(mCamera->getFarClipDistance() / 50.f); mReflectionCamera->enableReflection(mWaterPlane); } } @@ -229,7 +228,6 @@ void Water::postRenderTargetUpdate(const RenderTargetEvent& evt) if (evt.source == mReflectionTarget) { mSky->resetSkyPosition(); - mSky->scaleSky(1); mReflectionCamera->disableReflection(); mReflectionCamera->disableCustomNearClipPlane(); mReflectionRenderActive = false; From 34f796c38e01d7701706179588d502c74326d998 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Aug 2012 18:29:21 +0200 Subject: [PATCH 437/688] removed the far clip distance hack which is not needed anymore --- apps/openmw/mwrender/water.cpp | 16 ---------------- apps/openmw/mwrender/water.hpp | 3 --- 2 files changed, 19 deletions(-) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 46678f2bc..e79f308cd 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -32,7 +32,6 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mIsUnderwater(false), mVisibilityFlags(0), mReflectionTarget(0), mActive(1), mToggled(1), mReflectionRenderActive(false), mRendering(rend), - mOldFarClip(0), mOldFarClip2(0), mWaterTimer(0.f) { mSky = rend->getSkyManager(); @@ -267,34 +266,19 @@ void Water::renderQueueStarted (Ogre::uint8 queueGroupId, const Ogre::String &in // We don't want the sky to get clipped by custom near clip plane (the water plane) if (queueGroupId < 20 && mReflectionRenderActive) { - mOldFarClip = mReflectionCamera->getFarClipDistance (); mReflectionCamera->disableCustomNearClipPlane(); - mReflectionCamera->setFarClipDistance (1000000000); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } - else if (queueGroupId == RQG_UnderWater) - {/* - mOldFarClip2 = mCamera->getFarClipDistance (); - mCamera->setFarClipDistance (1000000000); - Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); - */} } void Water::renderQueueEnded (Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &repeatThisInvocation) { if (queueGroupId < 20 && mReflectionRenderActive) { - mReflectionCamera->setFarClipDistance (mOldFarClip); if (!mIsUnderwater) mReflectionCamera->enableCustomNearClipPlane(mErrorPlane); Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mReflectionCamera->getProjectionMatrixRS()); } - if (queueGroupId == RQG_UnderWater) - { - /* - mCamera->setFarClipDistance (mOldFarClip2); - Root::getSingleton().getRenderSystem()->_setProjectionMatrix(mCamera->getProjectionMatrixRS()); - */} } void Water::update(float dt) diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index f56ba7410..dcb76533b 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -50,9 +50,6 @@ namespace MWRender { bool mToggled; int mTop; - int mOldFarClip; - int mOldFarClip2; - float mWaterTimer; bool mReflectionRenderActive; From a453a7f035fbd4e21a5790258fce123415309966 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 16 Aug 2012 13:15:38 +0400 Subject: [PATCH 438/688] camera adjustment, fix view mode on start --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 +- apps/openmw/mwrender/player.cpp | 70 +++++++++++++++-------- apps/openmw/mwrender/player.hpp | 15 ++++- apps/openmw/mwrender/renderingmanager.cpp | 20 ++++++- 4 files changed, 80 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 9295233de..51c77e68e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -242,7 +242,8 @@ private: player(_player), windows(_windows), mEngine (engine), - mDragDrop(false) + mDragDrop(false), + mPreviewPOVDelay(0.f) { using namespace OEngine::Input; using namespace OEngine::Render; diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 6e0012d43..68def64e8 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -22,7 +22,8 @@ namespace MWRender mFirstPersonView(true), mPreviewMode(false), mHeight(128.f), - mCameraDistance(400.f) + mCameraDistance(300.f), + mDistanceAdjusted(false) { mVanity.enabled = false; mVanity.allowed = true; @@ -32,7 +33,7 @@ namespace MWRender mCameraNode->setPosition(0.f, 0.f, mHeight); mPreviewCam.yaw = 0.f; - mPreviewCam.offset = 600.f; + mPreviewCam.offset = 400.f; } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) @@ -167,7 +168,7 @@ namespace MWRender mVanity.enabled = enable; mVanity.forced = force && enable; - float offset = 300.f; + float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { mPlayerNode->setVisible(true, false); @@ -267,32 +268,45 @@ namespace MWRender node->addChild(mCameraNode); } - void Player::setCameraDistance(float dist, bool adjust) + void Player::setCameraDistance(float dist, bool adjust, bool override) { - /// \note non-Morrowind feature: allow to change camera distance - /// int 3d-person mode - /// \todo review and simplify condition if possible - if (mPreviewMode || - mVanity.forced || - (!mVanity.enabled && !mFirstPersonView)) - { - Ogre::Vector3 v(0.f, 0.f, dist); - if (adjust) { - v += mCamera->getPosition(); - } - if (v.z > 800.f) { - v.z = 800.f; - } else if (v.z < 100.f) { - v.z = 100.f; - } - mCamera->setPosition(v); + if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { + return; + } + Ogre::Vector3 v(0.f, 0.f, dist); + if (adjust) { + v += mCamera->getPosition(); + } + if (v.z > 800.f) { + v.z = 800.f; + } else if (v.z < 10.f) { + v.z = 10.f; + } + mCamera->setPosition(v); - if (!mVanity.enabled && !mFirstPersonView) { + if (override) { + if (mVanity.enabled || mPreviewMode) { + mPreviewCam.offset = v.z; + } else if (!mFirstPersonView) { mCameraDistance = v.z; } + } else { + mDistanceAdjusted = true; } } + void Player::setCameraDistance() + { + if (mDistanceAdjusted) { + if (mVanity.enabled || mPreviewMode) { + mCamera->setPosition(0, 0, mPreviewCam.offset); + } else if (!mFirstPersonView) { + mCamera->setPosition(0, 0, mCameraDistance); + } + } + mDistanceAdjusted = false; + } + void Player::setAnimation(NpcAnimation *anim) { mAnimation = anim; @@ -307,6 +321,16 @@ namespace MWRender float Player::getHeight() { - return mHeight; + return mHeight * mPlayerNode->getScale().z; + } + + bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) + { + float xch; + camera = mCamera->getRealPosition(); + xch = camera.z, camera.z = camera.y, camera.y = -xch; + player = mPlayerNode->getPosition(); + + return mFirstPersonView && !mVanity.enabled && !mPreviewMode; } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 8c1b3ed9f..c3585db9d 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -43,6 +43,8 @@ namespace MWRender float mHeight, mCameraDistance; CamData mMainCam, mPreviewCam; + bool mDistanceAdjusted; + float mTimeIdle; int mUpdates; @@ -84,12 +86,23 @@ namespace MWRender void update(float duration); - void setCameraDistance(float dist, bool adjust = false); + /// Set camera distance for current mode. Don't work on 1st person view. + /// \param adjust Indicates should distance be adjusted or set. + /// \param override If true new distance will be used as default. + /// If false, default distance can be restored with setCameraDistance(). + void setCameraDistance(float dist, bool adjust = false, bool override = true); + + /// Restore default camera distance for current mode. + void setCameraDistance(); void setAnimation(MWRender::NpcAnimation *anim); void setHeight(float height); float getHeight(); + + /// Stores player and camera world positions in passed arguments + /// \return true if camera at the eye-place + bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 87faba3e7..f1d2029bb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -41,7 +41,7 @@ using namespace Ogre; namespace MWRender { RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine) - :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0) + :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0), mPhysicsEngine(engine) { // select best shader mode bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); @@ -296,7 +296,22 @@ RenderingManager::moveObjectToCell( child->setPosition(pos); } -void RenderingManager::update (float duration){ +void RenderingManager::update (float duration) +{ + Ogre::Vector3 orig, dest; + mPlayer->setCameraDistance(); + if (!mPlayer->getPosition(orig, dest)) { + orig.z += mPlayer->getHeight() * mMwRoot->getScale().z; + + btVector3 btOrig(orig.x, orig.y, orig.z); + btVector3 btDest(dest.x, dest.y, dest.z); + std::pair test = + mPhysicsEngine->rayTest(btOrig, btDest); + if (!test.first.empty()) { + mPlayer->setCameraDistance(test.second * orig.distance(dest), false, false); + } + } + mPlayer->update(duration); mActors.update (duration); mObjects.update (duration); @@ -339,7 +354,6 @@ void RenderingManager::update (float duration){ ); mWater->update(duration); } - mPlayer->update(duration); } void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ From 40c7a850bf2e7e889d7d17b5733705f27494bbf9 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 16 Aug 2012 15:42:03 +0400 Subject: [PATCH 439/688] fix pmove BB shape origin --- libs/openengine/bullet/trace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 2d18aaa4d..89d654e6a 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -97,8 +97,8 @@ const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& - const btVector3 btstart(start.x, start.y, start.z); - const btVector3 btend(end.x, end.y, end.z); + const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); + const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); const btQuaternion btrot(rotation.y, rotation.x, rotation.z); //y, x, z const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); From e8fc942bef4b530e31e02a631717097478a04da0 Mon Sep 17 00:00:00 2001 From: greye Date: Thu, 16 Aug 2012 16:24:59 +0400 Subject: [PATCH 440/688] reverse Z-axis rotation to make doors happy --- apps/openmw/mwinput/mouselookevent.cpp | 2 +- apps/openmw/mwrender/player.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index 4138c481c..f318ce666 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -24,5 +24,5 @@ void MouseLookEvent::event(Type type, int index, const void *p) float y = arg->state.Y.rel * sensY; MWBase::World *world = MWBase::Environment::get().getWorld(); - world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, -x, true); + world->rotateObject(world->getPlayer().getPlayer(), -y, 0.f, x, true); } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 68def64e8..8ad7e04b0 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -82,7 +82,7 @@ namespace MWRender Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X ); - Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); pitchNode->setOrientation(xr); yawNode->setOrientation(zr); @@ -130,7 +130,7 @@ namespace MWRender } if (mVanity.enabled) { Ogre::Vector3 rot(0.f, 0.f, 0.f); - rot.z = Ogre::Degree(-3.f * duration).valueRadians(); + rot.z = Ogre::Degree(3.f * duration).valueRadians(); rotateCamera(rot, true); } } From 6961830efb5f4141d9ed97256672330d0a82109a Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 10:10:37 +0400 Subject: [PATCH 441/688] using real player eye direction when moving --- apps/openmw/mwrender/player.cpp | 12 ++++++++++++ apps/openmw/mwrender/player.hpp | 3 +++ apps/openmw/mwrender/renderingmanager.cpp | 7 +++++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/physicssystem.cpp | 24 ++++++++++++++--------- apps/openmw/mwworld/physicssystem.hpp | 8 +++++++- apps/openmw/mwworld/worldimp.cpp | 5 +++++ 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8ad7e04b0..c9db96ead 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -11,6 +11,7 @@ #include "../mwworld/refdata.hpp" #include "npcanimation.hpp" +#include namespace MWRender { @@ -333,4 +334,15 @@ namespace MWRender return mFirstPersonView && !mVanity.enabled && !mPreviewMode; } + + Ogre::Vector3 Player::getPosition() + { + return mPlayerNode->getPosition(); + } + + void Player::getSightAngles(float &pitch, float &yaw) + { + pitch = mMainCam.pitch; + yaw = mMainCam.yaw; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index c3585db9d..cc0fcac77 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -103,6 +103,9 @@ namespace MWRender /// Stores player and camera world positions in passed arguments /// \return true if camera at the eye-place bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera); + Ogre::Vector3 getPosition(); + + void getSightAngles(float &pitch, float &yaw); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f1d2029bb..2d29e8f55 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -856,4 +856,11 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) mPlayer->setAnimation(anim); } +void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) +{ + eyepos = mPlayer->getPosition(); + eyepos.z += mPlayer->getHeight(); + mPlayer->getSightAngles(pitch, yaw); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index aa07bbc22..170083556 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->allowVanityMode(allow); } + void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); + void attachCameraTo(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1bbfe33fd..24e2cdbb1 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -16,6 +16,8 @@ #include "ptr.hpp" #include "class.hpp" +#include + using namespace Ogre; namespace MWWorld { @@ -42,10 +44,8 @@ namespace MWWorld return mEngine; } - std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) - { - std::string handle = ""; - + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) + { //get a ray pointing to the center of the viewport Ray centerRay = mRender.getCamera()->getCameraToViewportRay( mRender.getViewport()->getWidth()/2, @@ -190,12 +190,11 @@ namespace MWWorld float pitch = 0.f; if (iter->first == "player") { - /// \fixme should not rely on player camera, real pitch needed - Ogre::SceneNode *node = mRender.getCamera()->getParentSceneNode(); - pitch = node->getOrientation().getPitch().valueDegrees(); + playerphysics->ps.viewangles.x = + Ogre::Radian(mPlayerData.pitch).valueDegrees(); - playerphysics->ps.viewangles.x = pitch - 90; - playerphysics->ps.viewangles.y = -yaw + 90; + playerphysics->ps.viewangles.y = + Ogre::Radian(mPlayerData.yaw).valueDegrees() + 90; pm_ref.rightmove = -iter->second.x; pm_ref.forwardmove = -iter->second.y; @@ -392,4 +391,11 @@ namespace MWWorld return true; } + + void PhysicsSystem::updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw) + { + mPlayerData.eyepos = eyepos; + mPlayerData.pitch = pitch; + mPlayerData.yaw = yaw; + } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index e42fa536b..c6b8199fa 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -70,7 +70,14 @@ namespace MWWorld bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); + void updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw); + private: + struct { + Ogre::Vector3 eyepos; + float pitch, yaw; + } mPlayerData; + OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; bool mFreeFly; @@ -80,7 +87,6 @@ namespace MWWorld PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); }; - } #endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7a8f73034..655397c1f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -806,6 +806,11 @@ namespace MWWorld /// \todo split this function up into subfunctions mWorldScene->update (duration); + + float pitch, yaw; + Ogre::Vector3 eyepos; + mRendering->getPlayerData(eyepos, pitch, yaw); + mPhysics->updatePlayerData(eyepos, pitch, yaw); mWeatherManager->update (duration); From 60fb1e8df6dd3117ee6f0102d08e195b14c4c8ba Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 12:08:31 +0400 Subject: [PATCH 442/688] fix sky in preview mode movement --- apps/openmw/mwrender/player.cpp | 50 ++++++++++++++++++--------------- apps/openmw/mwrender/player.hpp | 2 +- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index c9db96ead..f85e949d0 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -19,7 +19,6 @@ namespace MWRender : mCamera(camera), mPlayerNode(node), mCameraNode(mPlayerNode->createChildSceneNode()), - mVanityNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mPreviewMode(false), mHeight(128.f), @@ -50,14 +49,14 @@ namespace MWRender /// \note rotate player on forced vanity if (mVanity.forced) { - moveCameraNode(mPlayerNode); + float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; + mVanity.enabled = false; - rotateCamera(rot, adjust); - - moveCameraNode(mVanityNode); mVanity.enabled = true; + compensateYaw(diff); + trueRot.z = 0.f; } @@ -69,9 +68,6 @@ namespace MWRender void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) { - Ogre::SceneNode *pitchNode = mCamera->getParentSceneNode(); - Ogre::SceneNode *yawNode = pitchNode->getParentSceneNode(); - if (adjust) { setYaw(getYaw() + rot.z); setPitch(getPitch() + rot.x); @@ -83,11 +79,16 @@ namespace MWRender Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X ); - Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); - - pitchNode->setOrientation(xr); - yawNode->setOrientation(zr); - + Ogre::Quaternion zr( + Ogre::Radian(getYaw()), + Ogre::Vector3::NEGATIVE_UNIT_Z + ); + if (!mVanity.enabled && !mPreviewMode) { + mPlayerNode->setOrientation(zr); + mCameraNode->setOrientation(xr); + } else { + mCameraNode->setOrientation(zr * xr); + } updateListener(); } @@ -176,12 +177,10 @@ namespace MWRender rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - moveCameraNode(mVanityNode); } else { rot.x = getPitch(); offset = mMainCam.offset; - moveCameraNode(mPlayerNode); mPlayerNode->setVisible(!mFirstPersonView, false); } rot.z = getYaw(); @@ -203,12 +202,10 @@ namespace MWRender mMainCam.offset = offset; offset = mPreviewCam.offset; - moveCameraNode(mVanityNode); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; - moveCameraNode(mPlayerNode); mPlayerNode->setVisible(!mFirstPersonView, false); } mCamera->setPosition(0.f, 0.f, offset); @@ -263,12 +260,6 @@ namespace MWRender } } - void Player::moveCameraNode(Ogre::SceneNode *node) - { - mCameraNode->getParentSceneNode()->removeChild(mCameraNode); - node->addChild(mCameraNode); - } - void Player::setCameraDistance(float dist, bool adjust, bool override) { if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { @@ -345,4 +336,17 @@ namespace MWRender pitch = mMainCam.pitch; yaw = mMainCam.yaw; } + + void Player::compensateYaw(float diff) + { + mPreviewCam.yaw -= diff; + Ogre::Quaternion zr( + Ogre::Radian(mPreviewCam.yaw), + Ogre::Vector3::NEGATIVE_UNIT_Z + ); + Ogre::Quaternion xr( + Ogre::Radian(mPreviewCam.pitch), + Ogre::Vector3::UNIT_X); + mCameraNode->setOrientation(zr * xr); + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index cc0fcac77..3f419889a 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -59,7 +59,7 @@ namespace MWRender float getPitch(); void setPitch(float angle); - void moveCameraNode(Ogre::SceneNode *node); + void compensateYaw(float diff); public: From 0e6e141fd484609ea95c748661e5342ee5dfc8a5 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 13:23:02 +0400 Subject: [PATCH 443/688] camera control related script instructions --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 23 +++++++++++++--------- apps/openmw/mwinput/inputmanagerimp.hpp | 3 ++- apps/openmw/mwrender/player.cpp | 24 +++++++++++++++-------- apps/openmw/mwrender/player.hpp | 4 +++- apps/openmw/mwrender/renderingmanager.hpp | 4 ++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ 7 files changed, 44 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fbc57b487..9c801cbff 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -253,6 +253,7 @@ namespace MWBase virtual void togglePreviewMode(bool enable) = 0; virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; + virtual void togglePlayerLooking(bool enable) = 0; virtual void renderPlayer() = 0; }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 51c77e68e..df0a42996 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -443,10 +443,7 @@ private: } else { - // Start mouse-looking again if allowed. - if (mControlSwitch["playerlooking"]) { - mouse->enable(); - } + mouse->enable(); // Disable GUI events guiEvents->enabled = false; @@ -467,16 +464,19 @@ private: } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time player.setUpDown(0); + } else if (sw == "vanitymode") { + MWBase::Environment::get().getWorld()->allowVanityMode(value); } else if (sw == "playerlooking") { - if (value) { - mouse->enable(); - } else { - mouse->disable(); - } + MWBase::Environment::get().getWorld()->togglePlayerLooking(value); } mControlSwitch[sw] = value; } + bool getControlSwitch(std::string sw) + { + return mControlSwitch[sw]; + } + void togglePOV() { MWBase::Environment::get().getWorld()->togglePOV(); @@ -535,4 +535,9 @@ private: { impl->toggleControlSwitch(sw, value); } + + bool MWInputManager::getControlSwitch(std::string sw) + { + return impl->getControlSwitch(sw); + } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 70436e207..eff2236d4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -60,7 +60,8 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); - virtual void toggleControlSwitch (const std::string& sw, bool value); + void toggleControlSwitch(std::string sw, bool value); + bool getControlSwitch(std::string sw); }; } #endif diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index f85e949d0..3bbb56907 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -11,7 +11,6 @@ #include "../mwworld/refdata.hpp" #include "npcanimation.hpp" -#include namespace MWRender { @@ -21,6 +20,7 @@ namespace MWRender mCameraNode(mPlayerNode->createChildSceneNode()), mFirstPersonView(true), mPreviewMode(false), + mFreeLook(true), mHeight(128.f), mCameraDistance(300.f), mDistanceAdjusted(false) @@ -49,18 +49,21 @@ namespace MWRender /// \note rotate player on forced vanity if (mVanity.forced) { - float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; + if (mFreeLook) { + float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z; - mVanity.enabled = false; - rotateCamera(rot, adjust); - mVanity.enabled = true; - - compensateYaw(diff); + mVanity.enabled = false; + rotateCamera(rot, adjust); + mVanity.enabled = true; + compensateYaw(diff); + } trueRot.z = 0.f; } - rotateCamera(trueRot, adjust); + if (mFreeLook || mVanity.enabled || mPreviewMode) { + rotateCamera(trueRot, adjust); + } /// \note if vanity mode is forced by TVM then rotate player return (!mVanity.enabled && !mPreviewMode) || mVanity.forced; @@ -349,4 +352,9 @@ namespace MWRender Ogre::Vector3::UNIT_X); mCameraNode->setOrientation(zr * xr); } + + void Player::togglePlayerLooking(bool enable) + { + mFreeLook = enable; + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 3f419889a..5bc32fffe 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -29,12 +29,12 @@ namespace MWRender Ogre::SceneNode *mPlayerNode; Ogre::SceneNode *mCameraNode; - Ogre::SceneNode *mVanityNode; NpcAnimation *mAnimation; bool mFirstPersonView; bool mPreviewMode; + bool mFreeLook; struct { bool enabled, allowed, forced; @@ -106,6 +106,8 @@ namespace MWRender Ogre::Vector3 getPosition(); void getSightAngles(float &pitch, float &yaw); + + void togglePlayerLooking(bool enable); }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 170083556..17a4d5280 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->allowVanityMode(allow); } + virtual void togglePlayerLooking(bool enable) { + mPlayer->togglePlayerLooking(enable); + } + void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); void attachCameraTo(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 717c43cfa..51216e3a5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -293,6 +293,10 @@ namespace MWWorld mRendering->allowVanityMode(allow); } + virtual void togglePlayerLooking(bool enable) { + mRendering->togglePlayerLooking(enable); + } + virtual void renderPlayer(); }; } From 4f10138a04d966e3fc579288f42e9e7d4d7c1fd7 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 15:07:12 +0400 Subject: [PATCH 444/688] using real player sight angles on ray cast --- apps/openmw/mwworld/physicssystem.cpp | 42 +++++++++++++++------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 24e2cdbb1..dfba26589 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -16,8 +16,6 @@ #include "ptr.hpp" #include "class.hpp" -#include - using namespace Ogre; namespace MWWorld { @@ -46,28 +44,36 @@ namespace MWWorld std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) { - //get a ray pointing to the center of the viewport - Ray centerRay = mRender.getCamera()->getCameraToViewportRay( - mRender.getViewport()->getWidth()/2, - mRender.getViewport()->getHeight()/2); - //let's avoid the capsule shape of the player. - centerRay.setOrigin(centerRay.getOrigin() + 20*centerRay.getDirection()); - 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); + btVector3 dir(0, 1, 0); + dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir.setX(-dir.x()); - return mEngine->rayTest(from,to); + btVector3 origin( + mPlayerData.eyepos.x, + mPlayerData.eyepos.y, + mPlayerData.eyepos.z); + origin += dir * 5; + + btVector3 dest = origin + dir * 500; + return mEngine->rayTest(origin, dest); } 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); + btVector3 dir(0, 1, 0); + dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir.setX(-dir.x()); - return mEngine->rayTest2(from,to); + btVector3 origin( + mPlayerData.eyepos.x, + mPlayerData.eyepos.y, + mPlayerData.eyepos.z); + origin += dir * 5; + + btVector3 dest = origin + dir * 500; + return mEngine->rayTest2(origin, dest); } std::vector < std::pair > PhysicsSystem::getFacedObjects (float mouseX, float mouseY) From 7303d595dd0e342bab0bf567147918d4b336b5a1 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 15:36:51 +0400 Subject: [PATCH 445/688] resolving conflicts --- apps/openmw/mwbase/inputmanager.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++--- apps/openmw/mwinput/inputmanagerimp.hpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index d865bfb0e..fdbab9fac 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -22,7 +22,7 @@ namespace MWBase virtual ~InputManager() {} - virtual void update() = 0; + virtual void update(float duration) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index df0a42996..0945842b2 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -450,7 +450,7 @@ private: } } - void toggleControlSwitch(std::string sw, bool value) + void toggleControlSwitch(const std::string &sw, bool value) { if (mControlSwitch[sw] == value) { return; @@ -472,7 +472,7 @@ private: mControlSwitch[sw] = value; } - bool getControlSwitch(std::string sw) + bool getControlSwitch(const std::string &sw) { return mControlSwitch[sw]; } @@ -536,7 +536,7 @@ private: impl->toggleControlSwitch(sw, value); } - bool MWInputManager::getControlSwitch(std::string sw) + bool MWInputManager::getControlSwitch(const std::string &sw) { return impl->getControlSwitch(sw); } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index eff2236d4..d4ca32d2f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -60,8 +60,8 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); - void toggleControlSwitch(std::string sw, bool value); - bool getControlSwitch(std::string sw); + void toggleControlSwitch(const std::string &sw, bool value); + bool getControlSwitch(const std::string &sw); }; } #endif From 14f293882cf1f02b9bb0031a7929563a8852fc5b Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 16:42:42 +0400 Subject: [PATCH 446/688] still resolving --- apps/openmw/mwinput/inputmanagerimp.cpp | 14 +++++++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 91ef6722c..292e4c2e9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -45,6 +45,7 @@ namespace MWInput , mUISensitivity (Settings::Manager::getFloat("ui sensitivity", "Input")) , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) + , mPreviewPOVDelay(0.f) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -243,12 +244,12 @@ namespace MWInput else if (actionIsActive(A_Crouch)) mPlayer.setUpDown (-1); else - player.setUpDown (0); + mPlayer.setUpDown (0); if (mControlSwitch["playerviewswitch"]) { - if (poller.isDown(A_TogglePOV)) { + if (actionIsActive(A_TogglePOV)) { if (mPreviewPOVDelay <= 0.5 && - (mPreviewPOVDelay += duration) > 0.5) + (mPreviewPOVDelay += dt) > 0.5) { mPreviewPOVDelay = 1.f; MWBase::Environment::get().getWorld()->togglePreviewMode(true); @@ -258,7 +259,7 @@ namespace MWInput //disable preview mode MWBase::Environment::get().getWorld()->togglePreviewMode(false); } else if (mPreviewPOVDelay > 0.f) { - togglePOV(); + MWBase::Environment::get().getWorld()->togglePOV(); } mPreviewPOVDelay = 0.f; } @@ -329,7 +330,7 @@ namespace MWInput mPlayer.setUpDown(0); } else if (sw == "playerjumping" && !value) { /// \fixme maybe crouching at this time - player.setUpDown(0); + mPlayer.setUpDown(0); } else if (sw == "vanitymode") { MWBase::Environment::get().getWorld()->allowVanityMode(value); } else if (sw == "playerlooking") { @@ -552,6 +553,7 @@ namespace MWInput defaultKeyBindings[A_Journal] = OIS::KC_J; defaultKeyBindings[A_Rest] = OIS::KC_T; defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; + defaultKeyBindings[A_TogglePOV] = OIS::KC_TAB; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; @@ -601,6 +603,7 @@ namespace MWInput descriptions[A_Journal] = "sJournal"; descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; + descriptions[A_TogglePOV] = "Toggle POV"; if (descriptions[action] == "") return ""; // not configurable @@ -631,6 +634,7 @@ namespace MWInput ret.push_back(A_MoveLeft); ret.push_back(A_MoveRight); ret.push_back(A_Crouch); + ret.push_back(A_TogglePOV); ret.push_back(A_Activate); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 72d0b5d67..d199344a3 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -139,6 +139,7 @@ namespace MWInput float mUISensitivity; float mCameraYMultiplier; float mUIYMultiplier; + float mPreviewPOVDelay; bool mMouseLookEnabled; bool mGuiCursorEnabled; @@ -207,6 +208,8 @@ namespace MWInput A_ToggleWalk, //Toggle Walking/Running A_Crouch, + A_TogglePOV, + A_QuickSave, A_QuickLoad, A_QuickMenu, From 1da56e28325d1371648b1e46fcd27fd3f4095b8f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 15:37:14 +0200 Subject: [PATCH 447/688] removed some unused variables --- apps/openmw/mwrender/player.cpp | 1 - apps/openmw/mwworld/physicssystem.cpp | 6 ------ components/nifogre/ogre_nif_loader.cpp | 4 ++-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3bbb56907..953543d99 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -119,7 +119,6 @@ namespace MWRender void Player::update(float duration) { - Ogre::Vector3 pos = mPlayerNode->getPosition(); if (!mVanity.enabled) { ++mUpdates; mTimeIdle += duration; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index dfba26589..b7cb2778d 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -189,12 +189,6 @@ namespace MWWorld iter!=actors.end(); ++iter) { //dirty stuff to get the camera orientation. Must be changed! - Ogre::Quaternion orient = - mRender.getScene()->getSceneNode(iter->first)->getOrientation(); - - float yaw = orient.getRoll().valueDegrees(); - float pitch = 0.f; - if (iter->first == "player") { playerphysics->ps.viewangles.x = Ogre::Radian(mPlayerData.pitch).valueDegrees(); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 4393749fb..0fdeaae3d 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -471,7 +471,7 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam float glossiness = 0.0f; float alpha = 1.0f; int alphaFlags = -1; - ubyte alphaTest = 0; +// ubyte alphaTest = 0; Ogre::String texName; bool vertexColour = (shape->data->colors.size() != 0); @@ -523,7 +523,7 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam if (a) { alphaFlags = a->flags; - alphaTest = a->data.threshold; +// alphaTest = a->data.threshold; } // Material From 3112bfb54f105a4d571f790f06f1499b99e7142d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 15:37:54 +0200 Subject: [PATCH 448/688] incremented version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb98..77efa186c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 16) +set (OPENMW_VERSION_MINOR 17) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") diff --git a/readme.txt b/readme.txt index ce3d115e3..f146de71b 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.16.0 +Version: 0.17.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 296b61f476188925e19620c0add7e38809d66ecd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 17 Aug 2012 15:40:46 +0200 Subject: [PATCH 449/688] updated changelog --- readme.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/readme.txt b/readme.txt index f146de71b..10a0a6f5d 100644 --- a/readme.txt +++ b/readme.txt @@ -97,6 +97,37 @@ Allowed options: CHANGELOG +0.17.0 + +Bug #225: Valgrind reports about 40MB of leaked memory +Bug #241: Some physics meshes still don't match +Bug #248: Some textures are too dark +Bug #300: Dependency on proprietary CG toolkit +Bug #302: Some objects don't collide although they should +Bug #308: Freeze in Balmora, Meldor: Armorer +Bug #313: openmw without a ~/.config/openmw folder segfault. +Bug #317: adding non-existing spell via console locks game +Bug #318: Wrong character normals +Bug #341: Building with Ogre Debug libraries does not use debug version of plugins +Bug #347: Crash when running openmw with --start="XYZ" +Bug #353: FindMyGUI.cmake breaks path on Windows +Bug #359: WindowManager throws exception at destruction +Feature #33: Allow objects to cross cell-borders +Feature #59: Dropping Items (replaced stopgap implementation with a proper one) +Feature #93: Main Menu +Feature #96/329/330/331/332/333: Player Control +Feature #180: Object rotation and scaling. +Feature #272: Incorrect NIF material sharing +Feature #314: Potion usage +Feature #324: Skill Gain +Feature #342: Drain/fortify dynamic stats/attributes magic effects +Feature #350: Allow console only script instructions +Feature #352: Run scripts in console on startup +Task #107: Refactor mw*-subsystems +Task #325: Make CreatureStats into a class +Task #345: Use Ogre's animation system +Task #351: Rewrite Action class to support automatic sound playing + 0.16.0 Bug #250: OpenMW launcher erratic behaviour From 7952d38e6cb154e02203a61cedf178e49374065e Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Tue, 14 Aug 2012 14:45:16 -0400 Subject: [PATCH 450/688] Use debug dlls when debugging in vs2010 (try 2) Using the Debug build in vs2010 is not working because the debug dlls are not loaded when debugging. The reason they are not loaded is that CMAKE_BUILD_TYPE is not defined when doing multiple builds. This in turns causes OGRE_PLUGIN_DEBUG_SUFFIX not to be set. This patch makes sure that OGRE_PLUGIN_DEBUG_SUFFIX is always set but only used when debugging. It also defines DEBUG to make it easier turn things on and off when debugging. There are still other bugs that have broken Debug mode in vs2010 but those will be addressed in other patches. --- CMakeLists.txt | 10 ++++++---- components/files/ogreplugin.cpp | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 543d9cb98..d611e53d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,13 +257,15 @@ if (APPLE) "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) endif (APPLE) +# Set up DEBUG define +set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1) # Set up Ogre plugin folder & debug suffix -# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) -if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") -else() +if (APPLE) + # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") +else () + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index c434114b3..ca90fd30e 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,7 +6,11 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { + // Append plugin suffix if debugging. +#if defined(DEBUG) pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; +#endif + #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; From 8817a9634ccbab8bae0241b064a646304f24b115 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Aug 2012 16:55:21 +0200 Subject: [PATCH 451/688] don't switch to vanity mode as a result of idle time when the gui is opened (e.g. when talking to someone) --- apps/openmw/mwrender/player.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 953543d99..fb8109a1d 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -5,6 +5,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" @@ -119,7 +120,8 @@ namespace MWRender void Player::update(float duration) { - if (!mVanity.enabled) { + bool isGuiMode = MWBase::Environment::get().getWindowManager ()->isGuiMode(); + if (!mVanity.enabled && !isGuiMode) { ++mUpdates; mTimeIdle += duration; if (mTimeIdle > 30.f) { From b6954a4e8d251c7152ddc6278407f4d9c204fd18 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 22:08:16 +0400 Subject: [PATCH 452/688] use the same height on non-1st person modes --- apps/openmw/mwrender/player.cpp | 32 +++++++++++++++++--------------- apps/openmw/mwrender/player.hpp | 5 ++--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3bbb56907..98cb1695e 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -38,9 +37,6 @@ namespace MWRender bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - mUpdates = 0; - mTimeIdle = 0.f; - if (mVanity.enabled) { toggleVanityMode(false); } @@ -120,13 +116,6 @@ namespace MWRender void Player::update(float duration) { Ogre::Vector3 pos = mPlayerNode->getPosition(); - if (!mVanity.enabled) { - ++mUpdates; - mTimeIdle += duration; - if (mTimeIdle > 30.f) { - toggleVanityMode(true); - } - } if (mAnimation) { mAnimation->runAnimation(duration); } @@ -145,10 +134,10 @@ namespace MWRender mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); - mCameraNode->setPosition(0.f, 0.f, 128.f); + setLowHeight(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); - mCameraNode->setPosition(0.f, 0.f, 104.f); + setLowHeight(true); } mPlayerNode->setVisible(!mFirstPersonView, false); } @@ -176,15 +165,17 @@ namespace MWRender float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { - mPlayerNode->setVisible(true, false); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); @@ -201,15 +192,17 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { - mPlayerNode->setVisible(true, false); mMainCam.offset = offset; offset = mPreviewCam.offset; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); @@ -357,4 +350,13 @@ namespace MWRender { mFreeLook = enable; } + + void Player::setLowHeight(bool low) + { + if (low) { + mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); + } else { + mCameraNode->setPosition(0.f, 0.f, mHeight); + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 5bc32fffe..8dd313b7f 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -45,9 +45,6 @@ namespace MWRender bool mDistanceAdjusted; - float mTimeIdle; - int mUpdates; - /// Updates sound manager listener data void updateListener(); @@ -61,6 +58,8 @@ namespace MWRender void compensateYaw(float diff); + void setLowHeight(bool low = true); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); From d2b451eb7d7d9374f36c160e043776808205d2c0 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 23:25:29 +0400 Subject: [PATCH 453/688] entering vanity mode --- apps/openmw/mwbase/inputmanager.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 59 +++++++++++++++++++++---- apps/openmw/mwinput/inputmanagerimp.hpp | 3 +- apps/openmw/mwinput/mouselookevent.cpp | 2 + 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index fdbab9fac..00d7c8125 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -31,6 +31,8 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + + virtual void resetIdleTime() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 0945842b2..c78643f59 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -92,6 +92,7 @@ namespace MWInput std::map mControlSwitch; float mPreviewPOVDelay; + float mTimeIdle; /* InputImpl Methods */ public: @@ -99,11 +100,34 @@ public: { input.adjustMouseClippingSize(width, height); } + + void resetIdleTime() + { + if (mTimeIdle < 0) { + MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); + } + mTimeIdle = 0.f; + } + private: + + void updateIdleTime(float dt) + { + if (mTimeIdle >= 0.f) { + mTimeIdle += dt; + } + if (mTimeIdle > 30.f) { + MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); + mTimeIdle = -1.f; + } + } + void toggleSpell() { if (windows.isGuiMode()) return; + resetIdleTime(); + MWMechanics::DrawState_ state = player.getDrawState(); if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing) { @@ -121,6 +145,8 @@ private: { if (windows.isGuiMode()) return; + resetIdleTime(); + MWMechanics::DrawState_ state = player.getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) { @@ -200,18 +226,26 @@ private: void activate() { + resetIdleTime(); + mEngine.activate(); } void toggleAutoMove() { if (windows.isGuiMode()) return; + + resetIdleTime(); + player.setAutoMove (!player.getAutoMove()); } void toggleWalking() { if (windows.isGuiMode()) return; + + resetIdleTime(); + player.toggleRunning(); } @@ -243,7 +277,8 @@ private: windows(_windows), mEngine (engine), mDragDrop(false), - mPreviewPOVDelay(0.f) + mPreviewPOVDelay(0.f), + mTimeIdle(0.f) { using namespace OEngine::Input; using namespace OEngine::Render; @@ -426,6 +461,19 @@ private: } } } + // Idle time update despite of control switches + if (poller.isDown(A_MoveLeft) || + poller.isDown(A_MoveRight) || + poller.isDown(A_MoveForward) || + poller.isDown(A_MoveBackward) || + poller.isDown(A_Jump) || + poller.isDown(A_Crouch) || + poller.isDown(A_TogglePOV)) + { + resetIdleTime(); + } else { + updateIdleTime(duration); + } } // Switch between gui modes. Besides controlling the Gui windows @@ -472,11 +520,6 @@ private: mControlSwitch[sw] = value; } - bool getControlSwitch(const std::string &sw) - { - return mControlSwitch[sw]; - } - void togglePOV() { MWBase::Environment::get().getWorld()->togglePOV(); @@ -536,8 +579,8 @@ private: impl->toggleControlSwitch(sw, value); } - bool MWInputManager::getControlSwitch(const std::string &sw) + void MWInputManager::resetIdleTime() { - return impl->getControlSwitch(sw); + impl->resetIdleTime(); } } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d4ca32d2f..868565dbb 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -61,7 +61,8 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); void toggleControlSwitch(const std::string &sw, bool value); - bool getControlSwitch(const std::string &sw); + + void resetIdleTime(); }; } #endif diff --git a/apps/openmw/mwinput/mouselookevent.cpp b/apps/openmw/mwinput/mouselookevent.cpp index f318ce666..e6852d923 100644 --- a/apps/openmw/mwinput/mouselookevent.cpp +++ b/apps/openmw/mwinput/mouselookevent.cpp @@ -1,6 +1,7 @@ #include "mouselookevent.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" @@ -17,6 +18,7 @@ void MouseLookEvent::event(Type type, int index, const void *p) if (type != EV_MouseMove || mDisabled) { return; } + MWBase::Environment::get().getInputManager()->resetIdleTime(); MouseEvent *arg = (MouseEvent*)(p); From 3f3972eb3b0e73417de662245a07315b2dc18c19 Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 17 Aug 2012 23:44:28 +0400 Subject: [PATCH 454/688] no vanity in gui mode (thanks scrawl) --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c78643f59..638bc6047 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -116,7 +116,7 @@ private: if (mTimeIdle >= 0.f) { mTimeIdle += dt; } - if (mTimeIdle > 30.f) { + if (mTimeIdle > 30.f && !windows.isGuiMode()) { MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); mTimeIdle = -1.f; } From cc776810e24ed3dd3c1ed7b4ac41a4bff52c46cd Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Fri, 17 Aug 2012 17:10:21 -0400 Subject: [PATCH 455/688] smoother animations --- components/nifogre/ogre_nif_loader.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 74b8ea8f3..07f1bf622 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -274,15 +274,26 @@ void loadResource(Ogre::Resource *resource) if(scaleiter != scalekeys.mKeys.end()) lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale; bool didlast = false; + //The times are right while(!didlast) { float curtime = kfc->timeStop; - if(quatiter != quatkeys.mKeys.end()) + if(quatiter != quatkeys.mKeys.end()){ curtime = std::min(curtime, quatiter->mTime); - if(traniter != trankeys.mKeys.end()) + lastquat = curquat; + curquat = startquat.Inverse() * quatiter->mValue; + + } + if(traniter != trankeys.mKeys.end()){ curtime = std::min(curtime, traniter->mTime); - if(scaleiter != scalekeys.mKeys.end()) + lasttrans = curtrans; + curtrans = traniter->mValue - starttrans; + } + if(scaleiter != scalekeys.mKeys.end()){ curtime = std::min(curtime, scaleiter->mTime); + lastscale = curscale; + curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + } curtime = std::max(curtime, kfc->timeStart); if(curtime >= kfc->timeStop) @@ -295,20 +306,14 @@ void loadResource(Ogre::Resource *resource) // current time while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) { - lastquat = curquat; - curquat = startquat.Inverse() * quatiter->mValue; quatiter++; } while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) { - lasttrans = curtrans; - curtrans = traniter->mValue - starttrans; traniter++; } while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) { - lastscale = curscale; - curscale = Ogre::Vector3(scaleiter->mValue) / startscale; scaleiter++; } @@ -320,7 +325,7 @@ void loadResource(Ogre::Resource *resource) { QuaternionKeyList::VecType::const_iterator last = quatiter-1; float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); - kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); + kframe->setRotation(Ogre::Quaternion::Slerp(diff, lastquat, curquat, true)); } if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin() || (traniter-1)->mTime == curtime) kframe->setTranslate(curtrans); From b8e56d61d3592d8304699dc7d1ded378eeb76220 Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 18 Aug 2012 01:31:57 +0400 Subject: [PATCH 456/688] update camera code, add idle time --- apps/openmw/mwinput/inputmanagerimp.cpp | 40 +++++++++++++++++++++++++ apps/openmw/mwinput/inputmanagerimp.hpp | 6 ++-- apps/openmw/mwrender/player.cpp | 32 ++++++++++---------- apps/openmw/mwrender/player.hpp | 5 ++-- 4 files changed, 63 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 292e4c2e9..e4522e593 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -46,6 +46,7 @@ namespace MWInput , mCameraYMultiplier (Settings::Manager::getFloat("camera y multiplier", "Input")) , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) , mPreviewPOVDelay(0.f) + , mTimeIdle(0.f) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -164,24 +165,30 @@ namespace MWInput toggleConsole (); break; case A_Activate: + resetIdleTime(); activate(); break; case A_Journal: toggleJournal (); break; case A_AutoMove: + resetIdleTime(); toggleAutoMove (); break; case A_ToggleSneak: /// \todo implement + resetIdleTime(); break; case A_ToggleWalk: + resetIdleTime(); toggleWalking (); break; case A_ToggleWeapon: + resetIdleTime(); toggleWeapon (); break; case A_ToggleSpell: + resetIdleTime(); toggleSpell (); break; } @@ -265,6 +272,18 @@ namespace MWInput } } } + if (actionIsActive(A_MoveForward) || + actionIsActive(A_MoveBackward) || + actionIsActive(A_MoveLeft) || + actionIsActive(A_MoveRight) || + actionIsActive(A_Jump) || + actionIsActive(A_Crouch) || + actionIsActive(A_TogglePOV)) + { + resetIdleTime(); + } else { + updateIdleTime(dt); + } } void InputManager::setDragDrop(bool dragDrop) @@ -402,6 +421,8 @@ namespace MWInput if (mMouseLookEnabled) { + resetIdleTime(); + float x = arg.state.X.rel * mCameraSensitivity * 0.2; float y = arg.state.Y.rel * mCameraSensitivity * 0.2 * (mInvertY ? -1 : 1) * mUIYMultiplier; @@ -528,6 +549,25 @@ namespace MWInput Ogre::Root::getSingleton().queueEndRendering (); } + void InputManager::resetIdleTime() + { + if (mTimeIdle < 0) { + MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); + } + mTimeIdle = 0.f; + } + + void InputManager::updateIdleTime(float dt) + { + if (mTimeIdle >= 0.f) { + mTimeIdle += dt; + } + if (mTimeIdle > 30.f) { + MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); + mTimeIdle = -1.f; + } + } + bool InputManager::actionIsActive (int id) { return mInputCtrl->getChannel (id)->getValue () == 1; diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d199344a3..d1fb472c9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -83,7 +83,6 @@ namespace MWInput virtual void enableDetectingBindingMode (int action); virtual void resetToDefaultBindings(); - public: virtual bool keyPressed( const OIS::KeyEvent &arg ); virtual bool keyReleased( const OIS::KeyEvent &arg ); @@ -140,6 +139,7 @@ namespace MWInput float mCameraYMultiplier; float mUIYMultiplier; float mPreviewPOVDelay; + float mTimeIdle; bool mMouseLookEnabled; bool mGuiCursorEnabled; @@ -149,10 +149,12 @@ namespace MWInput std::map mControlSwitch; - private: void adjustMouseRegion(int width, int height); + void resetIdleTime(); + void updateIdleTime(float dt); + private: void toggleMainMenu(); void toggleSpell(); diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3bbb56907..98cb1695e 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -2,7 +2,6 @@ #include #include -#include #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" @@ -38,9 +37,6 @@ namespace MWRender bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { - mUpdates = 0; - mTimeIdle = 0.f; - if (mVanity.enabled) { toggleVanityMode(false); } @@ -120,13 +116,6 @@ namespace MWRender void Player::update(float duration) { Ogre::Vector3 pos = mPlayerNode->getPosition(); - if (!mVanity.enabled) { - ++mUpdates; - mTimeIdle += duration; - if (mTimeIdle > 30.f) { - toggleVanityMode(true); - } - } if (mAnimation) { mAnimation->runAnimation(duration); } @@ -145,10 +134,10 @@ namespace MWRender mFirstPersonView = !mFirstPersonView; if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); - mCameraNode->setPosition(0.f, 0.f, 128.f); + setLowHeight(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); - mCameraNode->setPosition(0.f, 0.f, 104.f); + setLowHeight(true); } mPlayerNode->setVisible(!mFirstPersonView, false); } @@ -176,15 +165,17 @@ namespace MWRender float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { - mPlayerNode->setVisible(true, false); rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } rot.z = getYaw(); mCamera->setPosition(0.f, 0.f, offset); @@ -201,15 +192,17 @@ namespace MWRender mPreviewMode = enable; float offset = mCamera->getPosition().z; if (mPreviewMode) { - mPlayerNode->setVisible(true, false); mMainCam.offset = offset; offset = mPreviewCam.offset; + mPlayerNode->setVisible(true, false); + setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; mPlayerNode->setVisible(!mFirstPersonView, false); + setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); @@ -357,4 +350,13 @@ namespace MWRender { mFreeLook = enable; } + + void Player::setLowHeight(bool low) + { + if (low) { + mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); + } else { + mCameraNode->setPosition(0.f, 0.f, mHeight); + } + } } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 5bc32fffe..8dd313b7f 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -45,9 +45,6 @@ namespace MWRender bool mDistanceAdjusted; - float mTimeIdle; - int mUpdates; - /// Updates sound manager listener data void updateListener(); @@ -61,6 +58,8 @@ namespace MWRender void compensateYaw(float diff); + void setLowHeight(bool low = true); + public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); From f2d080d0913a2ebcddce5d2740658d908bb9cf5a Mon Sep 17 00:00:00 2001 From: gugus Date: Sat, 18 Aug 2012 10:50:58 +0200 Subject: [PATCH 457/688] bugfix --- .../mwscript/transformationextensions.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 3f1cff7e7..18054374c 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -185,7 +185,7 @@ namespace MWScript float ax = ptr.getRefData().getPosition().pos[0]; float ay = ptr.getRefData().getPosition().pos[1]; float az = ptr.getRefData().getPosition().pos[2]; - std::cout << "setPos"; + if(axis == "x") { @@ -252,28 +252,32 @@ namespace MWScript MWWorld::CellStore* store = 0; try { - MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getInterior(cellID); + store = MWBase::Environment::get().getWorld()->getInterior(cellID); } catch(std::exception &e) { - const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); + /*const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); if(cell) { store = MWBase::Environment::get().getWorld()->getExterior(cell->getGridX(),cell->getGridY()); - } + }*/ } if(store) { MWBase::Environment::get().getWorld()->moveObject(ptr,*store,x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); - if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity + if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity { ax = ax/60.; ay = ay/60.; zRot = zRot/60.; } - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + //MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + } + else + { + throw std::runtime_error ("unknown cell"); } } }; @@ -301,7 +305,7 @@ namespace MWScript *MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); - if(ptr.getTypeName() == "struct ESM::NPC")//some morrowind oddity + if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity { ax = ax/60.; ay = ay/60.; From 5b2b378f25f4c94d7885879899508cea70df140e Mon Sep 17 00:00:00 2001 From: greye Date: Sat, 18 Aug 2012 18:05:10 +0400 Subject: [PATCH 458/688] tvm script instruction --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwrender/player.hpp | 4 ++++ apps/openmw/mwrender/renderingmanager.hpp | 10 ++++++--- apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 27 +++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9c801cbff..5e97b4922 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -254,6 +254,7 @@ namespace MWBase virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; + virtual bool isVanityEnabled() = 0; virtual void renderPlayer() = 0; }; diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 8dd313b7f..e56abc17d 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -107,6 +107,10 @@ namespace MWRender void getSightAngles(float &pitch, float &yaw); void togglePlayerLooking(bool enable); + + bool isVanityEnabled() { + return mVanity.enabled; + } }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 17a4d5280..003a61a39 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -64,15 +64,19 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList mPlayer->togglePreviewMode(enable); } - virtual bool toggleVanityMode(bool enable, bool force) { + bool toggleVanityMode(bool enable, bool force) { return mPlayer->toggleVanityMode(enable, force); } - virtual void allowVanityMode(bool allow) { + bool isVanityEnabled() { + return mPlayer->isVanityEnabled(); + } + + void allowVanityMode(bool allow) { mPlayer->allowVanityMode(allow); } - virtual void togglePlayerLooking(bool enable) { + void togglePlayerLooking(bool enable) { mPlayer->togglePlayerLooking(enable); } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 1ea5f8e3e..dbc8159d1 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -181,4 +181,5 @@ op 0x2000170: user4, explicit reference (console only, requires --script-console op 0x2000171: user4 (implicit reference, console only, requires --script-console switch) op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference -opcodes 0x2000174-0x3ffffff unused +op 0x2000174: ToggleVanityMode +opcodes 0x2000175-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 0c328e0da..e9e29bdae 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -207,6 +207,29 @@ namespace MWScript } }; + class OpToggleVanityMode : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime &runtime) + { + InterpreterContext& context = + static_cast (runtime.getContext()); + + MWBase::World *world = + MWBase::Environment::get().getWorld(); + + bool value = !world->isVanityEnabled(); + if (world->toggleVanityMode(value, true)) { + context.report( + (value) ? "Vanity Mode -> On" : "Vanity Mode -> Off" + ); + } else { + context.report("Vanity Mode -> No"); + } + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -222,6 +245,7 @@ namespace MWScript const int opcodeToggleWater = 0x2000144; const int opcodeTogglePathgrid = 0x2000146; const int opcodeDontSaveObject = 0x2000153; + const int opcodeToggleVanityMode = 0x2000174; void registerExtensions (Compiler::Extensions& extensions) { @@ -244,6 +268,8 @@ namespace MWScript extensions.registerInstruction ("togglepathgrid", "", opcodeTogglePathgrid); extensions.registerInstruction ("tpg", "", opcodeTogglePathgrid); extensions.registerInstruction ("dontsaveobject", "", opcodeDontSaveObject); + extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); + extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -263,6 +289,7 @@ namespace MWScript interpreter.installSegment5 (opcodeTogglePathgrid, new OpTogglePathgrid); interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater); interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject); + interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); } } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 51216e3a5..889741989 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -298,6 +298,10 @@ namespace MWWorld } virtual void renderPlayer(); + + virtual bool isVanityEnabled() { + return mRendering->isVanityEnabled(); + } }; } From 609a45258bf6d89a8c90dbfc5aafb0a4eeb385cd Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 18 Aug 2012 21:26:57 +0400 Subject: [PATCH 459/688] updated changelog with newly created bug for laggy input on OS X --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index 10a0a6f5d..a254e2672 100644 --- a/readme.txt +++ b/readme.txt @@ -112,6 +112,7 @@ Bug #341: Building with Ogre Debug libraries does not use debug version of plugi Bug #347: Crash when running openmw with --start="XYZ" Bug #353: FindMyGUI.cmake breaks path on Windows Bug #359: WindowManager throws exception at destruction +Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation Feature #33: Allow objects to cross cell-borders Feature #59: Dropping Items (replaced stopgap implementation with a proper one) Feature #93: Main Menu From f21df64808da8d6028e62119450d8428d302e644 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 18 Aug 2012 14:48:18 -0400 Subject: [PATCH 460/688] changing a few things around --- components/nifogre/ogre_nif_loader.cpp | 69 +++++++++++++++----------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 07f1bf622..a0c64398a 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -274,14 +274,18 @@ void loadResource(Ogre::Resource *resource) if(scaleiter != scalekeys.mKeys.end()) lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale; bool didlast = false; - //The times are right + while(!didlast) { float curtime = kfc->timeStop; + // Get the latest quaternion, translation, and scale for the + // current time + + //Get latest time if(quatiter != quatkeys.mKeys.end()){ curtime = std::min(curtime, quatiter->mTime); lastquat = curquat; - curquat = startquat.Inverse() * quatiter->mValue; + curquat = startquat.Inverse() * quatiter->mValue ; } if(traniter != trankeys.mKeys.end()){ @@ -302,8 +306,39 @@ void loadResource(Ogre::Resource *resource) curtime = kfc->timeStop; } - // Get the latest quaternion, translation, and scale for the - // current time + bool rinterpolate = quatiter != quatkeys.mKeys.end() && quatiter != quatkeys.mKeys.begin() && curtime != quatiter->mTime; + bool tinterpolate = traniter != trankeys.mKeys.end() && traniter != trankeys.mKeys.begin() && curtime != traniter->mTime; + bool sinterpolate = scaleiter != scalekeys.mKeys.end() && scaleiter != scalekeys.mKeys.begin() && curtime != scaleiter->mTime; + + + + Ogre::TransformKeyFrame *kframe; + kframe = nodetrack->createNodeKeyFrame(curtime); + if(!rinterpolate) + kframe->setRotation(curquat); + else + { + QuaternionKeyList::VecType::const_iterator last = quatiter-1; + float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); + kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); + } + if(!tinterpolate) + kframe->setTranslate(curtrans); + else + { + Vector3KeyList::VecType::const_iterator last = traniter-1; + float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); + kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); + } + if(!sinterpolate) + kframe->setScale(curscale); + else + { + FloatKeyList::VecType::const_iterator last = scaleiter-1; + float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); + kframe->setScale(lastscale + ((curscale-lastscale)*diff)); + } + while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) { quatiter++; @@ -317,32 +352,6 @@ void loadResource(Ogre::Resource *resource) scaleiter++; } - Ogre::TransformKeyFrame *kframe; - kframe = nodetrack->createNodeKeyFrame(curtime); - if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin() || (quatiter-1)->mTime == curtime) - kframe->setRotation(curquat); - else - { - QuaternionKeyList::VecType::const_iterator last = quatiter-1; - float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); - kframe->setRotation(Ogre::Quaternion::Slerp(diff, lastquat, curquat, true)); - } - if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin() || (traniter-1)->mTime == curtime) - kframe->setTranslate(curtrans); - else - { - Vector3KeyList::VecType::const_iterator last = traniter-1; - float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); - kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); - } - if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin() || (scaleiter-1)->mTime == curtime) - kframe->setScale(curscale); - else - { - FloatKeyList::VecType::const_iterator last = scaleiter-1; - float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); - kframe->setScale(lastscale + ((curscale-lastscale)*diff)); - } } } anim->optimise(); From 4a219404fc48aac4fafe6c1afa934f452b1751c8 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 18 Aug 2012 15:21:43 -0400 Subject: [PATCH 461/688] Super smooth animations; 100 PERCENT COMPLETE --- components/nifogre/ogre_nif_loader.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index a0c64398a..2557a260a 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -284,19 +284,13 @@ void loadResource(Ogre::Resource *resource) //Get latest time if(quatiter != quatkeys.mKeys.end()){ curtime = std::min(curtime, quatiter->mTime); - lastquat = curquat; - curquat = startquat.Inverse() * quatiter->mValue ; - } if(traniter != trankeys.mKeys.end()){ curtime = std::min(curtime, traniter->mTime); - lasttrans = curtrans; - curtrans = traniter->mValue - starttrans; + } if(scaleiter != scalekeys.mKeys.end()){ curtime = std::min(curtime, scaleiter->mTime); - lastscale = curscale; - curscale = Ogre::Vector3(scaleiter->mValue) / startscale; } curtime = std::max(curtime, kfc->timeStart); @@ -342,14 +336,23 @@ void loadResource(Ogre::Resource *resource) while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) { quatiter++; + lastquat = curquat; + if(quatiter != quatkeys.mKeys.end()) + curquat = startquat.Inverse() * quatiter->mValue ; } while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) { traniter++; + lasttrans = curtrans; + if(traniter != trankeys.mKeys.end()) + curtrans = traniter->mValue - starttrans; } while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) { scaleiter++; + lastscale = curscale; + if(scaleiter != scalekeys.mKeys.end()) + curscale = Ogre::Vector3(scaleiter->mValue) / startscale; } } From 5202b11235cb6a95b9e1467704ed1272f6e29d24 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 18 Aug 2012 15:31:54 -0400 Subject: [PATCH 462/688] just moving a comment --- components/nifogre/ogre_nif_loader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 2557a260a..660ca09ae 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -278,8 +278,7 @@ void loadResource(Ogre::Resource *resource) while(!didlast) { float curtime = kfc->timeStop; - // Get the latest quaternion, translation, and scale for the - // current time + //Get latest time if(quatiter != quatkeys.mKeys.end()){ @@ -333,6 +332,8 @@ void loadResource(Ogre::Resource *resource) kframe->setScale(lastscale + ((curscale-lastscale)*diff)); } + // Get the latest quaternion, translation, and scale for the + // current time while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) { quatiter++; From e6c3e0744efe94086ffed6515f75bf4c4dcfa8a9 Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 19 Aug 2012 10:37:51 +0400 Subject: [PATCH 463/688] tvm update and fix --- apps/openmw/mwbase/world.hpp | 1 - apps/openmw/mwrender/player.hpp | 4 ---- apps/openmw/mwrender/renderingmanager.hpp | 4 ---- apps/openmw/mwscript/miscextensions.cpp | 9 ++++++--- apps/openmw/mwworld/worldimp.hpp | 4 ---- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 5e97b4922..9c801cbff 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -254,7 +254,6 @@ namespace MWBase virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual void allowVanityMode(bool allow) = 0; virtual void togglePlayerLooking(bool enable) = 0; - virtual bool isVanityEnabled() = 0; virtual void renderPlayer() = 0; }; diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index e56abc17d..8dd313b7f 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -107,10 +107,6 @@ namespace MWRender void getSightAngles(float &pitch, float &yaw); void togglePlayerLooking(bool enable); - - bool isVanityEnabled() { - return mVanity.enabled; - } }; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 003a61a39..b6bfcbf97 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -68,10 +68,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList return mPlayer->toggleVanityMode(enable, force); } - bool isVanityEnabled() { - return mPlayer->isVanityEnabled(); - } - void allowVanityMode(bool allow) { mPlayer->allowVanityMode(allow); } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index e9e29bdae..864c1a1ee 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -209,6 +209,8 @@ namespace MWScript class OpToggleVanityMode : public Interpreter::Opcode0 { + static bool sActivate; + public: virtual void execute(Interpreter::Runtime &runtime) @@ -219,16 +221,17 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); - bool value = !world->isVanityEnabled(); - if (world->toggleVanityMode(value, true)) { + if (world->toggleVanityMode(sActivate, true)) { context.report( - (value) ? "Vanity Mode -> On" : "Vanity Mode -> Off" + (sActivate) ? "Vanity Mode -> On" : "Vanity Mode -> Off" ); + sActivate = !sActivate; } else { context.report("Vanity Mode -> No"); } } }; + bool OpToggleVanityMode::sActivate = true; const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 889741989..51216e3a5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -298,10 +298,6 @@ namespace MWWorld } virtual void renderPlayer(); - - virtual bool isVanityEnabled() { - return mRendering->isVanityEnabled(); - } }; } From 34513cf16b955be45361304f89dd76f969ef1a62 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 19 Aug 2012 22:23:46 +0300 Subject: [PATCH 464/688] Added support for code coverage checking and unittests. Added support for code coverage checking with gcov and unittests with Google C++ unittest and GMock frameworks. Signed-off-by: Lukasz Gromanowski --- CMakeLists.txt | 7 ++ apps/esmtool/CMakeLists.txt | 5 + apps/launcher/CMakeLists.txt | 5 + apps/mwiniimporter/CMakeLists.txt | 4 + apps/openmw/CMakeLists.txt | 5 + apps/openmw_test_suite/CMakeLists.txt | 24 +++++ .../components/misc/test_stringops.cpp | 7 ++ apps/openmw_test_suite/openmw_test_suite.cpp | 12 +++ cmake/FindGMock.cmake | 91 +++++++++++++++++++ 9 files changed, 160 insertions(+) create mode 100644 apps/openmw_test_suite/CMakeLists.txt create mode 100644 apps/openmw_test_suite/components/misc/test_stringops.cpp create mode 100644 apps/openmw_test_suite/openmw_test_suite.cpp create mode 100644 cmake/FindGMock.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ab235de24..df05b72e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) +option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) +option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF) # Sound source selection option(USE_FFMPEG "use ffmpeg for sound" OFF) @@ -464,6 +466,11 @@ if (BUILD_MWINIIMPORTER) add_subdirectory( apps/mwiniimporter ) endif() +# UnitTests +if (BUILD_UNITTESTS) + add_subdirectory( apps/openmw_test_suite ) +endif() + if (WIN32) if (MSVC) if (USE_DEBUG_CONSOLE) diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index af3dc090e..bd397d554 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -17,3 +17,8 @@ target_link_libraries(esmtool # find_library(CARBON_FRAMEWORK Carbon) # target_link_libraries(openmw ${CARBON_FRAMEWORK}) #endif (APPLE) + +if (BUILD_WITH_CODE_COVERAGE) + add_definitions (--coverage) + target_link_libraries(esmtool gcov) +endif() diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index ed3559fdc..2bc43db80 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -100,3 +100,8 @@ else() configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg") endif() + +if (BUILD_WITH_CODE_COVERAGE) + add_definitions (--coverage) + target_link_libraries(omwlauncher gcov) +endif() diff --git a/apps/mwiniimporter/CMakeLists.txt b/apps/mwiniimporter/CMakeLists.txt index 2a8c0f5fe..deab88ce2 100644 --- a/apps/mwiniimporter/CMakeLists.txt +++ b/apps/mwiniimporter/CMakeLists.txt @@ -18,3 +18,7 @@ target_link_libraries(mwiniimport components ) +if (BUILD_WITH_CODE_COVERAGE) + add_definitions (--coverage) + target_link_libraries(mwiniimport gcov) +endif() diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 02fe0b72c..326b4d35f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -120,3 +120,8 @@ endif(APPLE) if(DPKG_PROGRAM) INSTALL(TARGETS openmw RUNTIME DESTINATION games COMPONENT openmw) endif(DPKG_PROGRAM) + +if (BUILD_WITH_CODE_COVERAGE) + add_definitions (--coverage) + target_link_libraries(openmw gcov) +endif() diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt new file mode 100644 index 000000000..afe0f30da --- /dev/null +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -0,0 +1,24 @@ +# TODO: This should not be needed, check how it was done in FindGTEST +set(GMOCK_ROOT "/usr/include") +set(GMOCK_BUILD "/usr/lib") + +find_package(GTest REQUIRED) +find_package(GMock REQUIRED) + +if (GTEST_FOUND AND GMOCK_FOUND) + + include_directories(${GTEST_INCLUDE_DIRS}) + include_directories(${GMOCK_INCLUDE_DIRS}) + + file(GLOB UNITTEST_SRC_FILES + components/misc/*.cpp + ) + + source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) + + add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) + + target_link_libraries(openmw_test_suite ${GMOCK_BOTH_LIBRARIES} ${GTEST_BOTH_LIBRARIES}) +endif() + + diff --git a/apps/openmw_test_suite/components/misc/test_stringops.cpp b/apps/openmw_test_suite/components/misc/test_stringops.cpp new file mode 100644 index 000000000..b12584196 --- /dev/null +++ b/apps/openmw_test_suite/components/misc/test_stringops.cpp @@ -0,0 +1,7 @@ +#include + +TEST(simple_test, dummy) +{ + EXPECT_EQ(true, true); +} + diff --git a/apps/openmw_test_suite/openmw_test_suite.cpp b/apps/openmw_test_suite/openmw_test_suite.cpp new file mode 100644 index 000000000..0a09b3028 --- /dev/null +++ b/apps/openmw_test_suite/openmw_test_suite.cpp @@ -0,0 +1,12 @@ +#include +#include + + +int main(int argc, char** argv) { + // The following line causes Google Mock to throw an exception on failure, + // which will be interpreted by your testing framework as a test failure. + ::testing::GTEST_FLAG(throw_on_failure) = true; + ::testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/cmake/FindGMock.cmake b/cmake/FindGMock.cmake new file mode 100644 index 000000000..eda7d4d72 --- /dev/null +++ b/cmake/FindGMock.cmake @@ -0,0 +1,91 @@ +# Locate the Google C++ Mocking Framework. +# +# Defines the following variables: +# +# GMOCK_FOUND - Found the Google Mocking framework +# GMOCK_INCLUDE_DIRS - Include directories +# +# Also defines the library variables below as normal +# variables. These contain debug/optimized keywords when +# a debugging library is found. +# +# GMOCK_BOTH_LIBRARIES - Both libgmock & libgmock-main +# GMOCK_LIBRARIES - libgmock +# GMOCK_MAIN_LIBRARIES - libgmock-main +# +# Accepts the following variables as input: +# +# GMOCK_ROOT - (as CMake or env. variable) +# The root directory of the gmock install prefix +# +#----------------------- +# Example Usage: +# +# enable_testing(true) +# find_package(GMock REQUIRED) +# include_directories(${GMOCK_INCLUDE_DIRS}) +# +# add_executable(foo foo.cc) +# target_link_libraries(foo ${GMOCK_BOTH_LIBRARIES}) +# +# add_test(AllTestsInFoo foo) +# + +#set (GMOCK_FOUND FALSE) + + +#set (GMOCK_ROOT $ENV{GMOCK_ROOT} CACHE PATH "Path to the gmock root directory.") +if (NOT EXISTS ${GMOCK_ROOT}) + message (FATAL_ERROR "GMOCK_ROOT does not exist.") +endif () + +#set (GMOCK_BUILD ${GMOCK_ROOT}/build CACHE PATH "Path to the gmock build directory.") +if (NOT EXISTS ${GMOCK_BUILD}) + message (FATAL_ERROR "GMOCK_BUILD does not exist.") +endif () + +# Find the include directory +find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h + HINTS + $ENV{GMOCK_ROOT}/include + ${GMOCK_ROOT}/include +) +mark_as_advanced(GMOCK_INCLUDE_DIRS) + +function(_gmock_find_library _name) + find_library(${_name} + NAMES ${ARGN} + HINTS + $ENV{GMOCK_BUILD} + ${GMOCK_BUILD} + ) + mark_as_advanced(${_name}) +endfunction() + +# Find the gmock libraries +if (MSVC) + _gmock_find_library (GMOCK_LIBRARIES_DEBUG gmock ${GMOCK_BUILD}/Debug) + _gmock_find_library (GMOCK_LIBRARIES_RELEASE gmock ${GMOCK_BUILD}/Release) + _gmock_find_library (GMOCK_MAIN_LIBRARIES_DEBUG gmock_main ${GMOCK_BUILD}/Debug) + _gmock_find_library (GMOCK_MAIN_LIBRARIES_RELEASE gmock_main ${GMOCK_BUILD}/Release) + set (GMOCK_LIBRARIES + debug ${GMOCK_LIBRARIES_DEBUG} + optimized ${GMOCK_LIBRARIES_RELEASE} + ) + set (GMOCK_MAIN_LIBRARIES + debug ${GMOCK_MAIN_LIBRARIES_DEBUG} + optimized ${GMOCK_MAIN_LIBRARIES_RELEASE} + ) +else () + _gmock_find_library (GMOCK_LIBRARIES gmock ${GMOCK_BUILD}) + _gmock_find_library (GMOCK_MAIN_LIBRARIES gmock_main ${GMOCK_BUILD} ${GMOCK_BUILD}/Debug) +endif () + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMock DEFAULT_MSG GMOCK_LIBRARIES GMOCK_INCLUDE_DIRS GMOCK_MAIN_LIBRARIES) + +if(GMOCK_FOUND) + set(GMOCK_INCLUDE_DIRS ${GMOCK_INCLUDE_DIR}) + set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARIES} ${GMOCK_MAIN_LIBRARIES}) +endif() + + From f7d537cb2838772c67724a20e64ed30ca7091f8a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Aug 2012 22:15:31 +0200 Subject: [PATCH 465/688] very small correction --- apps/openmw/mwinput/inputmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 177e90b95..a7cce25c0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -275,7 +275,8 @@ namespace MWInput || actionIsActive(A_MoveForward) || actionIsActive(A_MoveBackward) || actionIsActive(A_Jump) - || actionIsActive(A_Crouch)) + || actionIsActive(A_Crouch) + || actionIsActive(A_TogglePOV)) { resetIdleTime (); From 770f747eec67377eb943b5e7d0eb35da7dd5e29e Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 20 Aug 2012 00:18:40 +0300 Subject: [PATCH 466/688] Added fix for not visible pthreads functions for linker with glibc 2.15 Signed-off-by: Lukasz Gromanowski --- apps/openmw_test_suite/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index afe0f30da..cdf8bc74b 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -19,6 +19,10 @@ if (GTEST_FOUND AND GMOCK_FOUND) add_executable(openmw_test_suite openmw_test_suite.cpp ${UNITTEST_SRC_FILES}) target_link_libraries(openmw_test_suite ${GMOCK_BOTH_LIBRARIES} ${GTEST_BOTH_LIBRARIES}) + # Fix for not visible pthreads functions for linker with glibc 2.15 + if (UNIX AND NOT APPLE) + target_link_libraries(openmw_test_suite ${CMAKE_THREAD_LIBS_INIT}) + endif() endif() From 82e7c04c0a267549aac3a380e4d45d81d3d8f8ab Mon Sep 17 00:00:00 2001 From: Douglas Diniz Date: Sun, 19 Aug 2012 20:11:50 -0300 Subject: [PATCH 467/688] Changing sounds to Action. Adding onActor to Action to choose between playSound and playSound3D. --- apps/openmw/mwclass/apparatus.cpp | 9 ++++---- apps/openmw/mwclass/armor.cpp | 14 +++++++----- apps/openmw/mwclass/clothing.cpp | 16 +++++++------ apps/openmw/mwclass/container.cpp | 11 ++++----- apps/openmw/mwclass/door.cpp | 36 +++++++++++++++++++++--------- apps/openmw/mwclass/ingredient.cpp | 8 +++---- apps/openmw/mwclass/light.cpp | 13 ++++++----- apps/openmw/mwclass/lockpick.cpp | 14 +++++++----- apps/openmw/mwclass/misc.cpp | 8 +++---- apps/openmw/mwclass/potion.cpp | 9 ++++---- apps/openmw/mwclass/probe.cpp | 14 +++++++----- apps/openmw/mwclass/repair.cpp | 8 +++---- apps/openmw/mwclass/weapon.cpp | 14 +++++++----- apps/openmw/mwworld/action.cpp | 19 +++++++++++++--- apps/openmw/mwworld/action.hpp | 3 ++- 15 files changed, 121 insertions(+), 75 deletions(-) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 8a117af5d..3d362e8c8 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -63,10 +62,12 @@ namespace MWClass boost::shared_ptr Apparatus::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action( + new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Apparatus::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index c48d7ff4d..f1400dc01 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -66,10 +65,11 @@ namespace MWClass boost::shared_ptr Armor::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } bool Armor::hasItemHealth (const MWWorld::Ptr& ptr) const @@ -271,9 +271,11 @@ namespace MWClass boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 597eb22fe..21069e667 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -62,12 +61,13 @@ namespace MWClass } boost::shared_ptr Clothing::activate (const MWWorld::Ptr& ptr, - const MWWorld::Ptr& actor) const + const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Clothing::getScript (const MWWorld::Ptr& ptr) const @@ -222,9 +222,11 @@ namespace MWClass boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index ad6da90dd..608fc6122 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -93,8 +92,9 @@ namespace MWClass { // TODO check for key std::cout << "Locked container" << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, lockedSound, 1.0, 1.0); - return boost::shared_ptr (new MWWorld::NullAction); + boost::shared_ptr action(new MWWorld::NullAction); + action->setSound(lockedSound); + return action; } else { @@ -109,9 +109,10 @@ namespace MWClass { // Trap activation goes here std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, trapActivationSound, 1.0, 1.0); + boost::shared_ptr action(new MWWorld::NullAction); + action->setSound(trapActivationSound); ptr.getCellRef().trap = ""; - return boost::shared_ptr (new MWWorld::NullAction); + return action; } } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 3b283a9d1..bf5dd4bba 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -5,8 +5,9 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" + #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -80,17 +81,25 @@ namespace MWClass // TODO check for key // TODO report failure to player (message, sound?). Look up behaviour of original MW. std::cout << "Locked!" << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, lockedSound, 1.0, 1.0); - return boost::shared_ptr (new MWWorld::NullAction); + + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(lockedSound); + + return action; } if(!ptr.getCellRef().trap.empty()) { // Trap activation std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - MWBase::Environment::get().getSoundManager()->playSound3D(ptr, trapActivationSound, 1.0, 1.0); + + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(trapActivationSound); ptr.getCellRef().trap = ""; - return boost::shared_ptr (new MWWorld::NullAction); + + return action; } if (ref->ref.teleport) @@ -101,9 +110,13 @@ namespace MWClass { // the player is using the door // The reason this is not 3D is that it would get interrupted when you teleport - MWBase::Environment::get().getSoundManager()->playSound(openSound, 1.0, 1.0); - return boost::shared_ptr ( - new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); + //MWBase::Environment::get().getSoundManager()->playSound3D(ptr,openSound, 1.0, 1.0); + + boost::shared_ptr action(new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); + + action->setSound(openSound, true); + + return action; } else { @@ -117,8 +130,11 @@ namespace MWClass // TODO return action for rotating the door // This is a little pointless, but helps with testing - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, openSound, 1.0, 1.0); - return boost::shared_ptr (new MWWorld::NullAction); + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(openSound); + + return action; } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 8f9f8a315..f0354de76 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -62,10 +61,11 @@ namespace MWClass boost::shared_ptr Ingredient::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Ingredient::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 40ecf0a0f..570221503 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -94,10 +94,11 @@ namespace MWClass if (!(ref->base->data.flags & ESM::Light::Carry)) return boost::shared_ptr (new MWWorld::NullAction); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Light::getScript (const MWWorld::Ptr& ptr) const @@ -191,9 +192,11 @@ namespace MWClass boost::shared_ptr Light::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 8fdd95760..44498e479 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -64,10 +63,11 @@ namespace MWClass boost::shared_ptr Lockpick::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Lockpick::getScript (const MWWorld::Ptr& ptr) const @@ -160,9 +160,11 @@ namespace MWClass boost::shared_ptr Lockpick::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 2cd0f63a1..eb44b8103 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -7,7 +7,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -67,10 +66,11 @@ namespace MWClass boost::shared_ptr Miscellaneous::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 6bff85553..8fef163b1 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -64,10 +63,12 @@ namespace MWClass boost::shared_ptr Potion::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action( + new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound (getUpSoundId(ptr)); + + return action; } std::string Potion::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 25aae445b..73258e528 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -63,10 +62,11 @@ namespace MWClass boost::shared_ptr Probe::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Probe::getScript (const MWWorld::Ptr& ptr) const @@ -159,9 +159,11 @@ namespace MWClass boost::shared_ptr Probe::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index ebba8c44e..a4240d0c4 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -62,10 +61,11 @@ namespace MWClass boost::shared_ptr Repair::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } std::string Repair::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 2a9863cdf..b2397f4af 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -5,7 +5,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" @@ -64,10 +63,11 @@ namespace MWClass boost::shared_ptr Weapon::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); + boost::shared_ptr action(new MWWorld::ActionTake (ptr)); - return boost::shared_ptr ( - new MWWorld::ActionTake (ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } bool Weapon::hasItemHealth (const MWWorld::Ptr& ptr) const @@ -360,9 +360,11 @@ namespace MWClass boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const { - MWBase::Environment::get().getSoundManager()->playSound (getUpSoundId(ptr), 1.0, 1.0); + boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); - return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); + action->setSound(getUpSoundId(ptr)); + + return action; } MWWorld::Ptr diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index c142294bb..15bae4cd4 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -11,13 +11,26 @@ MWWorld::Action::~Action() {} void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) - MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); + { + if (onActor) + { + std::cout << "Douglas - Som Normal" << std::endl; + MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } + else + { + std::cout << "Douglas - Som 3D" << std::endl; + MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } + } executeImp (actor); } -void MWWorld::Action::setSound (const std::string& id) +void MWWorld::Action::setSound (const std::string& id, const bool onActorValue) { mSoundId = id; + onActor = onActorValue; } diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index a00f67951..f9ad975d3 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -11,6 +11,7 @@ namespace MWWorld class Action { std::string mSoundId; + bool onActor; // not implemented Action (const Action& action); @@ -26,7 +27,7 @@ namespace MWWorld void execute (const Ptr& actor); - void setSound (const std::string& id); + void setSound (const std::string& id, const bool onActor = false); }; } From 5395721c264c0179ec29c6d8b2678d4ef3ce72d0 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Mon, 20 Aug 2012 21:05:02 +0400 Subject: [PATCH 468/688] more appropriate fix for #189 --- apps/openmw/CMakeLists.txt | 4 +++- apps/openmw/mwinput/inputmanagerimp.cpp | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 1b4ae209d..4741ba5dd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -114,7 +114,9 @@ endif() if(APPLE) find_library(CARBON_FRAMEWORK Carbon) - target_link_libraries(openmw ${CARBON_FRAMEWORK}) + find_library(COCOA_FRAMEWORK Cocoa) + find_library(IOKIT_FRAMEWORK IOKit) + target_link_libraries(openmw ${CARBON_FRAMEWORK} ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK}) endif(APPLE) if(DPKG_PROGRAM) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index a7cce25c0..1aa4ceeff 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -387,9 +387,18 @@ namespace MWInput bool InputManager::keyPressed( const OIS::KeyEvent &arg ) { - mInputCtrl->keyPressed (arg); + std::cout << "text received: " << arg.text << std::endl; - MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text); + mInputCtrl->keyPressed (arg); + unsigned int text = arg.text; + #ifdef __APPLE__ // filter \016 symbol for F-keys on OS X + if ((arg.key >= OIS::KC_F1 && arg.key <= OIS::KC_F10) || + (arg.key >= OIS::KC_F11 && arg.key <= OIS::KC_F15)) { + text = 0; + } + #endif + + MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), text); return true; } From 71a6ce2202b395dd08c87888d11ce7cad33dd3e2 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Mon, 20 Aug 2012 21:14:29 +0400 Subject: [PATCH 469/688] removed cout spam --- apps/openmw/mwinput/inputmanagerimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 1aa4ceeff..d1f02aaf0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -387,16 +387,14 @@ namespace MWInput bool InputManager::keyPressed( const OIS::KeyEvent &arg ) { - std::cout << "text received: " << arg.text << std::endl; - mInputCtrl->keyPressed (arg); unsigned int text = arg.text; - #ifdef __APPLE__ // filter \016 symbol for F-keys on OS X +#ifdef __APPLE__ // filter \016 symbol for F-keys on OS X if ((arg.key >= OIS::KC_F1 && arg.key <= OIS::KC_F10) || (arg.key >= OIS::KC_F11 && arg.key <= OIS::KC_F15)) { text = 0; } - #endif +#endif MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), text); From 165065d37891be204f67f9cc89de3780a0982b92 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 21 Aug 2012 19:54:42 +0200 Subject: [PATCH 470/688] fix a bug with case sensitivity: when searching for a cell which is already loaded,but with another case, the cell get loaded twice, which is bad :p --- apps/openmw/mwworld/cells.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 822aa78d6..dd2339eeb 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -8,6 +8,17 @@ #include "class.hpp" #include "containerstore.hpp" +//helper function +std::string toLower (const std::string& name) +{ + std::string lowerCase; + + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + return lowerCase; +} + MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { if (cell->data.flags & ESM::Cell::Interior) @@ -129,13 +140,14 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) { - std::map::iterator result = mInteriors.find (name); + std::string nName = toLower(name); + std::map::iterator result = mInteriors.find (nName); if (result==mInteriors.end()) { - const ESM::Cell *cell = mStore.cells.findInt (name); + const ESM::Cell *cell = mStore.cells.findInt (nName); - result = mInteriors.insert (std::make_pair (name, Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (nName, Ptr::CellStore (cell))).first; } if (result->second.mState!=Ptr::CellStore::State_Loaded) From 65678b95f0e2a0183c874a6e53a37e07ff635ad5 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 22 Aug 2012 21:11:03 +0300 Subject: [PATCH 471/688] Added support for make install. Added support for make install based on patch from Hasufells repository: https://github.com/hasufell/hasufell-overlay/tree/master/games-engines/openmw/files Signed-off-by: Lukasz Gromanowski --- CMakeLists.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index df05b72e8..cd888df72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -634,3 +634,34 @@ if (APPLE) " COMPONENT Runtime) include(CPack) endif (APPLE) + +## Linux building +# paths +set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/bin" CACHE PATH "Where to install binaries") +set(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") +set(DATADIR "${DATAROOTDIR}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") +set(DOCDIR "${DATAROOTDIR}/doc/openmw" CACHE PATH "Sets the doc directory to a non-default location.") +set(MANDIR "${DATAROOTDIR}/man" CACHE PATH "Where to install manpages") +set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc/openmw" CACHE PATH "Set config dir") +set(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") + +# Install binaries +INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) +INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) +INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) +INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) + +# Install icon and .desktop +INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}") +INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications") + +# Install global configuration files +INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" ) +#INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${SYSCONFDIR}" ) +INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" ) +INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" ) +INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.cfg" DESTINATION "${SYSCONFDIR}" ) + +# Install resources +INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) +INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) From ed607a625913cd175f0d12f5c2c5f911769fcdad Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Wed, 22 Aug 2012 15:00:07 -0400 Subject: [PATCH 472/688] Use char literals in UTF 8 conversion to fix 798 warnings The data type is specified as char but the literals are unsigned char. This results in 798 truncation warnings in vs2010. The literals were converted with a simple python script to signed char while taking two's complement and the overflow into account. --- components/to_utf8/tables_gen.hpp | 1536 ++++++++++++++--------------- 1 file changed, 768 insertions(+), 768 deletions(-) diff --git a/components/to_utf8/tables_gen.hpp b/components/to_utf8/tables_gen.hpp index 1084ca28f..9c32f0427 100644 --- a/components/to_utf8/tables_gen.hpp +++ b/components/to_utf8/tables_gen.hpp @@ -10,785 +10,785 @@ namespace ToUTF8 /// Serbian (Latin script), Romanian and Albanian. static char windows_1250[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x99, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 3, -30, -126, -84, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -102, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -80, 0, 0, + 2, -59, -96, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -59, -102, 0, 0, 0, + 2, -59, -92, 0, 0, 0, + 2, -59, -67, 0, 0, 0, + 2, -59, -71, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -124, -94, 0, 0, + 2, -59, -95, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -59, -101, 0, 0, 0, + 2, -59, -91, 0, 0, 0, + 2, -59, -66, 0, 0, 0, + 2, -59, -70, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -53, -121, 0, 0, 0, + 2, -53, -104, 0, 0, 0, + 2, -59, -127, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -60, -124, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -62, -88, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -59, -98, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -59, -69, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -53, -101, 0, 0, 0, + 2, -59, -126, 0, 0, 0, + 2, -62, -76, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -62, -72, 0, 0, 0, + 2, -60, -123, 0, 0, 0, + 2, -59, -97, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -60, -67, 0, 0, 0, + 2, -53, -99, 0, 0, 0, + 2, -60, -66, 0, 0, 0, + 2, -59, -68, 0, 0, 0, + 2, -59, -108, 0, 0, 0, + 2, -61, -127, 0, 0, 0, + 2, -61, -126, 0, 0, 0, + 2, -60, -126, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -60, -71, 0, 0, 0, + 2, -60, -122, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -60, -116, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -60, -104, 0, 0, 0, + 2, -61, -117, 0, 0, 0, + 2, -60, -102, 0, 0, 0, + 2, -61, -115, 0, 0, 0, + 2, -61, -114, 0, 0, 0, + 2, -60, -114, 0, 0, 0, + 2, -60, -112, 0, 0, 0, + 2, -59, -125, 0, 0, 0, + 2, -59, -121, 0, 0, 0, + 2, -61, -109, 0, 0, 0, + 2, -61, -108, 0, 0, 0, + 2, -59, -112, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -105, 0, 0, 0, + 2, -59, -104, 0, 0, 0, + 2, -59, -82, 0, 0, 0, + 2, -61, -102, 0, 0, 0, + 2, -59, -80, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -61, -99, 0, 0, 0, + 2, -59, -94, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -59, -107, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -60, -125, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -60, -70, 0, 0, 0, + 2, -60, -121, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -60, -115, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -60, -103, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -60, -101, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -60, -113, 0, 0, 0, + 2, -60, -111, 0, 0, 0, + 2, -59, -124, 0, 0, 0, + 2, -59, -120, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -59, -111, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -73, 0, 0, 0, + 2, -59, -103, 0, 0, 0, + 2, -59, -81, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -59, -79, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -67, 0, 0, 0, + 2, -59, -93, 0, 0, 0, + 2, -53, -103, 0, 0, 0 }; /// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic /// and other languages static char windows_1251[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd2, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd2, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x84, (char)0x96, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x80, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8f, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 2, -48, -126, 0, 0, 0, + 2, -48, -125, 0, 0, 0, + 3, -30, -128, -102, 0, 0, + 2, -47, -109, 0, 0, 0, + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 3, -30, -126, -84, 0, 0, + 3, -30, -128, -80, 0, 0, + 2, -48, -119, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -48, -118, 0, 0, 0, + 2, -48, -116, 0, 0, 0, + 2, -48, -117, 0, 0, 0, + 2, -48, -113, 0, 0, 0, + 2, -47, -110, 0, 0, 0, + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -124, -94, 0, 0, + 2, -47, -103, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -47, -102, 0, 0, 0, + 2, -47, -100, 0, 0, 0, + 2, -47, -101, 0, 0, 0, + 2, -47, -97, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -48, -114, 0, 0, 0, + 2, -47, -98, 0, 0, 0, + 2, -48, -120, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -46, -112, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -48, -127, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -48, -124, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -48, -121, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -48, -122, 0, 0, 0, + 2, -47, -106, 0, 0, 0, + 2, -46, -111, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -47, -111, 0, 0, 0, + 3, -30, -124, -106, 0, 0, + 2, -47, -108, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -47, -104, 0, 0, 0, + 2, -48, -123, 0, 0, 0, + 2, -47, -107, 0, 0, 0, + 2, -47, -105, 0, 0, 0, + 2, -48, -112, 0, 0, 0, + 2, -48, -111, 0, 0, 0, + 2, -48, -110, 0, 0, 0, + 2, -48, -109, 0, 0, 0, + 2, -48, -108, 0, 0, 0, + 2, -48, -107, 0, 0, 0, + 2, -48, -106, 0, 0, 0, + 2, -48, -105, 0, 0, 0, + 2, -48, -104, 0, 0, 0, + 2, -48, -103, 0, 0, 0, + 2, -48, -102, 0, 0, 0, + 2, -48, -101, 0, 0, 0, + 2, -48, -100, 0, 0, 0, + 2, -48, -99, 0, 0, 0, + 2, -48, -98, 0, 0, 0, + 2, -48, -97, 0, 0, 0, + 2, -48, -96, 0, 0, 0, + 2, -48, -95, 0, 0, 0, + 2, -48, -94, 0, 0, 0, + 2, -48, -93, 0, 0, 0, + 2, -48, -92, 0, 0, 0, + 2, -48, -91, 0, 0, 0, + 2, -48, -90, 0, 0, 0, + 2, -48, -89, 0, 0, 0, + 2, -48, -88, 0, 0, 0, + 2, -48, -87, 0, 0, 0, + 2, -48, -86, 0, 0, 0, + 2, -48, -85, 0, 0, 0, + 2, -48, -84, 0, 0, 0, + 2, -48, -83, 0, 0, 0, + 2, -48, -82, 0, 0, 0, + 2, -48, -81, 0, 0, 0, + 2, -48, -80, 0, 0, 0, + 2, -48, -79, 0, 0, 0, + 2, -48, -78, 0, 0, 0, + 2, -48, -77, 0, 0, 0, + 2, -48, -76, 0, 0, 0, + 2, -48, -75, 0, 0, 0, + 2, -48, -74, 0, 0, 0, + 2, -48, -73, 0, 0, 0, + 2, -48, -72, 0, 0, 0, + 2, -48, -71, 0, 0, 0, + 2, -48, -70, 0, 0, 0, + 2, -48, -69, 0, 0, 0, + 2, -48, -68, 0, 0, 0, + 2, -48, -67, 0, 0, 0, + 2, -48, -66, 0, 0, 0, + 2, -48, -65, 0, 0, 0, + 2, -47, -128, 0, 0, 0, + 2, -47, -127, 0, 0, 0, + 2, -47, -126, 0, 0, 0, + 2, -47, -125, 0, 0, 0, + 2, -47, -124, 0, 0, 0, + 2, -47, -123, 0, 0, 0, + 2, -47, -122, 0, 0, 0, + 2, -47, -121, 0, 0, 0, + 2, -47, -120, 0, 0, 0, + 2, -47, -119, 0, 0, 0, + 2, -47, -118, 0, 0, 0, + 2, -47, -117, 0, 0, 0, + 2, -47, -116, 0, 0, 0, + 2, -47, -115, 0, 0, 0, + 2, -47, -114, 0, 0, 0, + 2, -47, -113, 0, 0, 0 }; /// Latin alphabet used by English and some other Western languages static char windows_1252[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x2, (char)0xc6, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x2, (char)0xc5, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x2, (char)0xc5, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x80, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbf, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 3, -30, -126, -84, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -102, 0, 0, + 2, -58, -110, 0, 0, 0, + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 2, -53, -122, 0, 0, 0, + 3, -30, -128, -80, 0, 0, + 2, -59, -96, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -59, -110, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 2, -59, -67, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 2, -53, -100, 0, 0, 0, + 3, -30, -124, -94, 0, 0, + 2, -59, -95, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -59, -109, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 2, -59, -66, 0, 0, 0, + 2, -59, -72, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -62, -95, 0, 0, 0, + 2, -62, -94, 0, 0, 0, + 2, -62, -93, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -62, -91, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -62, -88, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -62, -86, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -62, -81, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -62, -78, 0, 0, 0, + 2, -62, -77, 0, 0, 0, + 2, -62, -76, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -62, -72, 0, 0, 0, + 2, -62, -71, 0, 0, 0, + 2, -62, -70, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -62, -68, 0, 0, 0, + 2, -62, -67, 0, 0, 0, + 2, -62, -66, 0, 0, 0, + 2, -62, -65, 0, 0, 0, + 2, -61, -128, 0, 0, 0, + 2, -61, -127, 0, 0, 0, + 2, -61, -126, 0, 0, 0, + 2, -61, -125, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -61, -123, 0, 0, 0, + 2, -61, -122, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -61, -120, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -61, -118, 0, 0, 0, + 2, -61, -117, 0, 0, 0, + 2, -61, -116, 0, 0, 0, + 2, -61, -115, 0, 0, 0, + 2, -61, -114, 0, 0, 0, + 2, -61, -113, 0, 0, 0, + 2, -61, -112, 0, 0, 0, + 2, -61, -111, 0, 0, 0, + 2, -61, -110, 0, 0, 0, + 2, -61, -109, 0, 0, 0, + 2, -61, -108, 0, 0, 0, + 2, -61, -107, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -105, 0, 0, 0, + 2, -61, -104, 0, 0, 0, + 2, -61, -103, 0, 0, 0, + 2, -61, -102, 0, 0, 0, + 2, -61, -101, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -61, -99, 0, 0, 0, + 2, -61, -98, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -61, -96, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -61, -93, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -61, -91, 0, 0, 0, + 2, -61, -90, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -61, -88, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -61, -86, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -61, -84, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -61, -81, 0, 0, 0, + 2, -61, -80, 0, 0, 0, + 2, -61, -79, 0, 0, 0, + 2, -61, -78, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -61, -75, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -73, 0, 0, 0, + 2, -61, -72, 0, 0, 0, + 2, -61, -71, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -61, -69, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -67, 0, 0, 0, + 2, -61, -66, 0, 0, 0, + 2, -61, -65, 0, 0, 0 }; } From 5fa8165f97ae19a461c904d60e3e9c23b5ff37b2 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Wed, 22 Aug 2012 15:00:07 -0400 Subject: [PATCH 473/688] Use char literals in UTF 8 conversion to fix 798 warnings The data type is specified as char but the literals are unsigned char. This results in 798 truncation warnings in vs2010. The literals were converted with a simple python script to signed char while taking two's complement and the overflow into account. Also tested on Ubuntu 12.04 with gcc 4.6. --- components/to_utf8/tables_gen.hpp | 1536 ++++++++++++++--------------- 1 file changed, 768 insertions(+), 768 deletions(-) diff --git a/components/to_utf8/tables_gen.hpp b/components/to_utf8/tables_gen.hpp index 1084ca28f..9c32f0427 100644 --- a/components/to_utf8/tables_gen.hpp +++ b/components/to_utf8/tables_gen.hpp @@ -10,785 +10,785 @@ namespace ToUTF8 /// Serbian (Latin script), Romanian and Albanian. static char windows_1250[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc4, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x99, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 3, -30, -126, -84, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -102, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -80, 0, 0, + 2, -59, -96, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -59, -102, 0, 0, 0, + 2, -59, -92, 0, 0, 0, + 2, -59, -67, 0, 0, 0, + 2, -59, -71, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -124, -94, 0, 0, + 2, -59, -95, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -59, -101, 0, 0, 0, + 2, -59, -91, 0, 0, 0, + 2, -59, -66, 0, 0, 0, + 2, -59, -70, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -53, -121, 0, 0, 0, + 2, -53, -104, 0, 0, 0, + 2, -59, -127, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -60, -124, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -62, -88, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -59, -98, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -59, -69, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -53, -101, 0, 0, 0, + 2, -59, -126, 0, 0, 0, + 2, -62, -76, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -62, -72, 0, 0, 0, + 2, -60, -123, 0, 0, 0, + 2, -59, -97, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -60, -67, 0, 0, 0, + 2, -53, -99, 0, 0, 0, + 2, -60, -66, 0, 0, 0, + 2, -59, -68, 0, 0, 0, + 2, -59, -108, 0, 0, 0, + 2, -61, -127, 0, 0, 0, + 2, -61, -126, 0, 0, 0, + 2, -60, -126, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -60, -71, 0, 0, 0, + 2, -60, -122, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -60, -116, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -60, -104, 0, 0, 0, + 2, -61, -117, 0, 0, 0, + 2, -60, -102, 0, 0, 0, + 2, -61, -115, 0, 0, 0, + 2, -61, -114, 0, 0, 0, + 2, -60, -114, 0, 0, 0, + 2, -60, -112, 0, 0, 0, + 2, -59, -125, 0, 0, 0, + 2, -59, -121, 0, 0, 0, + 2, -61, -109, 0, 0, 0, + 2, -61, -108, 0, 0, 0, + 2, -59, -112, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -105, 0, 0, 0, + 2, -59, -104, 0, 0, 0, + 2, -59, -82, 0, 0, 0, + 2, -61, -102, 0, 0, 0, + 2, -59, -80, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -61, -99, 0, 0, 0, + 2, -59, -94, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -59, -107, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -60, -125, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -60, -70, 0, 0, 0, + 2, -60, -121, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -60, -115, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -60, -103, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -60, -101, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -60, -113, 0, 0, 0, + 2, -60, -111, 0, 0, 0, + 2, -59, -124, 0, 0, 0, + 2, -59, -120, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -59, -111, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -73, 0, 0, 0, + 2, -59, -103, 0, 0, 0, + 2, -59, -81, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -59, -79, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -67, 0, 0, 0, + 2, -59, -93, 0, 0, 0, + 2, -53, -103, 0, 0, 0 }; /// Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic /// and other languages static char windows_1251[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd2, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd2, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x84, (char)0x96, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd0, (char)0xbf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x80, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xd1, (char)0x8f, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 2, -48, -126, 0, 0, 0, + 2, -48, -125, 0, 0, 0, + 3, -30, -128, -102, 0, 0, + 2, -47, -109, 0, 0, 0, + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 3, -30, -126, -84, 0, 0, + 3, -30, -128, -80, 0, 0, + 2, -48, -119, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -48, -118, 0, 0, 0, + 2, -48, -116, 0, 0, 0, + 2, -48, -117, 0, 0, 0, + 2, -48, -113, 0, 0, 0, + 2, -47, -110, 0, 0, 0, + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -124, -94, 0, 0, + 2, -47, -103, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -47, -102, 0, 0, 0, + 2, -47, -100, 0, 0, 0, + 2, -47, -101, 0, 0, 0, + 2, -47, -97, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -48, -114, 0, 0, 0, + 2, -47, -98, 0, 0, 0, + 2, -48, -120, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -46, -112, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -48, -127, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -48, -124, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -48, -121, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -48, -122, 0, 0, 0, + 2, -47, -106, 0, 0, 0, + 2, -46, -111, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -47, -111, 0, 0, 0, + 3, -30, -124, -106, 0, 0, + 2, -47, -108, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -47, -104, 0, 0, 0, + 2, -48, -123, 0, 0, 0, + 2, -47, -107, 0, 0, 0, + 2, -47, -105, 0, 0, 0, + 2, -48, -112, 0, 0, 0, + 2, -48, -111, 0, 0, 0, + 2, -48, -110, 0, 0, 0, + 2, -48, -109, 0, 0, 0, + 2, -48, -108, 0, 0, 0, + 2, -48, -107, 0, 0, 0, + 2, -48, -106, 0, 0, 0, + 2, -48, -105, 0, 0, 0, + 2, -48, -104, 0, 0, 0, + 2, -48, -103, 0, 0, 0, + 2, -48, -102, 0, 0, 0, + 2, -48, -101, 0, 0, 0, + 2, -48, -100, 0, 0, 0, + 2, -48, -99, 0, 0, 0, + 2, -48, -98, 0, 0, 0, + 2, -48, -97, 0, 0, 0, + 2, -48, -96, 0, 0, 0, + 2, -48, -95, 0, 0, 0, + 2, -48, -94, 0, 0, 0, + 2, -48, -93, 0, 0, 0, + 2, -48, -92, 0, 0, 0, + 2, -48, -91, 0, 0, 0, + 2, -48, -90, 0, 0, 0, + 2, -48, -89, 0, 0, 0, + 2, -48, -88, 0, 0, 0, + 2, -48, -87, 0, 0, 0, + 2, -48, -86, 0, 0, 0, + 2, -48, -85, 0, 0, 0, + 2, -48, -84, 0, 0, 0, + 2, -48, -83, 0, 0, 0, + 2, -48, -82, 0, 0, 0, + 2, -48, -81, 0, 0, 0, + 2, -48, -80, 0, 0, 0, + 2, -48, -79, 0, 0, 0, + 2, -48, -78, 0, 0, 0, + 2, -48, -77, 0, 0, 0, + 2, -48, -76, 0, 0, 0, + 2, -48, -75, 0, 0, 0, + 2, -48, -74, 0, 0, 0, + 2, -48, -73, 0, 0, 0, + 2, -48, -72, 0, 0, 0, + 2, -48, -71, 0, 0, 0, + 2, -48, -70, 0, 0, 0, + 2, -48, -69, 0, 0, 0, + 2, -48, -68, 0, 0, 0, + 2, -48, -67, 0, 0, 0, + 2, -48, -66, 0, 0, 0, + 2, -48, -65, 0, 0, 0, + 2, -47, -128, 0, 0, 0, + 2, -47, -127, 0, 0, 0, + 2, -47, -126, 0, 0, 0, + 2, -47, -125, 0, 0, 0, + 2, -47, -124, 0, 0, 0, + 2, -47, -123, 0, 0, 0, + 2, -47, -122, 0, 0, 0, + 2, -47, -121, 0, 0, 0, + 2, -47, -120, 0, 0, 0, + 2, -47, -119, 0, 0, 0, + 2, -47, -118, 0, 0, 0, + 2, -47, -117, 0, 0, 0, + 2, -47, -116, 0, 0, 0, + 2, -47, -115, 0, 0, 0, + 2, -47, -114, 0, 0, 0, + 2, -47, -113, 0, 0, 0 }; /// Latin alphabet used by English and some other Western languages static char windows_1252[] = { - (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x8, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x9, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xa, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xb, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xc, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xd, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xe, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0xf, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x10, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x11, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x12, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x13, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x14, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x15, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x16, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x17, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x18, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x19, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x1f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x21, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x22, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x23, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x24, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x25, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x26, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x27, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x28, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x29, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x2f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x30, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x31, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x32, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x33, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x34, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x35, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x36, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x37, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x38, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x39, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x3f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x40, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x41, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x42, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x43, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x44, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x45, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x46, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x47, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x48, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x49, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x4f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x50, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x51, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x52, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x53, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x54, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x55, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x56, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x57, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x58, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x59, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x5f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x60, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x61, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x62, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x63, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x64, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x65, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x66, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x67, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x68, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x69, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x6f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x70, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x71, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x72, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x73, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x74, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x75, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x76, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x77, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x78, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x79, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7a, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7b, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7c, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7d, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7e, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x7f, (char)0x0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x82, (char)0xac, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x9a, (char)0x0, (char)0x0, - (char)0x2, (char)0xc6, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9e, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa6, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa1, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xb9, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x2, (char)0xc5, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x3, (char)0xe2, (char)0x80, (char)0x98, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x99, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9c, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x9d, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xa2, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x93, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0x94, (char)0x0, (char)0x0, - (char)0x2, (char)0xcb, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x84, (char)0xa2, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x3, (char)0xe2, (char)0x80, (char)0xba, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x1, (char)0x20, (char)0x0, (char)0x0, (char)0x0, (char)0x0, // not part of this charset - (char)0x2, (char)0xc5, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc5, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc2, (char)0xbf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x80, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x81, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x82, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x83, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x84, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x85, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x86, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x87, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x88, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x89, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x8f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x90, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x91, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x92, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x93, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x94, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x95, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x96, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x97, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x98, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x99, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9a, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9b, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9c, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9d, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9e, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0x9f, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xa9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xaa, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xab, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xac, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xad, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xae, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xaf, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb0, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb1, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb2, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb3, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb4, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb5, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb6, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb7, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb8, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xb9, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xba, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbb, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbc, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbd, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbe, (char)0x0, (char)0x0, (char)0x0, - (char)0x2, (char)0xc3, (char)0xbf, (char)0x0, (char)0x0, (char)0x0 + 1, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, + 1, 3, 0, 0, 0, 0, + 1, 4, 0, 0, 0, 0, + 1, 5, 0, 0, 0, 0, + 1, 6, 0, 0, 0, 0, + 1, 7, 0, 0, 0, 0, + 1, 8, 0, 0, 0, 0, + 1, 9, 0, 0, 0, 0, + 1, 10, 0, 0, 0, 0, + 1, 11, 0, 0, 0, 0, + 1, 12, 0, 0, 0, 0, + 1, 13, 0, 0, 0, 0, + 1, 14, 0, 0, 0, 0, + 1, 15, 0, 0, 0, 0, + 1, 16, 0, 0, 0, 0, + 1, 17, 0, 0, 0, 0, + 1, 18, 0, 0, 0, 0, + 1, 19, 0, 0, 0, 0, + 1, 20, 0, 0, 0, 0, + 1, 21, 0, 0, 0, 0, + 1, 22, 0, 0, 0, 0, + 1, 23, 0, 0, 0, 0, + 1, 24, 0, 0, 0, 0, + 1, 25, 0, 0, 0, 0, + 1, 26, 0, 0, 0, 0, + 1, 27, 0, 0, 0, 0, + 1, 28, 0, 0, 0, 0, + 1, 29, 0, 0, 0, 0, + 1, 30, 0, 0, 0, 0, + 1, 31, 0, 0, 0, 0, + 1, 32, 0, 0, 0, 0, + 1, 33, 0, 0, 0, 0, + 1, 34, 0, 0, 0, 0, + 1, 35, 0, 0, 0, 0, + 1, 36, 0, 0, 0, 0, + 1, 37, 0, 0, 0, 0, + 1, 38, 0, 0, 0, 0, + 1, 39, 0, 0, 0, 0, + 1, 40, 0, 0, 0, 0, + 1, 41, 0, 0, 0, 0, + 1, 42, 0, 0, 0, 0, + 1, 43, 0, 0, 0, 0, + 1, 44, 0, 0, 0, 0, + 1, 45, 0, 0, 0, 0, + 1, 46, 0, 0, 0, 0, + 1, 47, 0, 0, 0, 0, + 1, 48, 0, 0, 0, 0, + 1, 49, 0, 0, 0, 0, + 1, 50, 0, 0, 0, 0, + 1, 51, 0, 0, 0, 0, + 1, 52, 0, 0, 0, 0, + 1, 53, 0, 0, 0, 0, + 1, 54, 0, 0, 0, 0, + 1, 55, 0, 0, 0, 0, + 1, 56, 0, 0, 0, 0, + 1, 57, 0, 0, 0, 0, + 1, 58, 0, 0, 0, 0, + 1, 59, 0, 0, 0, 0, + 1, 60, 0, 0, 0, 0, + 1, 61, 0, 0, 0, 0, + 1, 62, 0, 0, 0, 0, + 1, 63, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 1, 65, 0, 0, 0, 0, + 1, 66, 0, 0, 0, 0, + 1, 67, 0, 0, 0, 0, + 1, 68, 0, 0, 0, 0, + 1, 69, 0, 0, 0, 0, + 1, 70, 0, 0, 0, 0, + 1, 71, 0, 0, 0, 0, + 1, 72, 0, 0, 0, 0, + 1, 73, 0, 0, 0, 0, + 1, 74, 0, 0, 0, 0, + 1, 75, 0, 0, 0, 0, + 1, 76, 0, 0, 0, 0, + 1, 77, 0, 0, 0, 0, + 1, 78, 0, 0, 0, 0, + 1, 79, 0, 0, 0, 0, + 1, 80, 0, 0, 0, 0, + 1, 81, 0, 0, 0, 0, + 1, 82, 0, 0, 0, 0, + 1, 83, 0, 0, 0, 0, + 1, 84, 0, 0, 0, 0, + 1, 85, 0, 0, 0, 0, + 1, 86, 0, 0, 0, 0, + 1, 87, 0, 0, 0, 0, + 1, 88, 0, 0, 0, 0, + 1, 89, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, + 1, 91, 0, 0, 0, 0, + 1, 92, 0, 0, 0, 0, + 1, 93, 0, 0, 0, 0, + 1, 94, 0, 0, 0, 0, + 1, 95, 0, 0, 0, 0, + 1, 96, 0, 0, 0, 0, + 1, 97, 0, 0, 0, 0, + 1, 98, 0, 0, 0, 0, + 1, 99, 0, 0, 0, 0, + 1, 100, 0, 0, 0, 0, + 1, 101, 0, 0, 0, 0, + 1, 102, 0, 0, 0, 0, + 1, 103, 0, 0, 0, 0, + 1, 104, 0, 0, 0, 0, + 1, 105, 0, 0, 0, 0, + 1, 106, 0, 0, 0, 0, + 1, 107, 0, 0, 0, 0, + 1, 108, 0, 0, 0, 0, + 1, 109, 0, 0, 0, 0, + 1, 110, 0, 0, 0, 0, + 1, 111, 0, 0, 0, 0, + 1, 112, 0, 0, 0, 0, + 1, 113, 0, 0, 0, 0, + 1, 114, 0, 0, 0, 0, + 1, 115, 0, 0, 0, 0, + 1, 116, 0, 0, 0, 0, + 1, 117, 0, 0, 0, 0, + 1, 118, 0, 0, 0, 0, + 1, 119, 0, 0, 0, 0, + 1, 120, 0, 0, 0, 0, + 1, 121, 0, 0, 0, 0, + 1, 122, 0, 0, 0, 0, + 1, 123, 0, 0, 0, 0, + 1, 124, 0, 0, 0, 0, + 1, 125, 0, 0, 0, 0, + 1, 126, 0, 0, 0, 0, + 1, 127, 0, 0, 0, 0, + 3, -30, -126, -84, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -102, 0, 0, + 2, -58, -110, 0, 0, 0, + 3, -30, -128, -98, 0, 0, + 3, -30, -128, -90, 0, 0, + 3, -30, -128, -96, 0, 0, + 3, -30, -128, -95, 0, 0, + 2, -53, -122, 0, 0, 0, + 3, -30, -128, -80, 0, 0, + 2, -59, -96, 0, 0, 0, + 3, -30, -128, -71, 0, 0, + 2, -59, -110, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 2, -59, -67, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 1, 32, 0, 0, 0, 0, // not part of this charset + 3, -30, -128, -104, 0, 0, + 3, -30, -128, -103, 0, 0, + 3, -30, -128, -100, 0, 0, + 3, -30, -128, -99, 0, 0, + 3, -30, -128, -94, 0, 0, + 3, -30, -128, -109, 0, 0, + 3, -30, -128, -108, 0, 0, + 2, -53, -100, 0, 0, 0, + 3, -30, -124, -94, 0, 0, + 2, -59, -95, 0, 0, 0, + 3, -30, -128, -70, 0, 0, + 2, -59, -109, 0, 0, 0, + 1, 32, 0, 0, 0, 0, // not part of this charset + 2, -59, -66, 0, 0, 0, + 2, -59, -72, 0, 0, 0, + 2, -62, -96, 0, 0, 0, + 2, -62, -95, 0, 0, 0, + 2, -62, -94, 0, 0, 0, + 2, -62, -93, 0, 0, 0, + 2, -62, -92, 0, 0, 0, + 2, -62, -91, 0, 0, 0, + 2, -62, -90, 0, 0, 0, + 2, -62, -89, 0, 0, 0, + 2, -62, -88, 0, 0, 0, + 2, -62, -87, 0, 0, 0, + 2, -62, -86, 0, 0, 0, + 2, -62, -85, 0, 0, 0, + 2, -62, -84, 0, 0, 0, + 2, -62, -83, 0, 0, 0, + 2, -62, -82, 0, 0, 0, + 2, -62, -81, 0, 0, 0, + 2, -62, -80, 0, 0, 0, + 2, -62, -79, 0, 0, 0, + 2, -62, -78, 0, 0, 0, + 2, -62, -77, 0, 0, 0, + 2, -62, -76, 0, 0, 0, + 2, -62, -75, 0, 0, 0, + 2, -62, -74, 0, 0, 0, + 2, -62, -73, 0, 0, 0, + 2, -62, -72, 0, 0, 0, + 2, -62, -71, 0, 0, 0, + 2, -62, -70, 0, 0, 0, + 2, -62, -69, 0, 0, 0, + 2, -62, -68, 0, 0, 0, + 2, -62, -67, 0, 0, 0, + 2, -62, -66, 0, 0, 0, + 2, -62, -65, 0, 0, 0, + 2, -61, -128, 0, 0, 0, + 2, -61, -127, 0, 0, 0, + 2, -61, -126, 0, 0, 0, + 2, -61, -125, 0, 0, 0, + 2, -61, -124, 0, 0, 0, + 2, -61, -123, 0, 0, 0, + 2, -61, -122, 0, 0, 0, + 2, -61, -121, 0, 0, 0, + 2, -61, -120, 0, 0, 0, + 2, -61, -119, 0, 0, 0, + 2, -61, -118, 0, 0, 0, + 2, -61, -117, 0, 0, 0, + 2, -61, -116, 0, 0, 0, + 2, -61, -115, 0, 0, 0, + 2, -61, -114, 0, 0, 0, + 2, -61, -113, 0, 0, 0, + 2, -61, -112, 0, 0, 0, + 2, -61, -111, 0, 0, 0, + 2, -61, -110, 0, 0, 0, + 2, -61, -109, 0, 0, 0, + 2, -61, -108, 0, 0, 0, + 2, -61, -107, 0, 0, 0, + 2, -61, -106, 0, 0, 0, + 2, -61, -105, 0, 0, 0, + 2, -61, -104, 0, 0, 0, + 2, -61, -103, 0, 0, 0, + 2, -61, -102, 0, 0, 0, + 2, -61, -101, 0, 0, 0, + 2, -61, -100, 0, 0, 0, + 2, -61, -99, 0, 0, 0, + 2, -61, -98, 0, 0, 0, + 2, -61, -97, 0, 0, 0, + 2, -61, -96, 0, 0, 0, + 2, -61, -95, 0, 0, 0, + 2, -61, -94, 0, 0, 0, + 2, -61, -93, 0, 0, 0, + 2, -61, -92, 0, 0, 0, + 2, -61, -91, 0, 0, 0, + 2, -61, -90, 0, 0, 0, + 2, -61, -89, 0, 0, 0, + 2, -61, -88, 0, 0, 0, + 2, -61, -87, 0, 0, 0, + 2, -61, -86, 0, 0, 0, + 2, -61, -85, 0, 0, 0, + 2, -61, -84, 0, 0, 0, + 2, -61, -83, 0, 0, 0, + 2, -61, -82, 0, 0, 0, + 2, -61, -81, 0, 0, 0, + 2, -61, -80, 0, 0, 0, + 2, -61, -79, 0, 0, 0, + 2, -61, -78, 0, 0, 0, + 2, -61, -77, 0, 0, 0, + 2, -61, -76, 0, 0, 0, + 2, -61, -75, 0, 0, 0, + 2, -61, -74, 0, 0, 0, + 2, -61, -73, 0, 0, 0, + 2, -61, -72, 0, 0, 0, + 2, -61, -71, 0, 0, 0, + 2, -61, -70, 0, 0, 0, + 2, -61, -69, 0, 0, 0, + 2, -61, -68, 0, 0, 0, + 2, -61, -67, 0, 0, 0, + 2, -61, -66, 0, 0, 0, + 2, -61, -65, 0, 0, 0 }; } From fab4cfecb13d90e1ac8a8cf7278f82ca78628d48 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Aug 2012 10:52:06 +0200 Subject: [PATCH 474/688] some unfinished work on quick keys window --- apps/openmw/CMakeLists.txt | 3 +- apps/openmw/mwgui/class.cpp | 19 +- apps/openmw/mwgui/class.hpp | 8 +- apps/openmw/mwgui/confirmationdialog.cpp | 13 +- apps/openmw/mwgui/confirmationdialog.hpp | 4 +- apps/openmw/mwgui/countdialog.cpp | 15 +- apps/openmw/mwgui/countdialog.hpp | 4 +- apps/openmw/mwgui/itemselection.cpp | 39 +++ apps/openmw/mwgui/itemselection.hpp | 28 +++ apps/openmw/mwgui/mode.hpp | 4 +- apps/openmw/mwgui/quickkeysmenu.cpp | 237 ++++++++++++++++++ apps/openmw/mwgui/quickkeysmenu.hpp | 84 +++++++ apps/openmw/mwgui/window_base.cpp | 23 +- apps/openmw/mwgui/window_base.hpp | 17 +- apps/openmw/mwgui/windowmanagerimp.cpp | 7 + apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 82 +++++- apps/openmw/mwinput/inputmanagerimp.hpp | 16 ++ files/mygui/CMakeLists.txt | 4 + .../mygui/openmw_itemselection_dialog.layout | 20 ++ .../mygui/openmw_magicselection_dialog.layout | 19 ++ files/mygui/openmw_quickkeys_menu.layout | 38 +++ .../mygui/openmw_quickkeys_menu_assign.layout | 28 +++ 23 files changed, 655 insertions(+), 59 deletions(-) create mode 100644 apps/openmw/mwgui/itemselection.cpp create mode 100644 apps/openmw/mwgui/itemselection.hpp create mode 100644 apps/openmw/mwgui/quickkeysmenu.cpp create mode 100644 apps/openmw/mwgui/quickkeysmenu.hpp create mode 100644 files/mygui/openmw_itemselection_dialog.layout create mode 100644 files/mygui/openmw_magicselection_dialog.layout create mode 100644 files/mygui/openmw_quickkeys_menu.layout create mode 100644 files/mygui/openmw_quickkeys_menu_assign.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 1b4ae209d..fae76dd64 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -28,7 +28,8 @@ add_openmw_dir (mwgui dialogue_history window_base stats_window messagebox journalwindow charactercreation map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow - confirmationdialog alchemywindow referenceinterface spellwindow mainmenu + confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu + itemselection ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index eaf191819..971740b5e 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -695,7 +695,7 @@ void CreateClassDialog::onBackClicked(MyGUI::Widget* _sender) /* SelectSpecializationDialog */ SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_specialization.layout", parWindowManager) + : WindowModal("openmw_chargen_select_specialization.layout", parWindowManager) { // Centre dialog center(); @@ -727,13 +727,10 @@ SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& pa cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); int buttonWidth = cancelButton->getTextSize().width + 24; cancelButton->setCoord(216 - buttonWidth, 90, buttonWidth, 21); - - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } SelectSpecializationDialog::~SelectSpecializationDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls @@ -760,7 +757,7 @@ void SelectSpecializationDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectAttributeDialog */ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_attribute.layout", parWindowManager) + : WindowModal("openmw_chargen_select_attribute.layout", parWindowManager) { // Centre dialog center(); @@ -785,13 +782,10 @@ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowMan cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); int buttonWidth = cancelButton->getTextSize().width + 24; cancelButton->setCoord(186 - buttonWidth, 180, buttonWidth, 21); - - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } SelectAttributeDialog::~SelectAttributeDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls @@ -812,7 +806,7 @@ void SelectAttributeDialog::onCancelClicked(MyGUI::Widget* _sender) /* SelectSkillDialog */ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_select_skill.layout", parWindowManager) + : WindowModal("openmw_chargen_select_skill.layout", parWindowManager) { // Centre dialog center(); @@ -884,12 +878,10 @@ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) int buttonWidth = cancelButton->getTextSize().width + 24; cancelButton->setCoord(447 - buttonWidth, 218, buttonWidth, 21); - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } SelectSkillDialog::~SelectSkillDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls @@ -908,7 +900,7 @@ void SelectSkillDialog::onCancelClicked(MyGUI::Widget* _sender) /* DescriptionDialog */ DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_class_description.layout", parWindowManager) + : WindowModal("openmw_chargen_class_description.layout", parWindowManager) { // Centre dialog center(); @@ -924,13 +916,10 @@ DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) // Make sure the edit box has focus MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); - - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); } DescriptionDialog::~DescriptionDialog() { - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } // widget controls diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 4baceed1e..bcb5c2627 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -134,7 +134,7 @@ namespace MWGui std::string mCurrentClassId; }; - class SelectSpecializationDialog : public WindowBase + class SelectSpecializationDialog : public WindowModal { public: SelectSpecializationDialog(MWBase::WindowManager& parWindowManager); @@ -165,7 +165,7 @@ namespace MWGui ESM::Class::Specialization mSpecializationId; }; - class SelectAttributeDialog : public WindowBase + class SelectAttributeDialog : public WindowModal { public: SelectAttributeDialog(MWBase::WindowManager& parWindowManager); @@ -198,7 +198,7 @@ namespace MWGui ESM::Attribute::AttributeID mAttributeId; }; - class SelectSkillDialog : public WindowBase + class SelectSkillDialog : public WindowModal { public: SelectSkillDialog(MWBase::WindowManager& parWindowManager); @@ -234,7 +234,7 @@ namespace MWGui ESM::Skill::SkillEnum mSkillId; }; - class DescriptionDialog : public WindowBase + class DescriptionDialog : public WindowModal { public: DescriptionDialog(MWBase::WindowManager& parWindowManager); diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index 1c68da9e5..d22181d60 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -8,7 +8,7 @@ namespace MWGui { ConfirmationDialog::ConfirmationDialog(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_confirmation_dialog.layout", parWindowManager) + WindowModal("openmw_confirmation_dialog.layout", parWindowManager) { getWidget(mMessage, "Message"); getWidget(mOkButton, "OkButton"); @@ -32,9 +32,6 @@ namespace MWGui center(); - // make other gui elements inaccessible while this dialog is open - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); - int okButtonWidth = mOkButton->getTextSize().width + 24; mOkButton->setCoord(mMainWidget->getWidth() - 30 - okButtonWidth, mOkButton->getTop(), @@ -52,19 +49,13 @@ namespace MWGui { eventCancelClicked(); - close(); + setVisible(false); } void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender) { eventOkClicked(); - close(); - } - - void ConfirmationDialog::close() - { setVisible(false); - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); } } diff --git a/apps/openmw/mwgui/confirmationdialog.hpp b/apps/openmw/mwgui/confirmationdialog.hpp index a78028b1c..45941f2ad 100644 --- a/apps/openmw/mwgui/confirmationdialog.hpp +++ b/apps/openmw/mwgui/confirmationdialog.hpp @@ -5,7 +5,7 @@ namespace MWGui { - class ConfirmationDialog : public WindowBase + class ConfirmationDialog : public WindowModal { public: ConfirmationDialog(MWBase::WindowManager& parWindowManager); @@ -26,8 +26,6 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); - - void close(); }; } diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 6d9415354..0ed9cf8ca 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -8,7 +8,7 @@ namespace MWGui { CountDialog::CountDialog(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_count_window.layout", parWindowManager) + WindowModal("openmw_count_window.layout", parWindowManager) { getWidget(mSlider, "CountSlider"); getWidget(mItemEdit, "ItemEdit"); @@ -40,9 +40,6 @@ namespace MWGui width, mMainWidget->getHeight()); - // make other gui elements inaccessible while this dialog is open - MyGUI::InputManager::getInstance().addWidgetModal(mMainWidget); - MyGUI::InputManager::getInstance().setKeyFocusWidget(mItemEdit); mSlider->setScrollPosition(maxCount-1); @@ -63,14 +60,14 @@ namespace MWGui void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) { - close(); + setVisible(false); } void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); - close(); + setVisible(false); } void CountDialog::onEditTextChange(MyGUI::EditBox* _sender) @@ -99,10 +96,4 @@ namespace MWGui { mItemEdit->setCaption(boost::lexical_cast(_position+1)); } - - void CountDialog::close() - { - setVisible(false); - MyGUI::InputManager::getInstance().removeWidgetModal(mMainWidget); - } } diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 6002dadfe..80da6eea0 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -5,7 +5,7 @@ namespace MWGui { - class CountDialog : public WindowBase + class CountDialog : public WindowModal { public: CountDialog(MWBase::WindowManager& parWindowManager); @@ -30,8 +30,6 @@ namespace MWGui void onOkButtonClicked(MyGUI::Widget* _sender); void onEditTextChange(MyGUI::EditBox* _sender); void onSliderMoved(MyGUI::ScrollBar* _sender, size_t _position); - - void close(); }; } diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp new file mode 100644 index 000000000..14b1cf8ee --- /dev/null +++ b/apps/openmw/mwgui/itemselection.cpp @@ -0,0 +1,39 @@ +#include "itemselection.hpp" + +namespace MWGui +{ + + ItemSelectionDialog::ItemSelectionDialog(const std::string &label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager) + : ContainerBase(NULL) + , WindowModal("openmw_itemselection_dialog.layout", parWindowManager) + { + mFilter = filter; + + MyGUI::ScrollView* itemView; + MyGUI::Widget* containerWidget; + getWidget(containerWidget, "Items"); + getWidget(itemView, "ItemView"); + setWidgets(containerWidget, itemView); + + MyGUI::TextBox* l; + getWidget(l, "Label"); + l->setCaptionWithReplacing (label); + + MyGUI::Button* cancelButton; + getWidget(cancelButton, "CancelButton"); + cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked); + + center(); + } + + void ItemSelectionDialog::onSelectedItemImpl(MWWorld::Ptr item) + { + eventItemSelected(item); + } + + void ItemSelectionDialog::onCancelButtonClicked(MyGUI::Widget* sender) + { + eventDialogCanceled(); + } + +} diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp new file mode 100644 index 000000000..cab125f1f --- /dev/null +++ b/apps/openmw/mwgui/itemselection.hpp @@ -0,0 +1,28 @@ +#include "container.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MWGui +{ + + class ItemSelectionDialog : public ContainerBase, public WindowModal + { + public: + ItemSelectionDialog(const std::string& label, ContainerBase::Filter filter, MWBase::WindowManager& parWindowManager); + + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Item; + + EventHandle_Item eventItemSelected; + EventHandle_Void eventDialogCanceled; + + + private: + virtual void onReferenceUnavailable() { ; } + + virtual void onSelectedItemImpl(MWWorld::Ptr item); + + void onCancelButtonClicked(MyGUI::Widget* sender); + }; + +} diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 8b502ce7c..2d6f69871 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -32,7 +32,9 @@ namespace MWGui GM_Review, // interactive MessageBox - GM_InterMessageBox + GM_InterMessageBox, + + GM_QuickKeysMenu }; // Windows shown in inventory mode diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp new file mode 100644 index 000000000..a9aa3e616 --- /dev/null +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -0,0 +1,237 @@ +#include "quickkeysmenu.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" + +#include "windowmanagerimp.hpp" +#include "itemselection.hpp" + +namespace MWGui +{ + + QuickKeysMenu::QuickKeysMenu(MWBase::WindowManager& parWindowManager) + : WindowBase("openmw_quickkeys_menu.layout", parWindowManager) + , mAssignDialog(0) + , mItemSelectionDialog(0) + , mMagicSelectionDialog(0) + { + getWidget(mOkButton, "OKButton"); + getWidget(mInstructionLabel, "InstructionLabel"); + + mMainWidget->setSize(mMainWidget->getWidth(), + mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight())); + + int okButtonWidth = mOkButton->getTextSize ().width + 24; + mOkButton->setCoord(mOkButton->getLeft() - (okButtonWidth - mOkButton->getWidth()), + mOkButton->getTop(), + okButtonWidth, + mOkButton->getHeight()); + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked); + + center(); + + + for (int i = 0; i < 10; ++i) + { + MyGUI::Button* button; + getWidget(button, "QuickKey" + boost::lexical_cast(i+1)); + + if (i != 9) // 10th quick key is always set to hand-to-hand + button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + unassign(button, i); + + mQuickKeyButtons.push_back(button); + + } + } + + QuickKeysMenu::~QuickKeysMenu() + { + delete mAssignDialog; + } + + void QuickKeysMenu::unassign(MyGUI::Widget* key, int index) + { + while (key->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (key->getChildAt(0)); + + MyGUI::TextBox* textBox = key->createWidgetReal("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); + textBox->setTextAlign (MyGUI::Align::Center); + textBox->setCaption (boost::lexical_cast(index+1)); + textBox->setNeedMouseFocus (false); + } + + void QuickKeysMenu::onQuickKeyButtonClicked(MyGUI::Widget* sender) + { + int index = -1; + for (int i = 0; i < 10; ++i) + { + if (sender == mQuickKeyButtons[i] || sender->getParent () == mQuickKeyButtons[i]) + { + index = i; + break; + } + } + assert(index != -1); + mSelectedIndex = index; + + { + // open assign dialog + if (!mAssignDialog) + mAssignDialog = new QuickKeysMenuAssign(mWindowManager, this); + mAssignDialog->setVisible (true); + } + } + + void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender) + { + mWindowManager.removeGuiMode(GM_QuickKeysMenu); + } + + + void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender) + { + if (!mItemSelectionDialog ) + { + mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}", ContainerBase::Filter_All, mWindowManager); + mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem); + mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItemCancel); + } + mItemSelectionDialog->setVisible(true); + mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + mItemSelectionDialog->drawItems (); + + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender) + { + if (!mMagicSelectionDialog ) + { + mMagicSelectionDialog = new MagicSelectionDialog(mWindowManager, this); + } + mMagicSelectionDialog->setVisible(true); + + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender) + { + unassign(mQuickKeyButtons[mSelectedIndex], mSelectedIndex); + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender) + { + mAssignDialog->setVisible (false); + } + + void QuickKeysMenu::onAssignItem(MWWorld::Ptr item) + { + MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; + while (button->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); + + MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + image->setUserString ("ToolTipType", "ItemPtr"); + image->setUserData(item); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + mItemSelectionDialog->setVisible(false); + } + + void QuickKeysMenu::onAssignItemCancel() + { + mItemSelectionDialog->setVisible(false); + } + + void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) + { + + } + + void QuickKeysMenu::onAssignMagic (const std::string& spellId) + { + + } + + void QuickKeysMenu::onAssignMagicCancel () + { + mMagicSelectionDialog->setVisible(false); + } + + // --------------------------------------------------------------------------------------------------------- + + QuickKeysMenuAssign::QuickKeysMenuAssign (MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) + : WindowModal("openmw_quickkeys_menu_assign.layout", parWindowManager) + , mParent(parent) + { + getWidget(mLabel, "Label"); + getWidget(mItemButton, "ItemButton"); + getWidget(mMagicButton, "MagicButton"); + getWidget(mUnassignButton, "UnassignButton"); + getWidget(mCancelButton, "CancelButton"); + + mItemButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onItemButtonClicked); + mMagicButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onMagicButtonClicked); + mUnassignButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onUnassignButtonClicked); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(mParent, &QuickKeysMenu::onCancelButtonClicked); + + + int maxWidth = mItemButton->getTextSize ().width + 24; + maxWidth = std::max(maxWidth, mMagicButton->getTextSize ().width + 24); + maxWidth = std::max(maxWidth, mUnassignButton->getTextSize ().width + 24); + maxWidth = std::max(maxWidth, mCancelButton->getTextSize ().width + 24); + + mMainWidget->setSize(maxWidth + 24, mMainWidget->getHeight()); + mLabel->setSize(maxWidth, mLabel->getHeight()); + + mItemButton->setCoord((maxWidth - mItemButton->getTextSize().width-24)/2 + 8, + mItemButton->getTop(), + mItemButton->getTextSize().width + 24, + mItemButton->getHeight()); + mMagicButton->setCoord((maxWidth - mMagicButton->getTextSize().width-24)/2 + 8, + mMagicButton->getTop(), + mMagicButton->getTextSize().width + 24, + mMagicButton->getHeight()); + mUnassignButton->setCoord((maxWidth - mUnassignButton->getTextSize().width-24)/2 + 8, + mUnassignButton->getTop(), + mUnassignButton->getTextSize().width + 24, + mUnassignButton->getHeight()); + mCancelButton->setCoord((maxWidth - mCancelButton->getTextSize().width-24)/2 + 8, + mCancelButton->getTop(), + mCancelButton->getTextSize().width + 24, + mCancelButton->getHeight()); + + center(); + } + + + // --------------------------------------------------------------------------------------------------------- + + MagicSelectionDialog::MagicSelectionDialog(MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) + : WindowModal("openmw_magicselection_dialog.layout", parWindowManager) + , mParent(parent) + { + getWidget(mCancelButton, "CancelButton"); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); + + center(); + } + + void MagicSelectionDialog::onCancelButtonClicked (MyGUI::Widget *sender) + { + mParent->onAssignMagicCancel (); + } + +} diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp new file mode 100644 index 000000000..496806109 --- /dev/null +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -0,0 +1,84 @@ +#ifndef MWGUI_QUICKKEYS_H +#define MWGUI_QUICKKEYS_H + + +#include "../mwworld/ptr.hpp" + +#include "window_base.hpp" + +namespace MWGui +{ + + class QuickKeysMenuAssign; + class ItemSelectionDialog; + class MagicSelectionDialog; + + class QuickKeysMenu : public WindowBase + { + public: + QuickKeysMenu(MWBase::WindowManager& parWindowManager); + ~QuickKeysMenu(); + + + void onItemButtonClicked(MyGUI::Widget* sender); + void onMagicButtonClicked(MyGUI::Widget* sender); + void onUnassignButtonClicked(MyGUI::Widget* sender); + void onCancelButtonClicked(MyGUI::Widget* sender); + + void onAssignItem (MWWorld::Ptr item); + void onAssignItemCancel (); + void onAssignMagicItem (MWWorld::Ptr item); + void onAssignMagic (const std::string& spellId); + void onAssignMagicCancel (); + + + private: + MyGUI::EditBox* mInstructionLabel; + MyGUI::Button* mOkButton; + + std::vector mQuickKeyButtons; + + QuickKeysMenuAssign* mAssignDialog; + ItemSelectionDialog* mItemSelectionDialog; + MagicSelectionDialog* mMagicSelectionDialog; + + int mSelectedIndex; + + + void onQuickKeyButtonClicked(MyGUI::Widget* sender); + void onOkButtonClicked(MyGUI::Widget* sender); + + void unassign(MyGUI::Widget* key, int index); + }; + + class QuickKeysMenuAssign : public WindowModal + { + public: + QuickKeysMenuAssign(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + + private: + MyGUI::TextBox* mLabel; + MyGUI::Button* mItemButton; + MyGUI::Button* mMagicButton; + MyGUI::Button* mUnassignButton; + MyGUI::Button* mCancelButton; + + QuickKeysMenu* mParent; + }; + + class MagicSelectionDialog : public WindowModal + { + public: + MagicSelectionDialog(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + + private: + MyGUI::Button* mCancelButton; + + QuickKeysMenu* mParent; + + void onCancelButtonClicked (MyGUI::Widget* sender); + }; +} + + +#endif diff --git a/apps/openmw/mwgui/window_base.cpp b/apps/openmw/mwgui/window_base.cpp index dbb37efbb..38bee9ea3 100644 --- a/apps/openmw/mwgui/window_base.cpp +++ b/apps/openmw/mwgui/window_base.cpp @@ -12,17 +12,15 @@ WindowBase::WindowBase(const std::string& parLayout, MWBase::WindowManager& parW { } -void WindowBase::open() -{ -} - void WindowBase::setVisible(bool visible) { bool wasVisible = mMainWidget->getVisible(); mMainWidget->setVisible(visible); - if (!wasVisible && visible) + if (visible) open(); + else if (wasVisible && !visible) + close(); } void WindowBase::center() @@ -40,3 +38,18 @@ void WindowBase::center() coord.top = (gameWindowSize.height - coord.height)/2; mMainWidget->setCoord(coord); } + +WindowModal::WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager) + : WindowBase(parLayout, parWindowManager) +{ +} + +void WindowModal::open() +{ + MyGUI::InputManager::getInstance ().addWidgetModal (mMainWidget); +} + +void WindowModal::close() +{ + MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); +} diff --git a/apps/openmw/mwgui/window_base.hpp b/apps/openmw/mwgui/window_base.hpp index 74d874bb8..afdf4d065 100644 --- a/apps/openmw/mwgui/window_base.hpp +++ b/apps/openmw/mwgui/window_base.hpp @@ -20,8 +20,9 @@ namespace MWGui // Events typedef MyGUI::delegates::CMultiDelegate1 EventHandle_WindowBase; - virtual void open(); - virtual void setVisible(bool visible); // calls open() if visible is true and was false before + virtual void open() {} + virtual void close () {} + virtual void setVisible(bool visible); void center(); /** Event : Dialog finished, OK button clicked.\n @@ -33,6 +34,18 @@ namespace MWGui /// \todo remove MWBase::WindowManager& mWindowManager; }; + + + /* + * "Modal" windows cause the rest of the interface to be unaccessible while they are visible + */ + class WindowModal : public WindowBase + { + public: + WindowModal(const std::string& parLayout, MWBase::WindowManager& parWindowManager); + virtual void open(); + virtual void close(); + }; } #endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 676eb2046..f1ee01c4d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -41,6 +41,7 @@ #include "confirmationdialog.hpp" #include "alchemywindow.hpp" #include "spellwindow.hpp" +#include "quickkeysmenu.hpp" using namespace MWGui; @@ -133,6 +134,7 @@ WindowManager::WindowManager( mConfirmationDialog = new ConfirmationDialog(*this); mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); + mQuickKeysMenu = new QuickKeysMenu(*this); mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); @@ -226,6 +228,7 @@ void WindowManager::updateVisible() mSettingsWindow->setVisible(false); mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); + mQuickKeysMenu->setVisible(false); // Mouse is visible whenever we're not in game mode MyGUI::PointerManager::getInstance().setVisible(isGuiMode()); @@ -254,6 +257,9 @@ void WindowManager::updateVisible() GuiMode mode = mGuiModes.back(); switch(mode) { + case GM_QuickKeysMenu: + mQuickKeysMenu->setVisible (true); + break; case GM_MainMenu: mMenu->setVisible(true); break; @@ -652,6 +658,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mAlchemyWindow->center(); mScrollWindow->center(); mBookWindow->center(); + mQuickKeysMenu->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); mInputBlocker->setSize(MyGUI::IntSize(x,y)); } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 391305594..f27522986 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -60,6 +60,7 @@ namespace MWGui class MessageBoxManager; class SettingsWindow; class AlchemyWindow; + class QuickKeysMenu; class WindowManager : public MWBase::WindowManager { @@ -209,6 +210,7 @@ namespace MWGui ConfirmationDialog* mConfirmationDialog; AlchemyWindow* mAlchemyWindow; SpellWindow* mSpellWindow; + QuickKeysMenu* mQuickKeysMenu; CharacterCreation* mCharGen; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index a7cce25c0..8f4fde7a4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -188,7 +188,40 @@ namespace MWInput case A_ToggleSpell: toggleSpell (); break; - } + case A_QuickKey1: + quickKey(1); + break; + case A_QuickKey2: + quickKey(2); + break; + case A_QuickKey3: + quickKey(3); + break; + case A_QuickKey4: + quickKey(4); + break; + case A_QuickKey5: + quickKey(5); + break; + case A_QuickKey6: + quickKey(6); + break; + case A_QuickKey7: + quickKey(7); + break; + case A_QuickKey8: + quickKey(8); + break; + case A_QuickKey9: + quickKey(9); + break; + case A_QuickKey10: + quickKey(10); + break; + case A_QuickKeysMenu: + showQuickKeysMenu(); + break; + } } } @@ -545,6 +578,17 @@ namespace MWInput // .. but don't touch any other mode. } + void InputManager::quickKey (int index) + { + std::cout << "quick key " << index << std::endl; + } + + void InputManager::showQuickKeysMenu() + { + if (!mWindows.isGuiMode ()) + mWindows.pushGuiMode (MWGui::GM_QuickKeysMenu); + } + void InputManager::activate() { mEngine.activate(); @@ -587,7 +631,8 @@ namespace MWInput defaultKeyBindings[A_MoveRight] = OIS::KC_D; defaultKeyBindings[A_ToggleWeapon] = OIS::KC_F; defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; - defaultKeyBindings[A_Console] = OIS::KC_F1; + defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1; + defaultKeyBindings[A_Console] = OIS::KC_F2; defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL; defaultKeyBindings[A_AutoMove] = OIS::KC_Q; defaultKeyBindings[A_Jump] = OIS::KC_E; @@ -595,9 +640,20 @@ namespace MWInput defaultKeyBindings[A_Rest] = OIS::KC_T; defaultKeyBindings[A_GameMenu] = OIS::KC_ESCAPE; defaultKeyBindings[A_TogglePOV] = OIS::KC_TAB; + defaultKeyBindings[A_QuickKey1] = OIS::KC_1; + defaultKeyBindings[A_QuickKey2] = OIS::KC_2; + defaultKeyBindings[A_QuickKey3] = OIS::KC_3; + defaultKeyBindings[A_QuickKey4] = OIS::KC_4; + defaultKeyBindings[A_QuickKey5] = OIS::KC_5; + defaultKeyBindings[A_QuickKey6] = OIS::KC_6; + defaultKeyBindings[A_QuickKey7] = OIS::KC_7; + defaultKeyBindings[A_QuickKey8] = OIS::KC_8; + defaultKeyBindings[A_QuickKey9] = OIS::KC_9; + defaultKeyBindings[A_QuickKey10] = OIS::KC_0; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; + defaultMouseButtonBindings[A_Use] = OIS::MB_Left; for (int i = 0; i < A_Last; ++i) { @@ -645,6 +701,17 @@ namespace MWInput descriptions[A_Rest] = "sRestKey"; descriptions[A_Inventory] = "sInventory"; descriptions[A_TogglePOV] = "sTogglePOVCmd"; + descriptions[A_QuickKeysMenu] = "sQuickMenu"; + descriptions[A_QuickKey1] = "sQuick1Cmd"; + descriptions[A_QuickKey2] = "sQuick2Cmd"; + descriptions[A_QuickKey3] = "sQuick3Cmd"; + descriptions[A_QuickKey4] = "sQuick4Cmd"; + descriptions[A_QuickKey5] = "sQuick5Cmd"; + descriptions[A_QuickKey6] = "sQuick6Cmd"; + descriptions[A_QuickKey7] = "sQuick7Cmd"; + descriptions[A_QuickKey8] = "sQuick8Cmd"; + descriptions[A_QuickKey9] = "sQuick9Cmd"; + descriptions[A_QuickKey10] = "sQuick10Cmd"; if (descriptions[action] == "") return ""; // not configurable @@ -685,6 +752,17 @@ namespace MWInput ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); + ret.push_back(A_QuickKeysMenu); + ret.push_back(A_QuickKey1); + ret.push_back(A_QuickKey2); + ret.push_back(A_QuickKey3); + ret.push_back(A_QuickKey4); + ret.push_back(A_QuickKey5); + ret.push_back(A_QuickKey6); + ret.push_back(A_QuickKey7); + ret.push_back(A_QuickKey8); + ret.push_back(A_QuickKey9); + ret.push_back(A_QuickKey10); return ret; } diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 71d43b8d5..a7a2df852 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -168,6 +168,9 @@ namespace MWInput void toggleAutoMove(); void exitNow(); + void quickKey (int index); + void showQuickKeysMenu(); + bool actionIsActive (int id); void loadKeyDefaults(bool force = false); @@ -220,6 +223,19 @@ namespace MWInput A_TogglePOV, + A_QuickKey1, + A_QuickKey2, + A_QuickKey3, + A_QuickKey4, + A_QuickKey5, + A_QuickKey6, + A_QuickKey7, + A_QuickKey8, + A_QuickKey9, + A_QuickKey10, + + A_QuickKeysMenu, + A_Last // Marker for the last item }; }; diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 3a5430c6f..2e448a369 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -65,6 +65,10 @@ set(MYGUI_FILES openmw_tooltips.layout openmw_trade_window.layout openmw_windows.skin.xml + openmw_quickkeys_menu.layout + openmw_quickkeys_menu_assign.layout + openmw_itemselection_dialog.layout + openmw_magicselection_dialog.layout smallbars.png VeraMono.ttf ) diff --git a/files/mygui/openmw_itemselection_dialog.layout b/files/mygui/openmw_itemselection_dialog.layout new file mode 100644 index 000000000..93681e69e --- /dev/null +++ b/files/mygui/openmw_itemselection_dialog.layout @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout new file mode 100644 index 000000000..7834b0ed9 --- /dev/null +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_quickkeys_menu.layout b/files/mygui/openmw_quickkeys_menu.layout new file mode 100644 index 000000000..a45f2896f --- /dev/null +++ b/files/mygui/openmw_quickkeys_menu.layout @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_quickkeys_menu_assign.layout b/files/mygui/openmw_quickkeys_menu_assign.layout new file mode 100644 index 000000000..4bbd9f1fa --- /dev/null +++ b/files/mygui/openmw_quickkeys_menu_assign.layout @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c0f27bd5ef3f1420dd88aef367bc49d4788c9879 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 26 Aug 2012 11:37:33 +0200 Subject: [PATCH 475/688] magic selection window --- apps/openmw/mwgui/quickkeysmenu.cpp | 257 +++++++++++++++++- apps/openmw/mwgui/quickkeysmenu.hpp | 13 + .../mygui/openmw_magicselection_dialog.layout | 2 +- 3 files changed, 270 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index a9aa3e616..6739b3d1b 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -5,10 +5,33 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "windowmanagerimp.hpp" #include "itemselection.hpp" + +namespace +{ + bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) + { + int cmp = MWWorld::Class::get(left).getName(left).compare( + MWWorld::Class::get(right).getName(right)); + return cmp < 0; + } + + bool sortSpells(const std::string& left, const std::string& right) + { + const ESM::Spell* a = MWBase::Environment::get().getWorld()->getStore().spells.find(left); + const ESM::Spell* b = MWBase::Environment::get().getWorld()->getStore().spells.find(right); + + int cmp = a->name.compare(b->name); + return cmp < 0; + } +} + namespace MWGui { @@ -157,12 +180,35 @@ namespace MWGui void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) { - + onAssignItem(item); + mMagicSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignMagic (const std::string& spellId) { + MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; + while (button->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); + MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + image->setUserString ("ToolTipType", "Spell"); + image->setUserString ("Spell", spellId); + + // use the icon of the first effect + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); + const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().magicEffects.find(spell->effects.list.front().effectID); + std::string path = effect->icon; + int slashPos = path.find("\\"); + path.insert(slashPos+1, "b_"); + path = std::string("icons\\") + path; + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + + image->setImageTexture (path); + image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + mMagicSelectionDialog->setVisible(false); } void QuickKeysMenu::onAssignMagicCancel () @@ -222,8 +268,11 @@ namespace MWGui MagicSelectionDialog::MagicSelectionDialog(MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) : WindowModal("openmw_magicselection_dialog.layout", parWindowManager) , mParent(parent) + , mWidth(0) + , mHeight(0) { getWidget(mCancelButton, "CancelButton"); + getWidget(mMagicList, "MagicList"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); center(); @@ -234,4 +283,210 @@ namespace MWGui mParent->onAssignMagicCancel (); } + void MagicSelectionDialog::open () + { + WindowModal::open(); + + while (mMagicList->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (mMagicList->getChildAt (0)); + + mHeight = 0; + + const int spellHeight = 18; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + + /// \todo lots of copy&pasted code from SpellWindow + + // retrieve powers & spells, sort by name + std::vector spellList; + + for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) + { + spellList.push_back(*it); + } + + std::vector powers; + std::vector::iterator it = spellList.begin(); + while (it != spellList.end()) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it); + if (spell->data.type == ESM::Spell::ST_Power) + { + powers.push_back(*it); + it = spellList.erase(it); + } + else if (spell->data.type == ESM::Spell::ST_Ability + || spell->data.type == ESM::Spell::ST_Blight + || spell->data.type == ESM::Spell::ST_Curse + || spell->data.type == ESM::Spell::ST_Disease) + { + it = spellList.erase(it); + } + else + ++it; + } + std::sort(powers.begin(), powers.end(), sortSpells); + std::sort(spellList.begin(), spellList.end(), sortSpells); + + // retrieve usable magic items & sort + std::vector items; + for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it) + { + std::string enchantId = MWWorld::Class::get(*it).getEnchantment(*it); + if (enchantId != "") + { + // only add items with "Cast once" or "Cast on use" + const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().enchants.find(enchantId); + int type = enchant->data.type; + if (type != ESM::Enchantment::CastOnce + && type != ESM::Enchantment::WhenUsed) + continue; + + items.push_back(*it); + } + } + std::sort(items.begin(), items.end(), sortItems); + + + int height = estimateHeight(items.size() + powers.size() + spellList.size()); + bool scrollVisible = height > mMagicList->getHeight(); + mWidth = mMagicList->getWidth() - scrollVisible * 18; + + + // powers + addGroup("#{sPowers}", ""); + + for (std::vector::const_iterator it = powers.begin(); it != powers.end(); ++it) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it); + MyGUI::Button* t = mMagicList->createWidget("SpellText", + MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setCaption(spell->name); + t->setTextAlign(MyGUI::Align::Left); + t->setUserString("ToolTipType", "Spell"); + t->setUserString("Spell", *it); + t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); + t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onSpellSelected); + + mHeight += spellHeight; + } + + // other spells + addGroup("#{sSpells}", ""); + for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) + { + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(*it); + MyGUI::Button* t = mMagicList->createWidget("SpellText", + MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setCaption(spell->name); + t->setTextAlign(MyGUI::Align::Left); + t->setUserString("ToolTipType", "Spell"); + t->setUserString("Spell", *it); + t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); + t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onSpellSelected); + + mHeight += spellHeight; + } + + + // enchanted items + addGroup("#{sMagicItem}", ""); + + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + { + MWWorld::Ptr item = *it; + + // check if the item is currently equipped (will display in a different color) + bool equipped = false; + for (int i=0; i < MWWorld::InventoryStore::Slots; ++i) + { + if (store.getSlot(i) != store.end() && *store.getSlot(i) == item) + { + equipped = true; + break; + } + } + + MyGUI::Button* t = mMagicList->createWidget(equipped ? "SpellText" : "SpellTextUnequipped", + MyGUI::IntCoord(4, mHeight, mWidth-8, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); + t->setCaption(MWWorld::Class::get(item).getName(item)); + t->setTextAlign(MyGUI::Align::Left); + t->setUserData(item); + t->setUserString("ToolTipType", "ItemPtr"); + t->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onEnchantedItemSelected); + t->eventMouseWheel += MyGUI::newDelegate(this, &MagicSelectionDialog::onMouseWheel); + + mHeight += spellHeight; + } + + + mMagicList->setCanvasSize (mWidth, std::max(mMagicList->getHeight(), mHeight)); + + } + + void MagicSelectionDialog::addGroup(const std::string &label, const std::string& label2) + { + if (mMagicList->getChildCount() > 0) + { + MyGUI::ImageBox* separator = mMagicList->createWidget("MW_HLine", + MyGUI::IntCoord(4, mHeight, mWidth-8, 18), + MyGUI::Align::Left | MyGUI::Align::Top); + separator->setNeedMouseFocus(false); + mHeight += 18; + } + + MyGUI::TextBox* groupWidget = mMagicList->createWidget("SandBrightText", + MyGUI::IntCoord(0, mHeight, mWidth, 24), + MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + groupWidget->setCaptionWithReplacing(label); + groupWidget->setTextAlign(MyGUI::Align::Left); + groupWidget->setNeedMouseFocus(false); + + if (label2 != "") + { + MyGUI::TextBox* groupWidget2 = mMagicList->createWidget("SandBrightText", + MyGUI::IntCoord(0, mHeight, mWidth-4, 24), + MyGUI::Align::Left | MyGUI::Align::Top); + groupWidget2->setCaptionWithReplacing(label2); + groupWidget2->setTextAlign(MyGUI::Align::Right); + groupWidget2->setNeedMouseFocus(false); + } + + mHeight += 24; + } + + + void MagicSelectionDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mMagicList->getViewOffset().top + _rel*0.3 > 0) + mMagicList->setViewOffset(MyGUI::IntPoint(0, 0)); + else + mMagicList->setViewOffset(MyGUI::IntPoint(0, mMagicList->getViewOffset().top + _rel*0.3)); + } + + void MagicSelectionDialog::onEnchantedItemSelected(MyGUI::Widget* _sender) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr item = *_sender->getUserData(); + + mParent->onAssignMagicItem (item); + } + + void MagicSelectionDialog::onSpellSelected(MyGUI::Widget* _sender) + { + mParent->onAssignMagic (_sender->getUserString("Spell")); + } + + int MagicSelectionDialog::estimateHeight(int numSpells) const + { + int height = 0; + height += 24 * 3 + 18 * 2; // group headings + height += numSpells * 18; + return height; + } + } diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 496806109..44e48d3b0 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -71,12 +71,25 @@ namespace MWGui public: MagicSelectionDialog(MWBase::WindowManager& parWindowManager, QuickKeysMenu* parent); + virtual void open(); + private: MyGUI::Button* mCancelButton; + MyGUI::ScrollView* mMagicList; + + int mWidth; + int mHeight; QuickKeysMenu* mParent; void onCancelButtonClicked (MyGUI::Widget* sender); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void onEnchantedItemSelected(MyGUI::Widget* _sender); + void onSpellSelected(MyGUI::Widget* _sender); + int estimateHeight(int numSpells) const; + + + void addGroup(const std::string& label, const std::string& label2); }; } diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout index 7834b0ed9..bf942b32f 100644 --- a/files/mygui/openmw_magicselection_dialog.layout +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -7,7 +7,7 @@ - + From 5cbb08fee16cae95325f957bcd26f8f3f4116636 Mon Sep 17 00:00:00 2001 From: Douglas Diniz Date: Sun, 26 Aug 2012 11:47:45 -0300 Subject: [PATCH 476/688] Task 339 - Moving all sounds to actions --- apps/openmw/mwclass/door.cpp | 8 +------- apps/openmw/mwworld/action.cpp | 14 ++++++++------ apps/openmw/mwworld/action.hpp | 8 +++++--- apps/openmw/mwworld/actionteleport.cpp | 6 +++++- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index bf5dd4bba..f483aa3a8 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -6,8 +6,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwbase/soundmanager.hpp" - #include "../mwworld/player.hpp" #include "../mwworld/ptr.hpp" @@ -108,13 +106,9 @@ namespace MWClass /// \todo remove this if clause once ActionTeleport can also support other actors if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) { - // the player is using the door - // The reason this is not 3D is that it would get interrupted when you teleport - //MWBase::Environment::get().getSoundManager()->playSound3D(ptr,openSound, 1.0, 1.0); - boost::shared_ptr action(new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); - action->setSound(openSound, true); + action->setSound(openSound); return action; } diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 15bae4cd4..90e30a5bf 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -2,9 +2,13 @@ #include "action.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "../mwbase/soundmanager.hpp" -MWWorld::Action::Action() {} +MWWorld::Action::Action() { + teleport = false; +} MWWorld::Action::~Action() {} @@ -12,15 +16,14 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) { - if (onActor) + if (teleport == true) { - std::cout << "Douglas - Som Normal" << std::endl; + //this is a teleport action, so we need to call playSound MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); } else { - std::cout << "Douglas - Som 3D" << std::endl; MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); } @@ -29,8 +32,7 @@ void MWWorld::Action::execute (const Ptr& actor) executeImp (actor); } -void MWWorld::Action::setSound (const std::string& id, const bool onActorValue) +void MWWorld::Action::setSound (const std::string& id) { mSoundId = id; - onActor = onActorValue; } diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index f9ad975d3..01180a778 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -11,7 +11,6 @@ namespace MWWorld class Action { std::string mSoundId; - bool onActor; // not implemented Action (const Action& action); @@ -19,7 +18,10 @@ namespace MWWorld virtual void executeImp (const Ptr& actor) = 0; - public: + protected: + bool teleport; //True if the action will teleport the actor + + public: Action(); @@ -27,7 +29,7 @@ namespace MWWorld void execute (const Ptr& actor); - void setSound (const std::string& id, const bool onActor = false); + void setSound (const std::string& id); }; } diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 9c87d37ae..e65873223 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -9,10 +9,14 @@ namespace MWWorld ActionTeleport::ActionTeleport (const std::string& cellName, const ESM::Position& position) : mCellName (cellName), mPosition (position) - {} + { + teleport = true; + } void ActionTeleport::executeImp (const Ptr& actor) { + teleport = true; + if (mCellName.empty()) MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); else From d0cebea580b6148f7b175dcc36fc8f2516173df0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 26 Aug 2012 18:50:47 +0200 Subject: [PATCH 477/688] some cleanup --- apps/openmw/mwworld/action.cpp | 27 +++++++++++++------------- apps/openmw/mwworld/action.hpp | 7 +++---- apps/openmw/mwworld/actionteleport.cpp | 5 +---- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 90e30a5bf..748f6aff7 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -6,9 +6,8 @@ #include "../mwbase/soundmanager.hpp" -MWWorld::Action::Action() { - teleport = false; -} +MWWorld::Action::Action (bool teleport) : mTeleport (teleport) +{} MWWorld::Action::~Action() {} @@ -16,17 +15,17 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) { - if (teleport == true) - { - //this is a teleport action, so we need to call playSound - MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); - } - else - { - MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); - } + if (mTeleport == true) + { + //this is a teleport action, so we need to call playSound + MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } + else + { + MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, + MWBase::SoundManager::Play_NoTrack); + } } executeImp (actor); diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index 01180a778..511a7002d 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -11,6 +11,7 @@ namespace MWWorld class Action { std::string mSoundId; + bool mTeleport; // not implemented Action (const Action& action); @@ -18,12 +19,10 @@ namespace MWWorld virtual void executeImp (const Ptr& actor) = 0; - protected: - bool teleport; //True if the action will teleport the actor - public: - Action(); + Action (bool teleport = false); + ///< \param teleport action will teleport the actor virtual ~Action(); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index e65873223..ae5ffc3b9 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -8,15 +8,12 @@ namespace MWWorld { ActionTeleport::ActionTeleport (const std::string& cellName, const ESM::Position& position) - : mCellName (cellName), mPosition (position) + : Action (true), mCellName (cellName), mPosition (position) { - teleport = true; } void ActionTeleport::executeImp (const Ptr& actor) { - teleport = true; - if (mCellName.empty()) MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); else From cc4d55a7557a9976a8a5ea25a6e0e66191d1555c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 26 Aug 2012 20:59:19 +0200 Subject: [PATCH 478/688] updated credits.txt --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index c7ec4936e..62e41ff0b 100644 --- a/credits.txt +++ b/credits.txt @@ -16,6 +16,7 @@ Artem Kotsynyak (greye) athile BrotherBrick Cris Mihalache (Mirceam) +Douglas Diniz (Dgdiniz) Eli2 gugus / gus Jacob Essex (Yacoby) From b1a394552dd6c2f5b406a68514591d41340ab343 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 10:01:53 +0200 Subject: [PATCH 479/688] mouse click sounds --- apps/openmw/mwinput/inputmanagerimp.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 8f4fde7a4..40091f358 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include #include @@ -21,6 +23,7 @@ #include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" namespace MWInput { @@ -442,6 +445,15 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, MyGUI::MouseButton::Enum(id)); + if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) + { + MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); + if (b) + { + MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); + } + } + return true; } From c5e55d3cac140872de52455bdf1b9f658632d01d Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 15:51:01 +0200 Subject: [PATCH 480/688] fix screenshot function --- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwgui/quickkeysmenu.cpp | 82 +++++++++++++++++-- apps/openmw/mwgui/quickkeysmenu.hpp | 10 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 8 +- .../mygui/openmw_itemselection_dialog.layout | 8 +- 7 files changed, 102 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fde965256..589ab4a4b 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -171,6 +171,8 @@ namespace MWBase virtual void setWeaponVisibility(bool visible) = 0; virtual void setSpellVisibility(bool visible) = 0; + virtual void activateQuickKey (int index) = 0; + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) = 0; diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 6739b3d1b..853e2ed14 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -82,6 +82,8 @@ namespace MWGui while (key->getChildCount ()) MyGUI::Gui::getInstance ().destroyWidget (key->getChildAt(0)); + key->setUserData(Type_Unassigned); + MyGUI::TextBox* textBox = key->createWidgetReal("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); textBox->setTextAlign (MyGUI::Align::Center); textBox->setCaption (boost::lexical_cast(index+1)); @@ -159,16 +161,25 @@ namespace MWGui while (button->getChildCount ()) MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); - MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); - image->setUserString ("ToolTipType", "ItemPtr"); - image->setUserData(item); + button->setUserData(Type_Item); + + MyGUI::ImageBox* frame = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + std::string backgroundTex = "textures\\menu_icon_barter.dds"; + frame->setImageTexture (backgroundTex); + frame->setImageCoord (MyGUI::IntCoord(4, 4, 40, 40)); + frame->setUserString ("ToolTipType", "ItemPtr"); + frame->setUserData(item); + frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + + MyGUI::ImageBox* image = frame->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); std::string path = std::string("icons\\"); path += MWWorld::Class::get(item).getInventoryIcon(item); int pos = path.rfind("."); path.erase(pos); path.append(".dds"); image->setImageTexture (path); - image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + image->setNeedMouseFocus (false); mItemSelectionDialog->setVisible(false); } @@ -180,7 +191,29 @@ namespace MWGui void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) { - onAssignItem(item); + MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; + while (button->getChildCount ()) + MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); + + button->setUserData(Type_MagicItem); + + MyGUI::ImageBox* frame = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + std::string backgroundTex = "textures\\menu_icon_select_magic_magic.dds"; + frame->setImageTexture (backgroundTex); + frame->setImageCoord (MyGUI::IntCoord(2, 2, 40, 40)); + frame->setUserString ("ToolTipType", "ItemPtr"); + frame->setUserData(item); + frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + MyGUI::ImageBox* image = frame->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + std::string path = std::string("icons\\"); + path += MWWorld::Class::get(item).getInventoryIcon(item); + int pos = path.rfind("."); + path.erase(pos); + path.append(".dds"); + image->setImageTexture (path); + image->setNeedMouseFocus (false); + mMagicSelectionDialog->setVisible(false); } @@ -190,9 +223,17 @@ namespace MWGui while (button->getChildCount ()) MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); - MyGUI::ImageBox* image = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); - image->setUserString ("ToolTipType", "Spell"); - image->setUserString ("Spell", spellId); + button->setUserData(Type_Magic); + + MyGUI::ImageBox* frame = button->createWidget("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); + std::string backgroundTex = "textures\\menu_icon_select_magic.dds"; + frame->setImageTexture (backgroundTex); + frame->setImageCoord (MyGUI::IntCoord(2, 2, 40, 40)); + frame->setUserString ("ToolTipType", "Spell"); + frame->setUserString ("Spell", spellId); + frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + + MyGUI::ImageBox* image = frame->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); // use the icon of the first effect const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); @@ -206,7 +247,7 @@ namespace MWGui path.append(".dds"); image->setImageTexture (path); - image->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + image->setNeedMouseFocus (false); mMagicSelectionDialog->setVisible(false); } @@ -216,6 +257,29 @@ namespace MWGui mMagicSelectionDialog->setVisible(false); } + void QuickKeysMenu::activateQuickKey(int index) + { + MyGUI::Button* button = mQuickKeyButtons[index-1]; + + QuickKeyType type = *button->getUserData(); + + if (type == Type_Magic) + { + std::string spellId = button->getChildAt(0)->getUserString("Spell"); + MWBase::Environment::get().getWindowManager ()->setSelectedSpell (spellId, 100); + } + else if (type == Type_Item) + { + MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); + MWBase::Environment::get().getWindowManager ()->setSelectedWeapon(item, 100); + } + else if (type == Type_MagicItem) + { + MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); + MWBase::Environment::get().getWindowManager ()->setSelectedEnchantItem (item, 100); + } + } + // --------------------------------------------------------------------------------------------------------- QuickKeysMenuAssign::QuickKeysMenuAssign (MWBase::WindowManager &parWindowManager, QuickKeysMenu* parent) diff --git a/apps/openmw/mwgui/quickkeysmenu.hpp b/apps/openmw/mwgui/quickkeysmenu.hpp index 44e48d3b0..345ffa0c8 100644 --- a/apps/openmw/mwgui/quickkeysmenu.hpp +++ b/apps/openmw/mwgui/quickkeysmenu.hpp @@ -31,6 +31,16 @@ namespace MWGui void onAssignMagic (const std::string& spellId); void onAssignMagicCancel (); + void activateQuickKey(int index); + + enum QuickKeyType + { + Type_Item, + Type_Magic, + Type_MagicItem, + Type_Unassigned + }; + private: MyGUI::EditBox* mInstructionLabel; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f1ee01c4d..138349c2e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -854,3 +854,8 @@ void WindowManager::notifyInputActionBound () mSettingsWindow->updateControlsBox (); allowMouse(); } + +void WindowManager::activateQuickKey (int index) +{ + mQuickKeysMenu->activateQuickKey(index); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index f27522986..8b011e259 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -152,6 +152,8 @@ namespace MWGui virtual void setWeaponVisibility(bool visible); virtual void setSpellVisibility(bool visible); + virtual void activateQuickKey (int index); + virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent); virtual void setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 49b9a08a8..7daafc726 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -599,7 +599,7 @@ namespace MWInput void InputManager::quickKey (int index) { - std::cout << "quick key " << index << std::endl; + mWindows.activateQuickKey (index); } void InputManager::showQuickKeysMenu() @@ -669,6 +669,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey8] = OIS::KC_8; defaultKeyBindings[A_QuickKey9] = OIS::KC_9; defaultKeyBindings[A_QuickKey10] = OIS::KC_0; + defaultKeyBindings[A_Screenshot] = OIS::KC_SYSRQ; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; @@ -689,7 +690,10 @@ namespace MWInput control = mInputCtrl->getChannel(i)->getAttachedControls ().front().control; } - if (!controlExists || force) + if (!controlExists || force || + ( mInputCtrl->getKeyBinding (control, ICS::Control::INCREASE) == OIS::KC_UNASSIGNED + && mInputCtrl->getMouseButtonBinding (control, ICS::Control::INCREASE) == ICS_MAX_DEVICE_BUTTONS + )) { clearAllBindings (control); diff --git a/files/mygui/openmw_itemselection_dialog.layout b/files/mygui/openmw_itemselection_dialog.layout index 93681e69e..81376d6d5 100644 --- a/files/mygui/openmw_itemselection_dialog.layout +++ b/files/mygui/openmw_itemselection_dialog.layout @@ -4,14 +4,14 @@ - - + + - + - + From eff2799c1bc1e8374c60699a9f07bfc51619bfda Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Mon, 27 Aug 2012 10:55:39 -0400 Subject: [PATCH 481/688] Update UTF 8 table generator to print char values This patch is in relation to commit 25fa8165f97 (Use char literals in UTF 8 conversion to fix 798 warnings), which changed the UTF 8 table to have char integer values instead of unsigned chars. Those values were converted using a custom Python script. This patch changes the original table generator so it can now output the same format. --- components/to_utf8/gen_iconv.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/components/to_utf8/gen_iconv.cpp b/components/to_utf8/gen_iconv.cpp index cc7cc191a..dea68c1fa 100644 --- a/components/to_utf8/gen_iconv.cpp +++ b/components/to_utf8/gen_iconv.cpp @@ -1,7 +1,6 @@ // This program generates the file tables_gen.hpp #include -#include using namespace std; #include @@ -10,9 +9,11 @@ using namespace std; void tab() { cout << " "; } // write one number with a space in front of it and a comma after it -void num(unsigned char i, bool last) +void num(char i, bool last) { - cout << " (char)0x" << (unsigned)i; + // Convert i to its integer value, i.e. -128 to 127. Printing it directly + // would result in non-printable characters in the source code, which is bad. + cout << " " << static_cast(i); if(!last) cout << ","; } @@ -80,8 +81,6 @@ int write_table(const std::string &charset, const std::string &tableName) int main() { - cout << hex; - // Write header guard cout << "#ifndef COMPONENTS_TOUTF8_TABLE_GEN_H\n#define COMPONENTS_TOUTF8_TABLE_GEN_H\n\n"; From 9af0b48ad6affe3aa1d95e5ff98b1b86ea6b3238 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 19:18:55 +0200 Subject: [PATCH 482/688] show/hide crosshair correctly --- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwgui/hud.cpp | 161 ++++++++++++++---------- apps/openmw/mwgui/hud.hpp | 26 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 15 ++- apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 9 +- apps/openmw/mwrender/player.cpp | 4 + 7 files changed, 134 insertions(+), 85 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index fde965256..a65c2ff4d 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -177,6 +177,8 @@ namespace MWBase virtual void unsetSelectedSpell() = 0; virtual void unsetSelectedWeapon() = 0; + virtual void showCrosshair(bool show) = 0; + virtual void disallowMouse() = 0; virtual void allowMouse() = 0; virtual void notifyInputActionBound() = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 92dc4e495..20b9765d0 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -24,9 +24,9 @@ using namespace MWGui; HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) : Layout("openmw_hud.layout") - , health(NULL) - , magicka(NULL) - , stamina(NULL) + , mHealth(NULL) + , mMagicka(NULL) + , mStamina(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) @@ -36,10 +36,10 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) , mMinimap(NULL) , mCompass(NULL) , mCrosshair(NULL) - , fpsbox(NULL) - , fpscounter(NULL) - , trianglecounter(NULL) - , batchcounter(NULL) + , mFpsBox(NULL) + , mFpsCounter(NULL) + , mTriangleCounter(NULL) + , mBatchCounter(NULL) , mHealthManaStaminaBaseLeft(0) , mWeapBoxBaseLeft(0) , mSpellBoxBaseLeft(0) @@ -57,9 +57,9 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) // Energy bars getWidget(mHealthFrame, "HealthFrame"); - getWidget(health, "Health"); - getWidget(magicka, "Magicka"); - getWidget(stamina, "Stamina"); + getWidget(mHealth, "Health"); + getWidget(mMagicka, "Magicka"); + getWidget(mStamina, "Stamina"); mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); @@ -104,8 +104,8 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) setFpsLevel(fpsLevel); - getWidget(trianglecounter, "TriangleCounter"); - getWidget(batchcounter, "BatchCounter"); + getWidget(mTriangleCounter, "TriangleCounter"); + getWidget(mBatchCounter, "BatchCounter"); setEffect("icons\\s\\tx_s_chameleon.dds"); @@ -118,7 +118,7 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) void HUD::setFpsLevel(int level) { - fpscounter = 0; + mFpsCounter = 0; MyGUI::Widget* fps; getWidget(fps, "FPSBoxAdv"); @@ -128,32 +128,32 @@ void HUD::setFpsLevel(int level) if (level == 2) { - getWidget(fpsbox, "FPSBoxAdv"); - fpsbox->setVisible(true); - getWidget(fpscounter, "FPSCounterAdv"); + getWidget(mFpsBox, "FPSBoxAdv"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounterAdv"); } else if (level == 1) { - getWidget(fpsbox, "FPSBox"); - fpsbox->setVisible(true); - getWidget(fpscounter, "FPSCounter"); + getWidget(mFpsBox, "FPSBox"); + mFpsBox->setVisible(true); + getWidget(mFpsCounter, "FPSCounter"); } } void HUD::setFPS(float fps) { - if (fpscounter) - fpscounter->setCaption(boost::lexical_cast((int)fps)); + if (mFpsCounter) + mFpsCounter->setCaption(boost::lexical_cast((int)fps)); } void HUD::setTriangleCount(unsigned int count) { - trianglecounter->setCaption(boost::lexical_cast(count)); + mTriangleCounter->setCaption(boost::lexical_cast(count)); } void HUD::setBatchCount(unsigned int count) { - batchcounter->setCaption(boost::lexical_cast(count)); + mBatchCounter->setCaption(boost::lexical_cast(count)); } void HUD::setEffect(const char *img) @@ -176,20 +176,20 @@ void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& v switch (i) { case 0: - health->setProgressRange (value.getModified()); - health->setProgressPosition (value.getCurrent()); + mHealth->setProgressRange (value.getModified()); + mHealth->setProgressPosition (value.getCurrent()); getWidget(w, "HealthFrame"); w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); break; case 1: - magicka->setProgressRange (value.getModified()); - magicka->setProgressPosition (value.getCurrent()); + mMagicka->setProgressRange (value.getModified()); + mMagicka->setProgressPosition (value.getCurrent()); getWidget(w, "MagickaFrame"); w->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); break; case 2: - stamina->setProgressRange (value.getModified()); - stamina->setProgressPosition (value.getCurrent()); + mStamina->setProgressRange (value.getModified()); + mStamina->setProgressPosition (value.getCurrent()); getWidget(w, "FatigueFrame"); w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); break; @@ -197,44 +197,6 @@ void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& v } } -void HUD::setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellVisible) -{ - int weapDx = 0, spellDx = 0; - if (!hmsVisible) - spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; - - if (!weapVisible) - spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; - - mWeaponVisible = weapVisible; - mSpellVisible = spellVisible; - if (!mWeaponVisible && !mSpellVisible) - mWeaponSpellBox->setVisible(false); - - health->setVisible(hmsVisible); - stamina->setVisible(hmsVisible); - magicka->setVisible(hmsVisible); - mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); - mWeapBox->setVisible(weapVisible); - mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); - mSpellBox->setVisible(spellVisible); -} - -void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible) -{ - const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); - - // effect box can have variable width -> variable left coordinate - int effectsDx = 0; - if (!minimapBoxVisible) - effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); - - mMapVisible = minimapBoxVisible; - mMinimapBox->setVisible(minimapBoxVisible); - mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); - mEffectBox->setVisible(effectBoxVisible); -} - void HUD::onWorldClicked(MyGUI::Widget* _sender) { if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) @@ -520,3 +482,68 @@ void HUD::unsetSelectedWeapon() mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); mWeapBox->clearUserStrings(); } + +void HUD::setCrosshairVisible(bool visible) +{ + mCrosshair->setVisible (visible); +} + +void HUD::setHmsVisible(bool visible) +{ + mHealth->setVisible(visible); + mMagicka->setVisible(visible); + mStamina->setVisible(visible); + updatePositions(); +} + +void HUD::setWeapVisible(bool visible) +{ + mWeapBox->setVisible(visible); + updatePositions(); +} + +void HUD::setSpellVisible(bool visible) +{ + mSpellBox->setVisible(visible); + updatePositions(); +} + +void HUD::setEffectVisible(bool visible) +{ + mEffectBox->setVisible (visible); + updatePositions(); +} + +void HUD::setMinimapVisible(bool visible) +{ + mMinimapBox->setVisible (visible); + updatePositions(); +} + +void HUD::updatePositions() +{ + int weapDx = 0, spellDx = 0; + if (!mHealth->getVisible()) + spellDx = weapDx = mWeapBoxBaseLeft - mHealthManaStaminaBaseLeft; + + if (!mWeapBox->getVisible()) + spellDx += mSpellBoxBaseLeft - mWeapBoxBaseLeft; + + mWeaponVisible = mWeapBox->getVisible(); + mSpellVisible = mSpellBox->getVisible(); + if (!mWeaponVisible && !mSpellVisible) + mWeaponSpellBox->setVisible(false); + + mWeapBox->setPosition(mWeapBoxBaseLeft - weapDx, mWeapBox->getTop()); + mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop()); + + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // effect box can have variable width -> variable left coordinate + int effectsDx = 0; + if (!mMinimapBox->getVisible ()) + effectsDx = (viewSize.width - mMinimapBoxBaseRight) - (viewSize.width - mEffectBoxBaseRight); + + mMapVisible = mMinimapBox->getVisible (); + mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); +} diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index baa350a10..49ed6a698 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -18,8 +18,14 @@ namespace MWGui void setFPS(float fps); void setTriangleCount(unsigned int count); void setBatchCount(unsigned int count); - void setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellVisible); - void setBottomRightVisibility(bool effectBoxVisible, bool minimapVisible); + + void setHmsVisible(bool visible); + void setWeapVisible(bool visible); + void setSpellVisible(bool visible); + + void setEffectVisible(bool visible); + void setMinimapVisible(bool visible); + void setFpsLevel(const int level); void setSelectedSpell(const std::string& spellId, int successChancePercent); @@ -28,6 +34,8 @@ namespace MWGui void unsetSelectedSpell(); void unsetSelectedWeapon(); + void setCrosshairVisible(bool visible); + void onFrame(float dt); void onResChange(int width, int height); @@ -35,7 +43,8 @@ namespace MWGui bool getWorldMouseOver() { return mWorldMouseOver; } - MyGUI::ProgressPtr health, magicka, stamina; + private: + MyGUI::ProgressPtr mHealth, mMagicka, mStamina; MyGUI::Widget* mHealthFrame; MyGUI::Widget *mWeapBox, *mSpellBox; MyGUI::ImageBox *mWeapImage, *mSpellImage; @@ -50,12 +59,11 @@ namespace MWGui MyGUI::Widget* mDummy; - MyGUI::WidgetPtr fpsbox; - MyGUI::TextBox* fpscounter; - MyGUI::TextBox* trianglecounter; - MyGUI::TextBox* batchcounter; + MyGUI::WidgetPtr mFpsBox; + MyGUI::TextBox* mFpsCounter; + MyGUI::TextBox* mTriangleCounter; + MyGUI::TextBox* mBatchCounter; - private: // bottom left elements int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft; // bottom right elements @@ -83,5 +91,7 @@ namespace MWGui void onWeaponClicked(MyGUI::Widget* _sender); void onMagicClicked(MyGUI::Widget* _sender); void onMapClicked(MyGUI::Widget* _sender); + + void updatePositions(); }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 676eb2046..c458ac6c6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -559,12 +559,12 @@ void WindowManager::setPlayerDir(const float x, const float y) void WindowManager::setHMSVisibility(bool visible) { - mHud->setBottomLeftVisibility(visible, mHud->mWeapBox->getVisible(), mHud->mSpellBox->getVisible()); + mHud->setHmsVisible (visible); } void WindowManager::setMinimapVisibility(bool visible) { - mHud->setBottomRightVisibility(mHud->mEffectBox->getVisible(), visible); + mHud->setMinimapVisible (visible); } void WindowManager::toggleFogOfWar() @@ -595,13 +595,13 @@ bool WindowManager::getFullHelp() const void WindowManager::setWeaponVisibility(bool visible) { - mHud->setBottomLeftVisibility(mHud->health->getVisible(), visible, mHud->mSpellBox->getVisible()); + mHud->setWeapVisible (visible); } void WindowManager::setSpellVisibility(bool visible) { - mHud->setBottomLeftVisibility(mHud->health->getVisible(), mHud->mWeapBox->getVisible(), visible); - mHud->setBottomRightVisibility(visible, mHud->mMinimapBox->getVisible()); + mHud->setSpellVisible (visible); + mHud->setEffectVisible (visible); } void WindowManager::setMouseVisible(bool visible) @@ -847,3 +847,8 @@ void WindowManager::notifyInputActionBound () mSettingsWindow->updateControlsBox (); allowMouse(); } + +void WindowManager::showCrosshair (bool show) +{ + mHud->setCrosshairVisible (show); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 391305594..daff81f8c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -157,6 +157,8 @@ namespace MWGui virtual void unsetSelectedSpell(); virtual void unsetSelectedWeapon(); + virtual void showCrosshair(bool show); + virtual void disallowMouse(); virtual void allowMouse(); virtual void notifyInputActionBound(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index caf4d79c9..c0e678a10 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -176,23 +176,18 @@ namespace MWInput toggleJournal (); break; case A_AutoMove: - resetIdleTime(); toggleAutoMove (); break; case A_ToggleSneak: /// \todo implement - resetIdleTime(); break; case A_ToggleWalk: - resetIdleTime(); toggleWalking (); break; case A_ToggleWeapon: - resetIdleTime(); toggleWeapon (); break; case A_ToggleSpell: - resetIdleTime(); toggleSpell (); break; } @@ -303,6 +298,8 @@ namespace MWInput // Disable mouse look mMouseLookEnabled = false; + mWindows.showCrosshair (false); + // Enable GUI events mGuiCursorEnabled = true; } @@ -311,6 +308,8 @@ namespace MWInput // Enable mouse look mMouseLookEnabled = true; + mWindows.showCrosshair (false); + // Disable GUI events mGuiCursorEnabled = false; } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 8b99e001a..b9ab0da79 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -116,6 +116,10 @@ namespace MWRender void Player::update(float duration) { + // only show the crosshair in game mode and in first person mode. + MWBase::Environment::get().getWindowManager ()->showCrosshair + (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); + if (mAnimation) { mAnimation->runAnimation(duration); } From dc8f9bd92360b0a0d2384648efee0f64e71beae9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 19:25:30 +0200 Subject: [PATCH 483/688] workaround for alt tab view mode toggle --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index caf4d79c9..5ead446f1 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -258,7 +258,9 @@ namespace MWInput mPlayer.setUpDown (0); if (mControlSwitch["playerviewswitch"]) { - if (actionIsActive(A_TogglePOV)) { + + // work around preview mode toggle when pressing Alt+Tab + if (actionIsActive(A_TogglePOV) && !mKeyboard->isModifierDown (OIS::Keyboard::Alt)) { if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += dt) > 0.5) { From 6161f81c2475550feea994b405b6adb8df0ea762 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:44:14 +0200 Subject: [PATCH 484/688] last bits of the quick key menu. --- apps/openmw/mwgui/itemselection.cpp | 6 ++ apps/openmw/mwgui/quickkeysmenu.cpp | 79 ++++++++++++++++++- apps/openmw/mwgui/spellwindow.cpp | 29 +------ apps/openmw/mwmechanics/spellsuccess.hpp | 2 + .../mygui/openmw_magicselection_dialog.layout | 8 +- 5 files changed, 91 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index 14b1cf8ee..d93a621ac 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -23,6 +23,12 @@ namespace MWGui getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked); + int dx = (cancelButton->getTextSize().width + 24) - cancelButton->getWidth(); + cancelButton->setCoord(cancelButton->getLeft() - dx, + cancelButton->getTop(), + cancelButton->getTextSize ().width + 24, + cancelButton->getHeight()); + center(); } diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 853e2ed14..fcfac8d5d 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -6,8 +6,13 @@ #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/actionequip.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/spellsuccess.hpp" +#include "../mwgui/inventorywindow.hpp" +#include "../mwgui/bookwindow.hpp" +#include "../mwgui/scrollwindow.hpp" #include "windowmanagerimp.hpp" #include "itemselection.hpp" @@ -263,20 +268,82 @@ namespace MWGui QuickKeyType type = *button->getUserData(); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + if (type == Type_Magic) { std::string spellId = button->getChildAt(0)->getUserString("Spell"); - MWBase::Environment::get().getWindowManager ()->setSelectedSpell (spellId, 100); + spells.setSelectedSpell(spellId); + store.setSelectedEnchantItem(store.end()); + mWindowManager.setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } else if (type == Type_Item) { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - MWBase::Environment::get().getWindowManager ()->setSelectedWeapon(item, 100); + + // make sure the item is available + if (item.getRefData ().getCount() == 0) + { + MWBase::Environment::get().getWindowManager ()->messageBox ( + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + return; + } + + boost::shared_ptr action = MWWorld::Class::get(item).use(item); + + action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + // this is necessary for books/scrolls: if they are already in the player's inventory, + // the "Take" button should not be visible. + // NOTE: the take button is "reset" when the window opens, so we can safely do the following + // without screwing up future book windows + mWindowManager.getBookWindow()->setTakeButtonShow(false); + mWindowManager.getScrollWindow()->setTakeButtonShow(false); + + // since we changed equipping status, update the inventory window + mWindowManager.getInventoryWindow()->drawItems(); } else if (type == Type_MagicItem) { MWWorld::Ptr item = *button->getChildAt (0)->getUserData(); - MWBase::Environment::get().getWindowManager ()->setSelectedEnchantItem (item, 100); + + // make sure the item is available + if (item.getRefData ().getCount() == 0) + { + MWBase::Environment::get().getWindowManager ()->messageBox ( + "#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item), std::vector()); + return; + } + + // retrieve ContainerStoreIterator to the item + MWWorld::ContainerStoreIterator it = store.begin(); + for (; it != store.end(); ++it) + { + if (*it == item) + { + break; + } + } + assert(it != store.end()); + + // equip, if it can be equipped + if (!MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) + { + // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping + + MWWorld::ActionEquip action(item); + action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); + + // since we changed equipping status, update the inventory window + mWindowManager.getInventoryWindow()->drawItems(); + } + + store.setSelectedEnchantItem(it); + spells.setSelectedSpell(""); + mWindowManager.setSelectedEnchantItem(item, 100); /// \todo track charge % } } @@ -339,6 +406,12 @@ namespace MWGui getWidget(mMagicList, "MagicList"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); + int dx = (mCancelButton->getTextSize().width + 24) - mCancelButton->getWidth(); + mCancelButton->setCoord(mCancelButton->getLeft() - dx, + mCancelButton->getTop(), + mCancelButton->getTextSize ().width + 24, + mCancelButton->getHeight()); + center(); } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 8754f5d10..bd671cab6 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -13,6 +13,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/actionequip.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -350,34 +351,10 @@ namespace MWGui if (_sender->getUserString("Equipped") == "false" && !MWWorld::Class::get(item).getEquipmentSlots(item).first.empty()) { - // sound - MWBase::Environment::get().getSoundManager()->playSound(MWWorld::Class::get(item).getUpSoundId(item), 1.0, 1.0); - // Note: can't use Class::use here because enchanted scrolls for example would then open the scroll window instead of equipping - /// \todo the following code is pretty much copy&paste from ActionEquip, put it in a function? - // slots that this item can be equipped in - std::pair, bool> slots = MWWorld::Class::get(item).getEquipmentSlots(item); - - // equip the item in the first free slot - for (std::vector::const_iterator slot=slots.first.begin(); - slot!=slots.first.end(); ++slot) - { - // if all slots are occupied, replace the last slot - if (slot == --slots.first.end()) - { - store.equip(*slot, it); - break; - } - - if (store.getSlot(*slot) == store.end()) - { - // slot is not occupied - store.equip(*slot, it); - break; - } - } - /// \todo scripts? + MWWorld::ActionEquip action(item); + action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); // since we changed equipping status, update the inventory window mWindowManager.getInventoryWindow()->drawItems(); diff --git a/apps/openmw/mwmechanics/spellsuccess.hpp b/apps/openmw/mwmechanics/spellsuccess.hpp index 1ab1bb11f..e4778be69 100644 --- a/apps/openmw/mwmechanics/spellsuccess.hpp +++ b/apps/openmw/mwmechanics/spellsuccess.hpp @@ -8,6 +8,8 @@ #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" +#include + #include "npcstats.hpp" namespace MWMechanics diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout index bf942b32f..31ad7b4c9 100644 --- a/files/mygui/openmw_magicselection_dialog.layout +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -2,16 +2,16 @@ - + - - + + - + From bc36b0e2bda5e2aba63bd634a055321d3201e48c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:51:13 +0200 Subject: [PATCH 485/688] remove the restriction on the last quick key --- apps/openmw/mwgui/quickkeysmenu.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index fcfac8d5d..8bc441c65 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -67,9 +67,6 @@ namespace MWGui MyGUI::Button* button; getWidget(button, "QuickKey" + boost::lexical_cast(i+1)); - if (i != 9) // 10th quick key is always set to hand-to-hand - button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); - unassign(button, i); mQuickKeyButtons.push_back(button); From 4f19b7fb9b0fb2baaae45cda5196e420e173610a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:52:32 +0200 Subject: [PATCH 486/688] fix leaks. --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 8bc441c65..3c05e6327 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -77,6 +77,8 @@ namespace MWGui QuickKeysMenu::~QuickKeysMenu() { delete mAssignDialog; + delete mItemSelectionDialog; + delete mMagicSelectionDialog; } void QuickKeysMenu::unassign(MyGUI::Widget* key, int index) From 5c28a6720307cfa2fdab09293d043e93d3099522 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 20:55:39 +0200 Subject: [PATCH 487/688] broke something in the last commit. --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 3c05e6327..5b7115dbe 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -67,6 +67,8 @@ namespace MWGui MyGUI::Button* button; getWidget(button, "QuickKey" + boost::lexical_cast(i+1)); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); + unassign(button, i); mQuickKeyButtons.push_back(button); From 321f7c3419099d824c18a6b2b82b7e69bd60716e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Aug 2012 21:29:04 +0200 Subject: [PATCH 488/688] fix an infinite recursion regression on several dialogs. --- apps/openmw/mwgui/birth.cpp | 2 +- apps/openmw/mwgui/birth.hpp | 2 +- apps/openmw/mwgui/charactercreation.cpp | 18 +++++++++--------- apps/openmw/mwgui/class.cpp | 12 ------------ apps/openmw/mwgui/class.hpp | 7 ++----- apps/openmw/mwgui/journalwindow.hpp | 2 +- apps/openmw/mwgui/race.cpp | 1 - apps/openmw/mwgui/race.hpp | 2 +- apps/openmw/mwgui/review.cpp | 1 - apps/openmw/mwgui/review.hpp | 2 +- apps/openmw/mwgui/text_input.cpp | 1 - apps/openmw/mwgui/text_input.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 1 - 13 files changed, 17 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 05a337cbc..1a5c2d0f6 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -65,9 +65,9 @@ void BirthDialog::setNextButtonShow(bool shown) void BirthDialog::open() { + WindowBase::open(); updateBirths(); updateSpells(); - setVisible(true); } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 92665081d..5bd36e828 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -28,7 +28,7 @@ namespace MWGui void setBirthId(const std::string &raceId); void setNextButtonShow(bool shown); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 8c82b3e43..7d63f6922 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -194,7 +194,7 @@ void CharacterCreation::spawnDialog(const char id) mNameDialog->setTextInput(mPlayerName); mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); - mNameDialog->open(); + mNameDialog->setVisible(true); break; case GM_Race: @@ -205,7 +205,7 @@ void CharacterCreation::spawnDialog(const char id) mRaceDialog->setRaceId(mPlayerRaceId); mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); - mRaceDialog->open(); + mRaceDialog->setVisible(true);; break; case GM_Class: @@ -213,7 +213,7 @@ void CharacterCreation::spawnDialog(const char id) mClassChoiceDialog = 0; mClassChoiceDialog = new ClassChoiceDialog(*mWM); mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); - mClassChoiceDialog->open(); + mClassChoiceDialog->setVisible(true); break; case GM_ClassPick: @@ -224,7 +224,7 @@ void CharacterCreation::spawnDialog(const char id) mPickClassDialog->setClassId(mPlayerClass.name); mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); - mPickClassDialog->open(); + mPickClassDialog->setVisible(true); break; case GM_Birth: @@ -234,7 +234,7 @@ void CharacterCreation::spawnDialog(const char id) mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); - mBirthSignDialog->open(); + mBirthSignDialog->setVisible(true); break; case GM_ClassCreate: @@ -244,7 +244,7 @@ void CharacterCreation::spawnDialog(const char id) mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); - mCreateClassDialog->open(); + mCreateClassDialog->setVisible(true); break; case GM_ClassGenerate: mGenerateClassStep = 0; @@ -289,7 +289,7 @@ void CharacterCreation::spawnDialog(const char id) mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); - mReviewDialog->open(); + mReviewDialog->setVisible(true); break; } } @@ -680,7 +680,7 @@ void CharacterCreation::showClassQuestionDialog() mGenerateClassResultDialog->setClassId(mGenerateClass); mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); - mGenerateClassResultDialog->open(); + mGenerateClassResultDialog->setVisible(true); return; } @@ -703,7 +703,7 @@ void CharacterCreation::showClassQuestionDialog() buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[2]); mGenerateClassQuestionDialog->setButtons(buttons); mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); - mGenerateClassQuestionDialog->open(); + mGenerateClassQuestionDialog->setVisible(true); MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps[mGenerateClassStep].mSound); } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 971740b5e..9ea7b8052 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -46,11 +46,6 @@ GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parW backButton->setCoord(315 - okButtonWidth - backButtonWidth - 6, 219, backButtonWidth, 23); } -void GenerateClassResultDialog::open() -{ - setVisible(true); -} - std::string GenerateClassResultDialog::getClassId() const { return mClassName->getCaption(); @@ -143,7 +138,6 @@ void PickClassDialog::open() { updateClasses(); updateStats(); - setVisible(true); } @@ -341,7 +335,6 @@ void InfoBoxDialog::open() layoutVertically(mMainWidget, 4 + 6); center(); - setVisible(true); } int InfoBoxDialog::getChosenButton() const @@ -549,11 +542,6 @@ void CreateClassDialog::setNextButtonShow(bool shown) descriptionButton->setCoord(459 - okButtonWidth - backButtonWidth - descriptionButtonWidth - 12, 158, descriptionButtonWidth, 23); } -void CreateClassDialog::open() -{ - setVisible(true); -} - // widget controls void CreateClassDialog::onDialogCancel() diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index bcb5c2627..43511ccca 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -26,7 +26,7 @@ namespace MWGui std::string getText() const; void setButtons(ButtonList &buttons); - void open(); + virtual void open(); int getChosenButton() const; // Events @@ -74,8 +74,6 @@ namespace MWGui std::string getClassId() const; void setClassId(const std::string &classId); - void open(); - // Events typedef delegates::CMultiDelegate0 EventHandle_Void; @@ -104,7 +102,7 @@ namespace MWGui void setClassId(const std::string &classId); void setNextButtonShow(bool shown); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; @@ -264,7 +262,6 @@ namespace MWGui std::vector getMinorSkills() const; void setNextButtonShow(bool shown); - void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index fc05bbdbc..a62e48803 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -14,7 +14,7 @@ namespace MWGui { public: JournalWindow(MWBase::WindowManager& parWindowManager); - void open(); + virtual void open(); virtual void setVisible(bool visible); // only used to play close sound diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index ceb0452fb..a597dcadb 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -113,7 +113,6 @@ void RaceDialog::open() updateRaces(); updateSkills(); updateSpellPowers(); - setVisible(true); } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 3da6b0ace..2e9e09a3e 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -43,7 +43,7 @@ namespace MWGui // setHair() void setNextButtonShow(bool shown); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 8dd894c25..4a9cc778a 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -110,7 +110,6 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) void ReviewDialog::open() { updateSkillArea(); - setVisible(true); } void ReviewDialog::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index f0bde6ecd..f6d373232 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -46,7 +46,7 @@ namespace MWGui void configureSkills(const SkillList& major, const SkillList& minor); void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat& value); - void open(); + virtual void open(); // Events typedef delegates::CMultiDelegate0 EventHandle_Void; diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 802cd3063..b724bab15 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -45,7 +45,6 @@ void TextInputDialog::open() { // Make sure the edit box has focus MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); - setVisible(true); } // widget controls diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index 7a3325722..8fb0e5819 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -25,7 +25,7 @@ namespace MWGui void setNextButtonShow(bool shown); void setTextLabel(const std::string &label); - void open(); + virtual void open(); protected: void onOkClicked(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 138349c2e..60a574b3c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -318,7 +318,6 @@ void WindowManager::updateVisible() break; case GM_Journal: mJournal->setVisible(true); - mJournal->open(); break; default: // Unsupported mode, switch back to game From 40d4dad15e42ad882caa68e5a97821d48854bbbe Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Aug 2012 09:32:38 +0200 Subject: [PATCH 489/688] Issue #378: Fix (record wasn't build fully) --- apps/openmw/mwworld/worldimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 655397c1f..0ad733e70 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -745,8 +745,11 @@ namespace MWWorld std::ostringstream stream; stream << "$dynamic" << mNextDynamicRecord++; + ESM::Potion record2 (record); + record2.mId = stream.str(); + const ESM::Potion *created = - &mStore.potions.list.insert (std::make_pair (stream.str(), record)).first->second; + &mStore.potions.list.insert (std::make_pair (stream.str(), record2)).first->second; mStore.all.insert (std::make_pair (stream.str(), ESM::REC_ALCH)); From 34c30b132c58ca582c064efa128b120b248f6c3b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Aug 2012 10:12:22 +0200 Subject: [PATCH 490/688] Issue #378: workaround for bad ingredient records in Morrowind.esm --- components/esm/loadingr.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index a745ff669..ddc9de0d5 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -12,6 +12,18 @@ void Ingredient::load(ESMReader &esm, const std::string& id) esm.getHNT(data, "IRDT", 56); script = esm.getHNOString("SCRI"); icon = esm.getHNOString("ITEX"); + + // horrible hack to fix broken data in records + for (int i=0; i<4; ++i) + { + if (data.effectID[i]!=85 && data.effectID[i]!=22 && data.effectID[i]!=17 && data.effectID[i]!=79 && + data.effectID[i]!=74) + data.attributes[i] = -1; + + if (data.effectID[i]!=89 && data.effectID[i]!=26 && data.effectID[i]!=21 && data.effectID[i]!=83 && + data.effectID[i]!=78) + data.skills[i] = -1; + } } } From 5834b4baa55416856a395a0d1f462b3a921de81d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 17:30:34 +0200 Subject: [PATCH 491/688] door markers on the local map --- apps/openmw/mwbase/world.hpp | 15 ++++ apps/openmw/mwgui/hud.cpp | 4 +- apps/openmw/mwgui/hud.hpp | 1 + apps/openmw/mwgui/map_window.cpp | 86 ++++++++++++++++++++++- apps/openmw/mwgui/map_window.hpp | 9 +++ apps/openmw/mwgui/tooltips.cpp | 18 ++++- apps/openmw/mwrender/localmap.cpp | 45 ++++++++---- apps/openmw/mwrender/localmap.hpp | 12 ++++ apps/openmw/mwrender/renderingmanager.cpp | 10 +++ apps/openmw/mwrender/renderingmanager.hpp | 6 ++ apps/openmw/mwworld/worldimp.cpp | 58 +++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 9 +++ files/mygui/openmw_hud.layout | 9 ++- 13 files changed, 260 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9c801cbff..19405fb7a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -65,6 +65,12 @@ namespace MWBase Render_Compositors }; + struct DoorMarker + { + std::string name; + float x, y; // world position + }; + World() {} virtual ~World() {} @@ -108,6 +114,15 @@ namespace MWBase virtual Ogre::Vector2 getNorthVector (MWWorld::CellStore* cell) = 0; ///< get north vector (OGRE coordinates) for given interior cell + virtual std::vector getDoorMarkers (MWWorld::CellStore* cell) = 0; + ///< get a list of teleport door markers for a given cell, to be displayed on the local map + + virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) = 0; + ///< see MWRender::LocalMap::getInteriorMapPosition + + virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0; + ///< see MWRender::LocalMap::isPositionExplored + virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0; virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 20b9765d0..b14833553 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -60,7 +60,6 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) getWidget(mHealth, "Health"); getWidget(mMagicka, "Magicka"); getWidget(mStamina, "Stamina"); - mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; @@ -93,9 +92,10 @@ HUD::HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop) getWidget(mMinimapBox, "MiniMapBox"); mMinimapBoxBaseRight = viewSize.width - mMinimapBox->getRight(); - mMinimapBox->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); getWidget(mMinimap, "MiniMap"); getWidget(mCompass, "Compass"); + getWidget(mMinimapButton, "MiniMapButton"); + mMinimapButton->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked); getWidget(mCellNameBox, "CellName"); getWidget(mWeaponSpellBox, "WeaponSpellName"); diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 49ed6a698..de4228e87 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -50,6 +50,7 @@ namespace MWGui MyGUI::ImageBox *mWeapImage, *mSpellImage; MyGUI::ProgressPtr mWeapStatus, mSpellStatus; MyGUI::Widget *mEffectBox, *mMinimapBox; + MyGUI::Button* mMinimapButton; MyGUI::ImageBox* mEffect1; MyGUI::ScrollView* mMinimap; MyGUI::ImageBox* mCompass; diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 1ffedaac4..fb9d78147 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -1,9 +1,13 @@ #include "map_window.hpp" -#include "../mwbase/windowmanager.hpp" - #include +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + using namespace MWGui; LocalMapBase::LocalMapBase() @@ -92,10 +96,21 @@ void LocalMapBase::applyFogOfWar() void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell + + // clear all previous markers + for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i) + { + if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker") + { + MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i)); + } + } + for (int mx=0; mx<3; ++mx) { for (int my=0; my<3; ++my) { + // map std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" + boost::lexical_cast(y + (interior ? (my-1) : -1*(my-1))); @@ -108,12 +123,79 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) box->setImageTexture(image); else box->setImageTexture("black.png"); + + + // door markers + + // interior map only consists of one cell, so handle the markers only once + if (interior && (mx != 2 || my != 2)) + continue; + + MWWorld::CellStore* cell; + if (interior) + cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix); + else + cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1)); + + std::vector doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell); + + for (std::vector::iterator it = doors.begin(); it != doors.end(); ++it) + { + MWBase::World::DoorMarker marker = *it; + + // convert world coordinates to normalized cell coordinates + MyGUI::IntCoord widgetCoord; + float nX,nY; + int cellDx, cellDy; + if (!interior) + { + const int cellSize = 8192; + + nX = (marker.x - cellSize * (x+mx-1)) / cellSize; + nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; + + widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + mx * 512, nY * 512 - 3 + my * 512, 7, 7); + } + else + { + Ogre::Vector2 position (marker.x, -marker.y); + MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); + + widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + (1+cellDx-x) * 512, nY * 512 - 3 + (1+cellDy-y) * 512, 7, 7); + } + + std::cout << "widgetCoord " << widgetCoord.left << " " << widgetCoord.top << " nX " << nX << " nY " << nY << " xy " << x << " " << y << std::endl; + + static int counter = 0; + ++counter; + MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); + markerWidget->setImageTexture ("textures\\door_icon.dds"); + markerWidget->setImageCoord (MyGUI::IntCoord(0,0,7,7)); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTip"); + markerWidget->setUserString("Caption_Text", marker.name); + markerWidget->setUserString("IsMarker", "true"); + + MarkerPosition markerPos; + markerPos.interior = interior; + markerPos.cellX = interior ? cellDx : x + mx - 1; + markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1); + markerPos.nX = nX; + markerPos.nY = nY; + + markerWidget->setUserData(markerPos); + } + + } } mInterior = interior; mCurX = x; mCurY = y; mChanged = false; + + // fog of war applyFogOfWar(); // set the compass texture again, because MyGUI determines sorting of ImageBox widgets diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 447c16901..c69d8986c 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -18,6 +18,15 @@ namespace MWGui void toggleFogOfWar(); + struct MarkerPosition + { + bool interior; + int cellX; + int cellY; + float nX; + float nY; + }; + protected: int mCurX, mCurY; bool mInterior; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index edc787cef..16ee27923 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -12,6 +12,7 @@ #include "../mwworld/class.hpp" +#include "map_window.hpp" #include "widgets.hpp" using namespace MWGui; @@ -150,7 +151,19 @@ void ToolTips::onFrame(float frameDuration) { return; } - else if (type == "ItemPtr") + + // special handling for markers on the local map: the tooltip should only be visible + // if the marker is not hidden due to the fog of war. + if (focus->getUserString ("IsMarker") == "true") + { + LocalMapBase::MarkerPosition pos = *focus->getUserData(); + + if (!MWBase::Environment::get().getWorld ()->isPositionExplored (pos.nX, pos.nY, pos.cellX, pos.cellY, pos.interior)) + return; + } + + + if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); tooltipSize = getToolTipViaPtr(false); @@ -199,7 +212,8 @@ void ToolTips::onFrame(float frameDuration) it != userStrings.end(); ++it) { if (it->first == "ToolTipType" - || it->first == "ToolTipLayout") + || it->first == "ToolTipLayout" + || it->first == "IsMarker") continue; diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0e85d32e0..e254a2f0a 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -270,6 +270,34 @@ void LocalMap::render(const float x, const float y, mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, fStart, fEnd); } +void LocalMap::getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y) +{ + pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle); + + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + + x = std::ceil((pos.x - min.x)/sSize)-1; + y = std::ceil((pos.y - min.y)/sSize)-1; + + nX = (pos.x - min.x - sSize*x)/sSize; + nY = (pos.y - min.y - sSize*y)/sSize; +} + +bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interior) +{ + std::string texName = (interior ? mInteriorName + "_" : "Cell_") + coordStr(x, y); + + if (mBuffers.find(texName) == mBuffers.end()) + return false; + + int texU = (sFogOfWarResolution-1) * nX; + int texV = (sFogOfWarResolution-1) * nY; + + Ogre::uint32 clr = mBuffers[texName][texV * sFogOfWarResolution + texU]; + uint8 alpha = (clr >> 24); + return alpha < 200; +} + void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation) { if (sFogOfWarSkip != 0) @@ -281,14 +309,12 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni // retrieve the x,y grid coordinates the player is in int x,y; - Vector3 _pos(position.x, 0, position.z); - Vector2 pos(_pos.x, _pos.z); + float u,v; + + Vector2 pos(position.x, position.z); if (mInterior) - { - pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle); - } - + getInteriorMapPosition(pos, u,v, x,y); Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); @@ -303,14 +329,10 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } else { - x = std::ceil((pos.x - min.x)/sSize)-1; - y = std::ceil((pos.y - min.y)/sSize)-1; - MWBase::Environment::get().getWindowManager()->setInteriorMapTexture(x,y); } // convert from world coordinates to texture UV coordinates - float u,v; std::string texBaseName; if (!mInterior) { @@ -320,9 +342,6 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } else { - u = (pos.x - min.x - sSize*x)/sSize; - v = (pos.y - min.y - sSize*y)/sSize; - texBaseName = mInteriorName + "_"; } diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index c5cd908fc..056d2498a 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -58,6 +58,18 @@ namespace MWRender */ void saveFogOfWar(MWWorld::CellStore* cell); + + /** + * Get the interior map texture index and normalized position + * on this texture, given a world position (in ogre coordinates) + */ + void getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y); + + /** + * Check if a given position is explored by the player (i.e. not obscured by fog of war) + */ + bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + private: OEngine::Render::OgreRenderer* mRendering; MWRender::RenderingManager* mRenderingManager; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2d29e8f55..edeb0fe12 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -863,4 +863,14 @@ void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float mPlayer->getSightAngles(pitch, yaw); } +void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) +{ + return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y); +} + +bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, bool interior) +{ + return mLocalMap->isPositionExplored(nX, nY, x, y, interior); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b6bfcbf97..de2449150 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -184,6 +184,12 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList static bool waterShaderSupported(); + virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + ///< see MWRender::LocalMap::getInteriorMapPosition + + virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + ///< see MWRender::LocalMap::isPositionExplored + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0ad733e70..8e9d981c1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1031,6 +1031,64 @@ namespace MWWorld return d; } + std::vector World::getDoorMarkers (CellStore* cell) + { + std::vector result; + + MWWorld::CellRefList doors = cell->doors; + std::list< MWWorld::LiveCellRef > refList = doors.list; + for (std::list< MWWorld::LiveCellRef >::iterator it = refList.begin(); it != refList.end(); ++it) + { + MWWorld::LiveCellRef ref = *it; + + if (ref.ref.teleport) + { + World::DoorMarker newMarker; + + std::string dest; + if (ref.ref.destCell != "") + { + // door leads to an interior, use interior name + dest = ref.ref.destCell; + } + else + { + // door leads to exterior, use cell name (if any), otherwise translated region name + int x,y; + positionToIndex (ref.ref.doorDest.pos[0], ref.ref.doorDest.pos[1], x, y); + const ESM::Cell* cell = mStore.cells.findExt(x,y); + if (cell->name != "") + dest = cell->name; + else + { + const ESM::Region* region = mStore.regions.search(cell->region); + dest = region->name; + } + } + + newMarker.name = dest; + + ESM::Position pos = ref.mData.getPosition (); + + newMarker.x = pos.pos[0]; + newMarker.y = pos.pos[1]; + result.push_back(newMarker); + } + } + + return result; + } + + void World::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) + { + mRendering->getInteriorMapPosition(position, nX, nY, x, y); + } + + bool World::isPositionExplored (float nX, float nY, int x, int y, bool interior) + { + return mRendering->isPositionExplored(nX, nY, x, y, interior); + } + void World::setWaterHeight(const float height) { mRendering->setWaterHeight(height); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 51216e3a5..1d932bce2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -140,6 +140,15 @@ namespace MWWorld virtual Ogre::Vector2 getNorthVector (CellStore* cell); ///< get north vector (OGRE coordinates) for given interior cell + virtual std::vector getDoorMarkers (MWWorld::CellStore* cell); + ///< get a list of teleport door markers for a given cell, to be displayed on the local map + + virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + ///< see MWRender::LocalMap::getInteriorMapPosition + + virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + ///< see MWRender::LocalMap::isPositionExplored + virtual Globals::Data& getGlobalVariable (const std::string& name); virtual Globals::Data getGlobalVariable (const std::string& name) const; diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index cf353a205..e4c6f0765 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -76,18 +76,21 @@ - + - - + + + + + From 571d5095d45bcd929050195115d38cbc772b85b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 17:32:50 +0200 Subject: [PATCH 492/688] removed a cout --- apps/openmw/mwgui/map_window.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index fb9d78147..44baeddcb 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -164,8 +164,6 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + (1+cellDx-x) * 512, nY * 512 - 3 + (1+cellDy-y) * 512, 7, 7); } - std::cout << "widgetCoord " << widgetCoord.left << " " << widgetCoord.top << " nX " << nX << " nY " << nY << " xy " << x << " " << y << std::endl; - static int counter = 0; ++counter; MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", From 3c39c47e2fb5a003bb3bc940ecd8e0d830b11cf6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:23:01 +0200 Subject: [PATCH 493/688] don't make copies of CellRefList & LiveCellRef --- apps/openmw/mwworld/worldimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8e9d981c1..a099f71cc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1035,11 +1035,11 @@ namespace MWWorld { std::vector result; - MWWorld::CellRefList doors = cell->doors; - std::list< MWWorld::LiveCellRef > refList = doors.list; + MWWorld::CellRefList& doors = cell->doors; + std::list< MWWorld::LiveCellRef >& refList = doors.list; for (std::list< MWWorld::LiveCellRef >::iterator it = refList.begin(); it != refList.end(); ++it) { - MWWorld::LiveCellRef ref = *it; + MWWorld::LiveCellRef& ref = *it; if (ref.ref.teleport) { From d9276ca09dd1efd213fb3180cac60a2ab452c533 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:23:59 +0200 Subject: [PATCH 494/688] don't make copy of CellRefList in World::getNorthVector --- 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 a099f71cc..968aa8cee 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1021,7 +1021,7 @@ namespace MWWorld Ogre::Vector2 World::getNorthVector (CellStore* cell) { - MWWorld::CellRefList statics = cell->statics; + MWWorld::CellRefList& statics = cell->statics; MWWorld::LiveCellRef* ref = statics.find("northmarker"); if (!ref) return Vector2(0, 1); From 7eaf5e7f0f3ebb4f4fed3f383eec2781b55738ff Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:40:11 +0200 Subject: [PATCH 495/688] changed the markers to look more like morrowind's (hover effect) --- apps/openmw/mwgui/map_window.cpp | 9 ++++----- files/mygui/CMakeLists.txt | 1 + files/mygui/markers.png | Bin 0 -> 245 bytes files/mygui/openmw_resources.xml | 18 ++++++++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 files/mygui/markers.png diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 44baeddcb..c5c3dff92 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -154,22 +154,21 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) nX = (marker.x - cellSize * (x+mx-1)) / cellSize; nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize; - widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + mx * 512, nY * 512 - 3 + my * 512, 7, 7); + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8); } else { Ogre::Vector2 position (marker.x, -marker.y); MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); - widgetCoord = MyGUI::IntCoord(nX * 512 - 3 + (1+cellDx-x) * 512, nY * 512 - 3 + (1+cellDy-y) * 512, 7, 7); + widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); } static int counter = 0; ++counter; - MyGUI::ImageBox* markerWidget = mLocalMap->createWidget("ImageBox", + MyGUI::Button* markerWidget = mLocalMap->createWidget("ButtonImage", widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(counter)); - markerWidget->setImageTexture ("textures\\door_icon.dds"); - markerWidget->setImageCoord (MyGUI::IntCoord(0,0,7,7)); + markerWidget->setImageResource("DoorMarker"); markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTip"); markerWidget->setUserString("Caption_Text", marker.name); diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 2e448a369..70f7282e5 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -71,6 +71,7 @@ set(MYGUI_FILES openmw_magicselection_dialog.layout smallbars.png VeraMono.ttf + markers.png ) diff --git a/files/mygui/markers.png b/files/mygui/markers.png new file mode 100644 index 0000000000000000000000000000000000000000..76b6b9913b01cd9f805d1a9d7ff7693cf49c99fe GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~c!3HEhl+{lMQfx`y?k)`fL2$v|<&%LToCO|{ z#S9GG!XV7ZFl&wkP>{XE)7O>#4u_0@5~t(+j%PrjCQlc~5RLQ6JiQ!qPd2bbNJdQD z$+KUw&iKLP^u&aOgov#*p;F9Z0BR06 zaSc8`saxaIQNyTA9w6{J(G|8nV(z}2qnk?DWEf`K2s5`fu+9{g60{PwoxpMOFz*4; i2?h + + + + + + + + + + + + + + + + + + From 1c5055c8ac41c5a7ea3c08b0248842a754f80c41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Aug 2012 18:49:44 +0200 Subject: [PATCH 496/688] fix a problem where hovering a marker would render it on top of the fog of war instead of below --- apps/openmw/mwgui/map_window.cpp | 12 ++++++++++++ apps/openmw/mwgui/map_window.hpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index c5c3dff92..0d6300baa 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -93,6 +93,16 @@ void LocalMapBase::applyFogOfWar() } } +void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) +{ + applyFogOfWar (); +} + +void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2) +{ + applyFogOfWar (); +} + void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell @@ -173,6 +183,8 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) markerWidget->setUserString("ToolTipLayout", "TextToolTip"); markerWidget->setUserString("Caption_Text", marker.name); markerWidget->setUserString("IsMarker", "true"); + markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused); + markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused); MarkerPosition markerPos; markerPos.interior = interior; diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index c69d8986c..1203233bf 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -41,6 +41,9 @@ namespace MWGui void applyFogOfWar(); + void onMarkerFocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + OEngine::GUI::Layout* mLayout; bool mMapDragAndDrop; From e9b2f4ee74bdd346cfd4434edb6352aae30db16b Mon Sep 17 00:00:00 2001 From: Artem Kotsynyak Date: Tue, 28 Aug 2012 22:59:44 +0400 Subject: [PATCH 497/688] fix equipment visibility update --- apps/openmw/mwrender/player.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index b9ab0da79..3ccc08022 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -123,6 +123,11 @@ namespace MWRender if (mAnimation) { mAnimation->runAnimation(duration); } + mPlayerNode->setVisible( + mVanity.enabled || mPreviewMode || !mFirstPersonView, + false + ); + if (mFirstPersonView && !mVanity.enabled) { return; } @@ -143,7 +148,6 @@ namespace MWRender mCamera->setPosition(0.f, 0.f, mCameraDistance); setLowHeight(true); } - mPlayerNode->setVisible(!mFirstPersonView, false); } void Player::allowVanityMode(bool allow) @@ -172,13 +176,11 @@ namespace MWRender rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - mPlayerNode->setVisible(true, false); setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; - mPlayerNode->setVisible(!mFirstPersonView, false); setLowHeight(!mFirstPersonView); } rot.z = getYaw(); @@ -199,13 +201,11 @@ namespace MWRender mMainCam.offset = offset; offset = mPreviewCam.offset; - mPlayerNode->setVisible(true, false); setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; - mPlayerNode->setVisible(!mFirstPersonView, false); setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); @@ -302,7 +302,6 @@ namespace MWRender void Player::setAnimation(NpcAnimation *anim) { mAnimation = anim; - mPlayerNode->setVisible(!mFirstPersonView, false); } void Player::setHeight(float height) From d7572d815e64737404c3c38da1fa1f3b05127a01 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Tue, 28 Aug 2012 15:32:12 -0400 Subject: [PATCH 498/688] Do not define OIS_DYNAMIC_LIB in ICSPrerequisites.h Fixes http://bugs.openmw.org/issues/381 --- extern/oics/ICSPrerequisites.h | 1 - 1 file changed, 1 deletion(-) diff --git a/extern/oics/ICSPrerequisites.h b/extern/oics/ICSPrerequisites.h index 864dad15f..3b5d1935b 100644 --- a/extern/oics/ICSPrerequisites.h +++ b/extern/oics/ICSPrerequisites.h @@ -39,7 +39,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "tinyxml.h" -#define OIS_DYNAMIC_LIB #include #include #include From 5370fecb6f25e942ec3fbaa3ce9e658c4f60561a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Aug 2012 10:51:36 +0200 Subject: [PATCH 499/688] fix a small input focus issue with the map --- files/mygui/openmw_map_window_skin.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/mygui/openmw_map_window_skin.xml b/files/mygui/openmw_map_window_skin.xml index fbb7af7ae..0c6050969 100644 --- a/files/mygui/openmw_map_window_skin.xml +++ b/files/mygui/openmw_map_window_skin.xml @@ -5,7 +5,7 @@ - - + + From 6ad08dfd338eb7fba55e76a22ce51c0d54f20be8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Aug 2012 11:15:17 +0200 Subject: [PATCH 500/688] added settings for crosshair & subtitles --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/settingswindow.cpp | 11 +++++++++++ apps/openmw/mwgui/settingswindow.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 13 ++++++++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++++ apps/openmw/mwscript/soundextensions.cpp | 5 ++++- files/mygui/openmw_settings_window.layout | 12 ++++++++++++ files/settings-default.cfg | 6 +++++- 8 files changed, 51 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 0e1ad9c6e..388dc4af9 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -180,6 +180,7 @@ namespace MWBase virtual void unsetSelectedWeapon() = 0; virtual void showCrosshair(bool show) = 0; + virtual bool getSubtitlesEnabled() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 4039136a1..2599ea0b2 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -85,6 +85,8 @@ namespace MWGui WindowBase("openmw_settings_window.layout", parWindowManager) { getWidget(mOkButton, "OkButton"); + getWidget(mSubtitlesButton, "SubtitlesButton"); + getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mResolutionList, "ResolutionList"); getWidget(mMenuTransparencySlider, "MenuTransparencySlider"); getWidget(mToolTipDelaySlider, "ToolTipDelaySlider"); @@ -121,6 +123,8 @@ namespace MWGui getWidget(mUISensitivitySlider, "UISensitivitySlider"); getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); + mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mUnderwaterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -192,6 +196,9 @@ namespace MWGui int tooltip_delay = (mToolTipDelaySlider->getScrollRange()-1) * Settings::Manager::getFloat("tooltip delay", "GUI"); mToolTipDelaySlider->setScrollPosition(tooltip_delay); + mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); + mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}"); + float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin); mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); MyGUI::TextBox* fovText; @@ -403,6 +410,10 @@ namespace MWGui Settings::Manager::setBool("debug", "Shadows", newState); else if (_sender == mInvertYButton) Settings::Manager::setBool("invert y axis", "Input", newState); + else if (_sender == mCrosshairButton) + Settings::Manager::setBool("crosshair", "HUD", newState); + else if (_sender == mSubtitlesButton) + Settings::Manager::setBool("subtitles", "GUI", newState); apply(); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 159d52bdc..d1f35ed71 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -30,6 +30,8 @@ namespace MWGui MyGUI::ScrollBar* mMenuTransparencySlider; MyGUI::ScrollBar* mToolTipDelaySlider; + MyGUI::Button* mSubtitlesButton; + MyGUI::Button* mCrosshairButton; // graphics MyGUI::ListBox* mResolutionList; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 091d31bfe..a3d20ac9e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -84,6 +84,8 @@ WindowManager::WindowManager( , mFPS(0.0f) , mTriangleCount(0) , mBatchCount(0) + , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) + , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) { // Set up the GUI system @@ -644,6 +646,10 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector { changeRes = true; } + else if (it->first == "HUD" && it->second == "crosshair") + mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); + else if (it->first == "GUI" && it->second == "subtitles") + mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); } if (changeRes) @@ -857,10 +863,15 @@ void WindowManager::notifyInputActionBound () void WindowManager::showCrosshair (bool show) { - mHud->setCrosshairVisible (show); + mHud->setCrosshairVisible (show && mCrosshairEnabled); } void WindowManager::activateQuickKey (int index) { mQuickKeysMenu->activateQuickKey(index); } + +bool WindowManager::getSubtitlesEnabled () +{ + return mSubtitlesEnabled; +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 91e9d44b5..dd8a3b006 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -161,6 +161,7 @@ namespace MWGui virtual void unsetSelectedWeapon(); virtual void showCrosshair(bool show); + virtual bool getSubtitlesEnabled(); virtual void disallowMouse(); virtual void allowMouse(); @@ -220,6 +221,9 @@ namespace MWGui MyGUI::Widget* mInputBlocker; + bool mCrosshairEnabled; + bool mSubtitlesEnabled; + /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager ESM::Class mPlayerClass; diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 6dc8f3919..35e66241e 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -10,6 +10,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -37,7 +38,9 @@ namespace MWScript runtime.pop(); MWBase::Environment::get().getSoundManager()->say (ptr, file); - context.messageBox (text); + + if (MWBase::Environment::get().getWindowManager ()->getSubtitlesEnabled()) + context.messageBox (text); } }; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ad906d2a1..c8eae518f 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -41,6 +41,18 @@ + + + + + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index effd9f3da..8fab98da2 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -29,7 +29,9 @@ vsync = false menu transparency = 0.84 # 0 - instantly, 1 - max. delay -tooltip delay = 0.2 +tooltip delay = 0 + +subtitles = false [General] # Camera field of view @@ -85,6 +87,8 @@ debug = false # 2: advanced FPS display (batches, triangles) fps = 0 +crosshair = true + [Objects] shaders = true From 02d7aa41355a9cfc68061d583f9aa592fcd935f9 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 29 Aug 2012 14:01:10 +0400 Subject: [PATCH 501/688] npc ai packages --- components/esm/defs_ai.hpp | 128 ++++++++++++++++++++++++++++++++++ components/esm/esm_reader.hpp | 12 ++-- components/esm/loadcrea.hpp | 10 +-- components/esm/loadnpc.cpp | 10 +++ components/esm/loadnpc.hpp | 20 +++--- 5 files changed, 155 insertions(+), 25 deletions(-) create mode 100644 components/esm/defs_ai.hpp diff --git a/components/esm/defs_ai.hpp b/components/esm/defs_ai.hpp new file mode 100644 index 000000000..a7f2a26ea --- /dev/null +++ b/components/esm/defs_ai.hpp @@ -0,0 +1,128 @@ +#ifndef _ESM_DEFS_AI_H +#define _ESM_DEFS_AI_H + +#include "esm_reader.hpp" + +#include + +namespace ESM +{ + #pragma pack(push) + #pragma pack(1) + + struct AIDTstruct + { + // These are probabilities + char hello, u1, fight, flee, alarm, u2, u3, u4; + // The last u's might be the skills that this NPC can train you + // in? + int services; // See the Services enum + }; // 12 bytes + + struct AIWander + { + short distance; + short duration; + char timeOfDay; + char idle[8]; + char unk; + }; + + struct AITravel + { + float x, y, z; + long unk; + }; + + struct AITarget + { + float x, y, z; + short duration; + NAME32 id; + short unk; + }; + + struct AIActivate + { + NAME32 name; + char unk; + }; + + #pragma pack(pop) + + enum + { + AI_Wander = 0x575f4941, + AI_Travel = 0x545f4941, + AI_Follow = 0x465f4941, + AI_Escort = 0x455f4941, + AI_Activate = 0x415f4941, + }; + + /// \note Used for storaging packages in a single container + /// w/o manual memory allocation accordingly to policy standards + struct AIPackage + { + int type; + + // Anonymous union + union + { + AIWander wander; + AITravel travel; + AITarget target; + AIActivate activate; + }; + + /// \note for AITarget only, placed here to stick with union, + /// overhead should be not so awful + std::string cellName; + }; + + struct AIPackageList + { + std::vector list; + + /// \note This breaks consistency of subrecords reading: + /// after calling it subrecord name is already read, so + /// it needs to use retSubName() if needed. But, hey, there + /// is only one field left (XSCL) and only two records uses AI + void load(ESMReader &esm) { + while (esm.hasMoreSubs()) { + // initialize every iteration + AIPackage pack; + + esm.getSubName(); + std::cout << esm.retSubName().toString() << std::endl; + if (esm.retSubName() == 0x54444e43) { // CNDT + list.back().cellName = esm.getHString(); + } else if (esm.retSubName() == AI_Wander) { + pack.type = AI_Wander; + esm.getHExact(&pack.wander, 14); + list.push_back(pack); + } else if (esm.retSubName() == AI_Travel) { + pack.type = AI_Travel; + esm.getHExact(&pack.travel, 16); + list.push_back(pack); + } else if (esm.retSubName() == AI_Escort || + esm.retSubName() == AI_Follow) + { + pack.type = + (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow; + + esm.getHExact(&pack.target, 48); + list.push_back(pack); + } else if (esm.retSubName() == AI_Activate) { + pack.type = AI_Activate; + esm.getHExact(&pack.activate, 33); + list.push_back(pack); + } else { // not AI package related data, so leave + return; + } + } + } + }; +} + +#endif + diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 13f1f4a01..66c0710a6 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -49,23 +49,23 @@ union NAME_T char name[LEN]; int32_t val; - bool operator==(const char *str) + bool operator==(const char *str) const { for(int i=0; i dest; + AIPackageList aiPack; + std::string name, model, race, cls, faction, script, hair, head; // body parts From 75fa0288a34b8ae5e08b44d8859c5b5404d0c9aa Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 29 Aug 2012 14:34:27 +0400 Subject: [PATCH 502/688] fix creatures has spells, creatures ai data --- components/esm/defs_ai.hpp | 1 - components/esm/loadcrea.cpp | 15 ++------------- components/esm/loadcrea.hpp | 3 +++ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/components/esm/defs_ai.hpp b/components/esm/defs_ai.hpp index a7f2a26ea..b14cc7d65 100644 --- a/components/esm/defs_ai.hpp +++ b/components/esm/defs_ai.hpp @@ -93,7 +93,6 @@ namespace ESM AIPackage pack; esm.getSubName(); - std::cout << esm.retSubName().toString() << std::endl; if (esm.retSubName() == 0x54444e43) { // CNDT list.back().cellName = esm.getHString(); } else if (esm.retSubName() == AI_Wander) { diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index 8af3526a0..fcef9ccd4 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -18,6 +18,7 @@ void Creature::load(ESMReader &esm, const std::string& id) esm.getHNOT(scale, "XSCL"); inventory.load(esm); + spells.load(esm); if (esm.isNextSub("AIDT")) { @@ -27,20 +28,8 @@ void Creature::load(ESMReader &esm, const std::string& id) else hasAI = false; - // More subrecords: - // AI_W - wander (14 bytes, i don't understand it) - // short distance - // byte duration - // byte timeOfDay - // byte idle[10] - // - // Rest is optional: - // AI_T - travel? - // AI_F - follow? - // AI_E - escort? - // AI_A - activate? + aiPack.load(esm); esm.skipRecord(); - } } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 364b10eeb..9db1f3fff 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -3,6 +3,7 @@ #include "esm_reader.hpp" #include "loadcont.hpp" +#include "defs.hpp" #include "defs_ai.hpp" namespace ESM @@ -61,9 +62,11 @@ struct Creature // Defined in loadcont.hpp InventoryList inventory; + SpellList spells; bool hasAI; AIDTstruct AI; + AIPackageList aiPack; std::string mId; From dbe3cca828587eff84585ef4e44acb163f55b7b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Aug 2012 13:11:06 +0200 Subject: [PATCH 503/688] added a separate core.xml file that works with the mygui layout editor --- files/mygui/core_layouteditor.xml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 files/mygui/core_layouteditor.xml diff --git a/files/mygui/core_layouteditor.xml b/files/mygui/core_layouteditor.xml new file mode 100644 index 000000000..740f129cc --- /dev/null +++ b/files/mygui/core_layouteditor.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + From 81463fa23db4643a35bfb8811b1bb06426923044 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 29 Aug 2012 18:48:20 +0200 Subject: [PATCH 504/688] Issue #372: fixed positions in SoundManager --- apps/openmw/mwsound/soundmanagerimp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index e1720453b..4b10624a5 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -215,7 +215,7 @@ namespace MWSound // The range values are not tested float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - const ESM::Position &pos = ptr.getCellRef().pos; + const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, @@ -313,7 +313,7 @@ namespace MWSound float basevol = mMasterVolume * mSFXVolume; float min, max; std::string file = lookup(soundId, basevol, min, max); - const ESM::Position &pos = ptr.getCellRef().pos; + const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode); @@ -406,7 +406,7 @@ namespace MWSound void SoundManager::updateObject(MWWorld::Ptr ptr) { - const ESM::Position &pos = ptr.getCellRef().pos; + const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) From a90547bbbef6ff431ac5e5047f25aefc018531b9 Mon Sep 17 00:00:00 2001 From: greye Date: Wed, 29 Aug 2012 21:35:06 +0400 Subject: [PATCH 505/688] code formatting --- apps/openmw/mwclass/creature.cpp | 8 +- apps/openmw/mwclass/npc.cpp | 8 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 8 +- apps/openmw/mwgui/tradewindow.cpp | 8 +- components/CMakeLists.txt | 2 +- components/esm/aipackage.cpp | 37 +++++ components/esm/aipackage.hpp | 95 +++++++++++++ components/esm/defs_ai.hpp | 127 ------------------ components/esm/loadcrea.cpp | 10 +- components/esm/loadcrea.hpp | 10 +- components/esm/loadnpc.cpp | 14 +- components/esm/loadnpc.hpp | 34 ++--- 12 files changed, 184 insertions(+), 177 deletions(-) create mode 100644 components/esm/aipackage.cpp create mode 100644 components/esm/aipackage.hpp delete mode 100644 components/esm/defs_ai.hpp diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a22c2d661..7c6021652 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -61,10 +61,10 @@ namespace MWClass data->mCreatureStats.setLevel(ref->base->data.level); - data->mCreatureStats.setHello(ref->base->AI.hello); - data->mCreatureStats.setFight(ref->base->AI.fight); - data->mCreatureStats.setFlee(ref->base->AI.flee); - data->mCreatureStats.setAlarm(ref->base->AI.alarm); + data->mCreatureStats.setHello(ref->base->mAiData.mHello); + data->mCreatureStats.setFight(ref->base->mAiData.mFight); + data->mCreatureStats.setFlee(ref->base->mAiData.mFlee); + data->mCreatureStats.setAlarm(ref->base->mAiData.mAlarm); // store ptr.getRefData().setCustomData (data.release()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 94c98ba42..909c681a5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -100,10 +100,10 @@ namespace MWClass /// \todo do something with npdt12 maybe:p } - data->mCreatureStats.setHello(ref->base->AI.hello); - data->mCreatureStats.setFight(ref->base->AI.fight); - data->mCreatureStats.setFlee(ref->base->AI.flee); - data->mCreatureStats.setAlarm(ref->base->AI.alarm); + data->mCreatureStats.setHello(ref->base->mAiData.mHello); + data->mCreatureStats.setFight(ref->base->mAiData.mFight); + data->mCreatureStats.setFlee(ref->base->mAiData.mFlee); + data->mCreatureStats.setAlarm(ref->base->mAiData.mAlarm); // store ptr.getRefData().setCustomData (data.release()); diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index acd786892..841e36bf4 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -769,14 +769,14 @@ namespace MWDialogue if (mActor.getTypeName() == typeid(ESM::NPC).name()) { MWWorld::LiveCellRef* ref = mActor.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } else if (mActor.getTypeName() == typeid(ESM::Creature).name()) { MWWorld::LiveCellRef* ref = mActor.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } if (services & ESM::NPC::Weapon diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 8471367c6..9abd97bb7 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -291,14 +291,14 @@ namespace MWGui if (mPtr.getTypeName() == typeid(ESM::NPC).name()) { MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } else if (mPtr.getTypeName() == typeid(ESM::Creature).name()) { MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->base->hasAI) - services = ref->base->AI.services; + if (ref->base->mHasAI) + services = ref->base->mAiData.mServices; } /// \todo what about potions, there doesn't seem to be a flag for them?? diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index c0585d5ee..1bc5e65f8 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -43,7 +43,7 @@ add_component_dir (esm loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst loadinfo loadingr loadland loadlevlist loadligh loadlocks loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat - loadweap records + loadweap records aipackage ) add_component_dir (misc diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp new file mode 100644 index 000000000..a2fd8beb1 --- /dev/null +++ b/components/esm/aipackage.cpp @@ -0,0 +1,37 @@ +#include "aipackage.hpp" + +namespace ESM +{ + void AIPackageList::load(ESMReader &esm) + { + while (esm.hasMoreSubs()) { + // initialize every iteration + AIPackage pack; + esm.getSubName(); + if (esm.retSubName() == 0x54444e43) { // CNDT + mList.back().mCellName = esm.getHString(); + } else if (esm.retSubName() == AI_Wander) { + pack.mType = AI_Wander; + esm.getHExact(&pack.mWander, 14); + mList.push_back(pack); + } else if (esm.retSubName() == AI_Travel) { + pack.mType = AI_Travel; + esm.getHExact(&pack.mTravel, 16); + mList.push_back(pack); + } else if (esm.retSubName() == AI_Escort || + esm.retSubName() == AI_Follow) + { + pack.mType = + (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow; + esm.getHExact(&pack.mTarget, 48); + mList.push_back(pack); + } else if (esm.retSubName() == AI_Activate) { + pack.mType = AI_Activate; + esm.getHExact(&pack.mActivate, 33); + mList.push_back(pack); + } else { // not AI package related data, so leave + return; + } + } + } +} diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp new file mode 100644 index 000000000..3a8547402 --- /dev/null +++ b/components/esm/aipackage.hpp @@ -0,0 +1,95 @@ +#ifndef OPENMW_ESM_AIPACKAGE_H +#define OPENMW_ESM_AIPACKAGE_H + +#include "esm_reader.hpp" + +#include + +namespace ESM +{ + #pragma pack(push) + #pragma pack(1) + + struct AIData + { + // These are probabilities + char mHello, mU1, mFight, mFlee, mAlarm, mU2, mU3, mU4; + // The last u's might be the skills that this NPC can train you + // in? + int mServices; // See the Services enum + }; // 12 bytes + + struct AIWander + { + short mDistance; + short mDuration; + unsigned char mTimeOfDay; + unsigned char mIdle[8]; + unsigned char mUnk; + }; + + struct AITravel + { + float mX, mY, mZ; + long mUnk; + }; + + struct AITarget + { + float mX, mY, mZ; + short mDuration; + NAME32 mId; + short mUnk; + }; + + struct AIActivate + { + NAME32 mName; + unsigned char mUnk; + }; + + #pragma pack(pop) + + enum + { + AI_Wander = 0x575f4941, + AI_Travel = 0x545f4941, + AI_Follow = 0x465f4941, + AI_Escort = 0x455f4941, + AI_Activate = 0x415f4941, + }; + + /// \note Used for storaging packages in a single container + /// w/o manual memory allocation accordingly to policy standards + struct AIPackage + { + int mType; + + // Anonymous union + union + { + AIWander mWander; + AITravel mTravel; + AITarget mTarget; + AIActivate mActivate; + }; + + /// \note for AITarget only, placed here to stick with union, + /// overhead should be not so awful + std::string mCellName; + }; + + struct AIPackageList + { + std::vector mList; + + /// \note This breaks consistency of subrecords reading: + /// after calling it subrecord name is already read, so + /// it needs to use retSubName() if needed. But, hey, there + /// is only one field left (XSCL) and only two records uses AI + void load(ESMReader &esm); + }; +} + +#endif + diff --git a/components/esm/defs_ai.hpp b/components/esm/defs_ai.hpp deleted file mode 100644 index b14cc7d65..000000000 --- a/components/esm/defs_ai.hpp +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef _ESM_DEFS_AI_H -#define _ESM_DEFS_AI_H - -#include "esm_reader.hpp" - -#include - -namespace ESM -{ - #pragma pack(push) - #pragma pack(1) - - struct AIDTstruct - { - // These are probabilities - char hello, u1, fight, flee, alarm, u2, u3, u4; - // The last u's might be the skills that this NPC can train you - // in? - int services; // See the Services enum - }; // 12 bytes - - struct AIWander - { - short distance; - short duration; - char timeOfDay; - char idle[8]; - char unk; - }; - - struct AITravel - { - float x, y, z; - long unk; - }; - - struct AITarget - { - float x, y, z; - short duration; - NAME32 id; - short unk; - }; - - struct AIActivate - { - NAME32 name; - char unk; - }; - - #pragma pack(pop) - - enum - { - AI_Wander = 0x575f4941, - AI_Travel = 0x545f4941, - AI_Follow = 0x465f4941, - AI_Escort = 0x455f4941, - AI_Activate = 0x415f4941, - }; - - /// \note Used for storaging packages in a single container - /// w/o manual memory allocation accordingly to policy standards - struct AIPackage - { - int type; - - // Anonymous union - union - { - AIWander wander; - AITravel travel; - AITarget target; - AIActivate activate; - }; - - /// \note for AITarget only, placed here to stick with union, - /// overhead should be not so awful - std::string cellName; - }; - - struct AIPackageList - { - std::vector list; - - /// \note This breaks consistency of subrecords reading: - /// after calling it subrecord name is already read, so - /// it needs to use retSubName() if needed. But, hey, there - /// is only one field left (XSCL) and only two records uses AI - void load(ESMReader &esm) { - while (esm.hasMoreSubs()) { - // initialize every iteration - AIPackage pack; - - esm.getSubName(); - if (esm.retSubName() == 0x54444e43) { // CNDT - list.back().cellName = esm.getHString(); - } else if (esm.retSubName() == AI_Wander) { - pack.type = AI_Wander; - esm.getHExact(&pack.wander, 14); - list.push_back(pack); - } else if (esm.retSubName() == AI_Travel) { - pack.type = AI_Travel; - esm.getHExact(&pack.travel, 16); - list.push_back(pack); - } else if (esm.retSubName() == AI_Escort || - esm.retSubName() == AI_Follow) - { - pack.type = - (esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow; - - esm.getHExact(&pack.target, 48); - list.push_back(pack); - } else if (esm.retSubName() == AI_Activate) { - pack.type = AI_Activate; - esm.getHExact(&pack.activate, 33); - list.push_back(pack); - } else { // not AI package related data, so leave - return; - } - } - } - }; -} - -#endif - diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index fcef9ccd4..e72944787 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -18,17 +18,17 @@ void Creature::load(ESMReader &esm, const std::string& id) esm.getHNOT(scale, "XSCL"); inventory.load(esm); - spells.load(esm); + mSpells.load(esm); if (esm.isNextSub("AIDT")) { - esm.getHExact(&AI, sizeof(AI)); - hasAI = true; + esm.getHExact(&mAiData, sizeof(mAiData)); + mHasAI = true; } else - hasAI = false; + mHasAI = false; - aiPack.load(esm); + mAiPackage.load(esm); esm.skipRecord(); } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 9db1f3fff..59e926446 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -4,7 +4,7 @@ #include "esm_reader.hpp" #include "loadcont.hpp" #include "defs.hpp" -#include "defs_ai.hpp" +#include "aipackage.hpp" namespace ESM { @@ -62,11 +62,11 @@ struct Creature // Defined in loadcont.hpp InventoryList inventory; - SpellList spells; + SpellList mSpells; - bool hasAI; - AIDTstruct AI; - AIPackageList aiPack; + bool mHasAI; + AIData mAiData; + AIPackageList mAiPackage; std::string mId; diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 9fba48246..637aa0e62 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -36,22 +36,22 @@ void NPC::load(ESMReader &esm, const std::string& id) if (esm.isNextSub("AIDT")) { - esm.getHExact(&AI, sizeof(AI)); - hasAI = true; + esm.getHExact(&mAiData, sizeof(mAiData)); + mHasAI= true; } else - hasAI = false; + mHasAI = false; while (esm.isNextSub("DODT") || esm.isNextSub("DNAM")) { if (esm.retSubName() == 0x54444f44) { // DODT struct Dest dodt; - esm.getHExact(&dodt.pos, 24); - dest.push_back(dodt); + esm.getHExact(&dodt.mPos, 24); + mTransport.push_back(dodt); } else if (esm.retSubName() == 0x4d414e44) { // DNAM struct - dest.back().cellName = esm.getHString(); + mTransport.back().mCellName = esm.getHString(); } } - aiPack.load(esm); + mAiPackage.load(esm); esm.skipRecord(); } diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index c636d1f31..eec978e9f 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -4,7 +4,7 @@ #include "esm_reader.hpp" #include "loadcont.hpp" #include "defs.hpp" -#include "defs_ai.hpp" +#include "aipackage.hpp" namespace ESM { @@ -19,16 +19,16 @@ struct NPC { // This merchant buys: Weapon = 0x00001, - Armor = 0x00002, + Armor = 0x00002, Clothing = 0x00004, - Books = 0x00008, + Books = 0x00008, Ingredients = 0x00010, - Picks = 0x00020, + Picks = 0x00020, Probes = 0x00040, Lights = 0x00080, Apparatus = 0x00100, RepairItem = 0x00200, - Misc = 0x00400, + Misc = 0x00400, // Other services Spells = 0x00800, @@ -47,7 +47,7 @@ struct NPC Respawn = 0x0004, Autocalc = 0x0008, Skeleton = 0x0400, // Skeleton blood effect (white) - Metal = 0x0800 // Metal blood effect (golden?) + Metal = 0x0800 // Metal blood effect (golden?) }; #pragma pack(push) @@ -76,8 +76,8 @@ struct NPC struct Dest { - Position pos; - std::string cellName; + Position mPos; + std::string mCellName; }; NPDTstruct52 npdt52; @@ -88,19 +88,21 @@ struct NPC InventoryList inventory; SpellList spells; - AIDTstruct AI; - bool hasAI; + AIData mAiData; + bool mHasAI; - std::vector dest; - AIPackageList aiPack; + std::vector mTransport; + AIPackageList mAiPackage; - std::string name, model, race, cls, faction, script, - hair, head; // body parts + std::string name, model, race, cls, faction, script; + + // body parts + std::string hair, head; std::string mId; - // Implementation moved to load_impl.cpp - void load(ESMReader &esm, const std::string& id); + // Implementation moved to load_impl.cpp + void load(ESMReader &esm, const std::string& id); }; } #endif From b7697a62adcbf5df4fa1d23e35292b4330719a18 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 29 Aug 2012 18:35:39 -0400 Subject: [PATCH 506/688] Position set back to zero --- libs/openengine/bullet/pmove.cpp | 2 +- libs/openengine/bullet/pmove.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 32a11179f..3d2462ab5 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -301,7 +301,7 @@ bool PM_SlideMove( bool gravity ) if(planes[i].x >= .70) { - pm->ps.velocity = Ogre::Vector3(0,0,0); + pm->ps.velocity.z = 0; return true; } // see how hard we are hitting things diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index a0fec3d1c..aa10a6bff 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -92,7 +92,7 @@ struct playerMove { playerStruct() : gravity(800.0f), speed(480.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) { - origin = Ogre::Vector3(733.164f,900.0f, 839.432f); + origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); viewangles = Ogre::Vector3(0.0f, 0.0f, 0.0f); From 3c3737ed78a81d05a0243687a12a1089d80c2994 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Wed, 29 Aug 2012 19:38:33 -0400 Subject: [PATCH 507/688] Write error message for exception and fix warning Provides details of what went wrong when reading the .esp and also fixes a warning on vs2010. --- apps/launcher/datafilespage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8545be835..7ff2ce44c 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -351,6 +351,7 @@ bool DataFilesPage::setupDataFiles() } catch(std::runtime_error &e) { // An error occurred while reading the .esp + std::cerr << "Error reading .esp: " << e.what() << std::endl; continue; } } From 760f05c4542e8406c98455a04900fab179b5ca0f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Aug 2012 20:47:39 +0200 Subject: [PATCH 508/688] the HUD can be toggled with F12 key; useful for screenshots --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 7 +++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++++ apps/openmw/mwinput/inputmanagerimp.hpp | 2 ++ 5 files changed, 16 insertions(+) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 388dc4af9..389f816dc 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -181,6 +181,7 @@ namespace MWBase virtual void showCrosshair(bool show) = 0; virtual bool getSubtitlesEnabled() = 0; + virtual void toggleHud() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a3d20ac9e..faaa41783 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -86,6 +86,7 @@ WindowManager::WindowManager( , mBatchCount(0) , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) + , mHudEnabled(true) { // Set up the GUI system @@ -875,3 +876,9 @@ bool WindowManager::getSubtitlesEnabled () { return mSubtitlesEnabled; } + +void WindowManager::toggleHud () +{ + mHudEnabled = !mHudEnabled; + mHud->setVisible (mHudEnabled); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index dd8a3b006..38411bb18 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -162,6 +162,7 @@ namespace MWGui virtual void showCrosshair(bool show); virtual bool getSubtitlesEnabled(); + virtual void toggleHud(); virtual void disallowMouse(); virtual void allowMouse(); @@ -223,6 +224,7 @@ namespace MWGui bool mCrosshairEnabled; bool mSubtitlesEnabled; + bool mHudEnabled; /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. // Various stats about player as needed by window manager diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 852f90ae9..f47ea8bb1 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -226,6 +226,9 @@ namespace MWInput case A_QuickKeysMenu: showQuickKeysMenu(); break; + case A_ToggleHUD: + mWindows.toggleHud(); + break; } } } @@ -676,6 +679,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey9] = OIS::KC_9; defaultKeyBindings[A_QuickKey10] = OIS::KC_0; defaultKeyBindings[A_Screenshot] = OIS::KC_SYSRQ; + defaultKeyBindings[A_ToggleHUD] = OIS::KC_F12; std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = OIS::MB_Right; diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index ffa222f44..2572ec115 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -234,6 +234,8 @@ namespace MWInput A_QuickKeysMenu, + A_ToggleHUD, + A_Last // Marker for the last item }; }; From ba43f2997f17d1833b04f5d5f74371f82a6efef9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 30 Aug 2012 22:08:30 +0200 Subject: [PATCH 509/688] fix missing character in font for french morrowind version, thanks to jvoisin --- files/mygui/openmw.font.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/files/mygui/openmw.font.xml b/files/mygui/openmw.font.xml index 73d491e04..cb5dd648f 100644 --- a/files/mygui/openmw.font.xml +++ b/files/mygui/openmw.font.xml @@ -12,6 +12,7 @@ + From 4f1bcb749fab947cc511559198421235171bbf6c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 2 Sep 2012 10:09:41 +0200 Subject: [PATCH 510/688] turned off redundant edge list building --- components/nifogre/ogre_nif_loader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 0fdeaae3d..3f5ac3482 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -969,6 +969,7 @@ public: } mesh = meshMgr.createManual(fullname, mGroup, loader); + mesh->setAutoBuildEdgeLists(false); } meshes.push_back(std::make_pair(mesh, shape->name)); From 950bf663346322a15f1cb212f1324a87a05b4f1b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 2 Sep 2012 11:39:54 +0200 Subject: [PATCH 511/688] throw an exception if default value for a setting is not found --- apps/openmw/mwrender/objects.cpp | 2 +- components/settings/settings.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index a76ef09d1..2a90dea5d 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -106,7 +106,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) extents *= insert->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); - bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && Settings::Manager::getBool("limit small object distance", "Objects"); + bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && Settings::Manager::getBool("limit small object distance", "Viewing distance"); // do not fade out doors. that will cause holes and look stupid if (ptr.getTypeName().find("Door") != std::string::npos) diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index dd89dde65..49fc92a7c 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -75,8 +75,12 @@ const std::string Manager::getString (const std::string& setting, const std::str if (mNewSettings.find(std::make_pair(category, setting)) != mNewSettings.end()) return mNewSettings[std::make_pair(category, setting)]; - std::string defaultval = mDefaultFile.getSetting(setting, category); - return mFile.getSetting(setting, category, defaultval); + std::string defaultval = mDefaultFile.getSetting(setting, category, "NOTFOUND"); + std::string val = mFile.getSetting(setting, category, defaultval); + + if (val == "NOTFOUND") + throw std::runtime_error("Trying to retrieve a non-existing setting: " + setting + " Make sure the settings-default.cfg file was properly installed."); + return val; } const float Manager::getFloat (const std::string& setting, const std::string& category) From fa9f2b268b0869b3203789098a3226ef2de1c9fe Mon Sep 17 00:00:00 2001 From: gugus Date: Sun, 2 Sep 2012 17:57:03 +0200 Subject: [PATCH 512/688] PlaceItem,PlaceItemCell,PlaceAtPC,PlaceAtMe --- apps/openmw/mwbase/world.hpp | 3 + .../mwscript/transformationextensions.cpp | 209 +++++++++++++++++- 2 files changed, 208 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8b809d399..b0ce261f1 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -122,6 +122,9 @@ namespace MWBase virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0; ///< Return a pointer to a liveCellRef with the given Ogre handle. + virtual void + copyObjectToCell(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell, const ESM::Position &pos) = 0; + /// \todo enable reference in the OGRE scene virtual void enable (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 18054374c..c82108f8b 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -16,6 +16,11 @@ #include "ref.hpp" #include "OgreSceneNode.h" + +#include "../mwworld/player.hpp" +#include "components\esm\loadcell.hpp" + +#include "OgreMath.h" namespace MWScript { namespace Transformation @@ -256,11 +261,13 @@ namespace MWScript } catch(std::exception &e) { - /*const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); + const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); if(cell) { - store = MWBase::Environment::get().getWorld()->getExterior(cell->getGridX(),cell->getGridY()); - }*/ + int cx,cy; + MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); + store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); + } } if(store) { @@ -273,7 +280,7 @@ namespace MWScript ay = ay/60.; zRot = zRot/60.; } - //MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot); } else { @@ -315,6 +322,185 @@ namespace MWScript } }; + template + class OpPlaceItemCell : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + //MWWorld::Ptr ptr = R()(runtime); + + std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Float x = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float y = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float z = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float zRot = runtime[0].mFloat; + runtime.pop(); + + MWWorld::CellStore* store = 0; + try + { + store = MWBase::Environment::get().getWorld()->getInterior(cellID); + } + catch(std::exception &e) + { + const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID); + if(cell) + { + int cx,cy; + MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); + store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); + } + } + if(store) + { + ESM::Position pos; + pos.pos[0] = x; + pos.pos[1] = y; + pos.pos[2] = z; + pos.rot[0] = pos.rot[1] = 0; + pos.rot[2] = zRot; + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); + MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,pos); + } + else + { + throw std::runtime_error ("unknown cell"); + } + } + }; + + template + class OpPlaceItem : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + //MWWorld::Ptr ptr = R()(runtime); + + std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Float x = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float y = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float z = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Float zRot = runtime[0].mFloat; + runtime.pop(); + + MWWorld::CellStore* store = 0; + int cx,cy; + MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); + store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); + if(store) + { + ESM::Position pos; + pos.pos[0] = x; + pos.pos[1] = y; + pos.pos[2] = z; + pos.rot[0] = pos.rot[1] = 0; + pos.rot[2] = zRot; + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); + MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,pos); + } + else + { + throw std::runtime_error ("unknown cell"); + } + } + }; + + template + class OpPlaceAtPc : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer count = runtime[0].mInteger; + runtime.pop(); + Interpreter::Type_Float distance = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Integer direction = runtime[0].mInteger; + runtime.pop(); + + ESM::Position ipos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); + Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); + Ogre::Quaternion rot(Ogre::Radian(ipos.rot[2]), Ogre::Vector3::UNIT_Z); + if(direction == 0) pos = pos + distance*rot.yAxis(); + else if(direction == 1) pos = pos - distance*rot.yAxis(); + else if(direction == 2) pos = pos - distance*rot.xAxis(); + else if(direction == 3) pos = pos + distance*rot.xAxis(); + else throw std::runtime_error ("direction must be 0,1,2 or 3"); + + ipos.pos[0] = pos.x; + ipos.pos[1] = pos.y; + ipos.pos[2] = pos.z; + MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); + int icount = ptr.getRefData().getCount(); + ptr.getRefData().setCount(count); + MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,ipos); + ptr.getRefData().setCount(icount); + //store->ge + } + }; + + template + class OpPlaceAtMe : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr me = R()(runtime); + + std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer count = runtime[0].mInteger; + runtime.pop(); + Interpreter::Type_Float distance = runtime[0].mFloat; + runtime.pop(); + Interpreter::Type_Integer direction = runtime[0].mInteger; + runtime.pop(); + + ESM::Position ipos = me.getRefData().getPosition(); + Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); + Ogre::Quaternion rot(Ogre::Radian(ipos.rot[2]), Ogre::Vector3::UNIT_Z); + if(direction == 0) pos = pos + distance*rot.yAxis(); + else if(direction == 1) pos = pos - distance*rot.yAxis(); + else if(direction == 2) pos = pos - distance*rot.xAxis(); + else if(direction == 3) pos = pos + distance*rot.xAxis(); + else throw std::runtime_error ("direction must be 0,1,2 or 3"); + + ipos.pos[0] = pos.x; + ipos.pos[1] = pos.y; + ipos.pos[2] = pos.z; + MWWorld::CellStore* store = me.getCell(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); + int icount = ptr.getRefData().getCount(); + ptr.getRefData().setCount(count); + MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,ipos); + ptr.getRefData().setCount(icount); + //store->ge + } + }; + const int opcodeSetScale = 0x2000164; const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -334,6 +520,12 @@ namespace MWScript const int opcodePositionCell = 0x2000174; const int opcodePositionCellExplicit = 0x2000175; + const int opcodePlaceItemCell = 0x2000176; + const int opcodePlaceItem = 0x2000177; + const int opcodePlaceAtPc = 0x2000178; + const int opcodePlaceAtMe = 0x2000179; + const int opcodePlaceAtMeExplicit = 0x200017a; + void registerExtensions (Compiler::Extensions& extensions) { extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); @@ -345,6 +537,10 @@ namespace MWScript extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit); extensions.registerInstruction("position","ffff",opcodePosition,opcodePositionExplicit); extensions.registerInstruction("positioncell","ffffS",opcodePositionCell,opcodePositionCellExplicit); + extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell); + extensions.registerInstruction("placeitem","cffff",opcodePlaceItem); + extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); + extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -367,6 +563,11 @@ namespace MWScript interpreter.installSegment5(opcodePositionExplicit,new OpPosition); interpreter.installSegment5(opcodePositionCell,new OpPositionCell); interpreter.installSegment5(opcodePositionCellExplicit,new OpPositionCell); + interpreter.installSegment5(opcodePlaceItemCell,new OpPlaceItemCell); + interpreter.installSegment5(opcodePlaceItem,new OpPlaceItem); + interpreter.installSegment5(opcodePlaceAtPc,new OpPlaceAtPc); + interpreter.installSegment5(opcodePlaceAtMe,new OpPlaceAtPc); + interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtPc); } } } From 99885e8ca411ff4e06fd8c77c64fda722301610f Mon Sep 17 00:00:00 2001 From: gugus Date: Sun, 2 Sep 2012 18:04:36 +0200 Subject: [PATCH 513/688] Revert "fix a bug with case sensitivity: when searching for a cell which is already loaded,but with another case, the cell get loaded twice, which is bad :p" This reverts commit 165065d37891be204f67f9cc89de3780a0982b92. --- apps/openmw/mwworld/cells.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index dd2339eeb..822aa78d6 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -8,17 +8,6 @@ #include "class.hpp" #include "containerstore.hpp" -//helper function -std::string toLower (const std::string& name) -{ - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; -} - MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { if (cell->data.flags & ESM::Cell::Interior) @@ -140,14 +129,13 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) { - std::string nName = toLower(name); - std::map::iterator result = mInteriors.find (nName); + std::map::iterator result = mInteriors.find (name); if (result==mInteriors.end()) { - const ESM::Cell *cell = mStore.cells.findInt (nName); + const ESM::Cell *cell = mStore.cells.findInt (name); - result = mInteriors.insert (std::make_pair (nName, Ptr::CellStore (cell))).first; + result = mInteriors.insert (std::make_pair (name, Ptr::CellStore (cell))).first; } if (result->second.mState!=Ptr::CellStore::State_Loaded) From 95c27723f7733f02a8d753d229d52e829c9c4913 Mon Sep 17 00:00:00 2001 From: gugus Date: Sun, 2 Sep 2012 18:12:13 +0200 Subject: [PATCH 514/688] fixed a bug (see commit reverted) --- apps/openmw/mwworld/worldimp.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8ace54378..d9c4fe321 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -541,6 +541,16 @@ namespace MWWorld } } + std::string toLower (const std::string& name) + { + std::string lowerCase; + + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + return lowerCase; + } + void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) { ESM::Position &pos = ptr.getRefData().getPosition(); @@ -550,11 +560,10 @@ namespace MWWorld CellStore *currCell = ptr.getCell(); bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = mWorldScene->isCellActive(*currCell) || isPlayer; - if (*currCell != newCell) { if (isPlayer) { if (!newCell.isExterior()) { - changeToInteriorCell(newCell.cell->name, pos); + changeToInteriorCell(toLower(newCell.cell->name), pos); } else { int cellX = newCell.cell->data.gridX; int cellY = newCell.cell->data.gridY; From c27ff546e4c5ae4975dfda6f7f13dbaddea65843 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 2 Sep 2012 19:40:26 +0200 Subject: [PATCH 515/688] shader cache --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 12 ++++++++++-- apps/openmw/mwrender/renderingmanager.hpp | 3 ++- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- components/files/configurationmanager.cpp | 5 +++++ components/files/configurationmanager.hpp | 2 ++ components/files/fixedpath.hpp | 9 +++++++++ components/files/linuxpath.cpp | 23 +++++++++++++++++++++++ components/files/linuxpath.hpp | 7 +++++++ components/files/macospath.cpp | 21 +++++++++++++++++++++ components/files/macospath.hpp | 7 +++++++ components/files/windowspath.cpp | 5 +++++ components/files/windowspath.hpp | 7 +++++++ extern/shiny | 2 +- 15 files changed, 103 insertions(+), 8 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3de4ba8ac..171fcde7c 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -325,7 +325,7 @@ void OMW::Engine::go() // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mNewGame, mEncoding, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index edeb0fe12..e983353cb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -40,7 +40,8 @@ using namespace Ogre; namespace MWRender { -RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine) +RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, + const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine) :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0), mPhysicsEngine(engine) { // select best shader mode @@ -63,7 +64,9 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const // material system sh::OgrePlatform* platform = new sh::OgrePlatform("General", (resDir / "materials").string()); - platform->setCacheFolder ("./"); + if (!boost::filesystem::exists (cacheDir)) + boost::filesystem::create_directory (cacheDir); + platform->setCacheFolder (cacheDir.string()); mFactory = new sh::Factory(platform); sh::Language lang; @@ -75,6 +78,11 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const else lang = sh::Language_CG; mFactory->setCurrentLanguage (lang); + mFactory->setWriteSourceCache (true); + mFactory->setReadSourceCache (true); + mFactory->setReadMicrocodeCache (true); + mFactory->setWriteMicrocodeCache (true); + mFactory->loadAllFiles(); // Set default mipmap level (NB some APIs ignore this) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index de2449150..e5be238bd 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -53,7 +53,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList virtual MWRender::Actors& getActors(); public: - RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine); + RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, + const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine); virtual ~RenderingManager(); void togglePOV() { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 968aa8cee..869ce94d1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -164,7 +164,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mNextDynamicRecord (0), mCells (mStore, mEsm), @@ -173,7 +173,7 @@ namespace MWWorld mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); - mRendering = new MWRender::RenderingManager(renderer, resDir, mPhysEngine); + mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine); mWeatherManager = new MWWorld::WeatherManager(mRendering); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1d932bce2..6279343d4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -96,7 +96,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap); virtual ~World(); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index ef810fb06..56e55a98d 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -147,6 +147,11 @@ const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const return mFixedPath.getGlobalDataPath(); } +const boost::filesystem::path& ConfigurationManager::getCachePath() const +{ + return mFixedPath.getCachePath(); +} + const boost::filesystem::path& ConfigurationManager::getInstallPath() const { return mFixedPath.getInstallPath(); diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index ecbfac664..9056e792d 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -40,6 +40,8 @@ struct ConfigurationManager const boost::filesystem::path& getLocalDataPath() const; const boost::filesystem::path& getInstallPath() const; + const boost::filesystem::path& getCachePath() const; + const boost::filesystem::path& getLogPath() const; private: diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 9e5c4f8f2..3c1f6d801 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -75,6 +75,7 @@ struct FixedPath , mLocalPath(mPath.getLocalPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) , mInstallPath(mPath.getInstallPath()) + , mCachePath(mPath.getCachePath()) { if (!application_name.empty()) { @@ -83,6 +84,7 @@ struct FixedPath mUserPath /= suffix; mGlobalPath /= suffix; mGlobalDataPath /= suffix; + mCachePath /= suffix; } } @@ -126,6 +128,11 @@ struct FixedPath return mGlobalDataPath; } + const boost::filesystem::path& getCachePath() const + { + return mCachePath; + } + private: PathType mPath; @@ -135,6 +142,8 @@ struct FixedPath boost::filesystem::path mGlobalDataPath; /**< Global application data path */ + boost::filesystem::path mCachePath; + boost::filesystem::path mInstallPath; }; diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 92e4dce33..8009fd3e8 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -62,6 +62,29 @@ boost::filesystem::path LinuxPath::getUserPath() const return userPath; } +boost::filesystem::path LinuxPath::getCachePath() const +{ + boost::filesystem::path userPath("."); + + const char* theDir = getenv("HOME"); + if (theDir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + theDir = pwd->pw_dir; + } + } + + if (theDir != NULL) + { + userPath = boost::filesystem::path(theDir); + } + userPath /= "/.cache/"; + + return userPath; +} + boost::filesystem::path LinuxPath::getGlobalPath() const { boost::filesystem::path globalPath("/etc/"); diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 48fdbb2ff..ce339a5bf 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -67,6 +67,13 @@ struct LinuxPath */ boost::filesystem::path getGlobalDataPath() const; + /** + * \brief + * + * \return boost::filesystem::path + */ + boost::filesystem::path getCachePath() const; + /** * \brief Gets the path of the installed Morrowind version if there is one. * diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 94dafe794..bbd1dbcdd 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -69,6 +69,27 @@ boost::filesystem::path MacOsPath::getGlobalPath() const return globalPath; } +boost::filesystem::path MacOsPath::getCachePath() const +{ + boost::filesystem::path userPath("."); + + const char* theDir = getenv("HOME"); + if (theDir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + theDir = pwd->pw_dir; + } + } + if (theDir != NULL) + { + userPath = boost::filesystem::path(theDir) / "Library/Caches/"; + } + + return userPath; +} + boost::filesystem::path MacOsPath::getLocalPath() const { return boost::filesystem::path("./"); diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 0e9b3402e..0e9136078 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -60,6 +60,13 @@ struct MacOsPath */ boost::filesystem::path getLocalPath() const; + /** + * \brief + * + * \return boost::filesystem::path + */ + boost::filesystem::path getCachePath() const; + /** * \brief * diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index dfa8f20cc..744ad8ce8 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -67,6 +67,11 @@ boost::filesystem::path WindowsPath::getGlobalDataPath() const return getGlobalPath(); } +boost::filesystem::path WindowsPath::getCachePath() const +{ + return getUserPath() / "/cache"; +} + boost::filesystem::path WindowsPath::getInstallPath() const { boost::filesystem::path installPath(""); diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index d4c716c50..fa010730e 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -61,6 +61,13 @@ struct WindowsPath */ boost::filesystem::path getLocalPath() const; + /** + * \brief + * + * \return boost::filesystem::path + */ + boost::filesystem::path getCachePath() const; + /** * \brief Return same path like getGlobalPath * diff --git a/extern/shiny b/extern/shiny index 164bc8d3b..278c6952b 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 164bc8d3bfe860bd16ad89c0bd1b59f465c9bb24 +Subproject commit 278c6952b5c38e4b2a10205953e379598fad71a0 From d2612638241e86a5a1a007b4e9b3d12626a1cdc0 Mon Sep 17 00:00:00 2001 From: gugus Date: Sun, 2 Sep 2012 21:09:07 +0200 Subject: [PATCH 516/688] oups forgot vmformat --- apps/openmw/mwscript/docs/vmformat.txt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 1ea5f8e3e..ac8e5d01d 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -31,7 +31,7 @@ op 0x2000e: PCGetRank implicit op 0x2000f: PCGetRank explicit op 0x20010: AiWander op 0x20011: AiWander, explicit reference -opcodes 0x20012-0x3ffff unused +op s 0x20012-0x3ffff unused Segment 4: (not implemented yet) @@ -181,4 +181,19 @@ op 0x2000170: user4, explicit reference (console only, requires --script-console op 0x2000171: user4 (implicit reference, console only, requires --script-console switch) op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference -opcodes 0x2000174-0x3ffffff unused +op 0x200016c: GetPos +op 0x200016d: GetPosExplicit +op 0x200016e: SetPos +op 0x200016f: SetPosExplicit +op 0x2000170: GetStartingPos +op 0x2000171: GetStartingPosExplicit +op 0x2000172: Position +op 0x2000173: Position Explicit +op 0x2000174: PositionCell +op 0x2000175: PositionCell Explicit +op 0x2000176: PlaceItemCell +op 0x2000177: PlaceItem +op 0x2000178: PlaceAtPc +op 0x2000179: PlaceAtMe +op 0x200017a: PlaceAtMe Explicit +opcodes 0x200017b-0x3ffffff unused From 2efceba1fca6e5d430a5e285016a8937503ad9ec Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 3 Sep 2012 20:32:20 -0400 Subject: [PATCH 517/688] Tearing apart PhysicActor --- apps/openmw/mwworld/physicssystem.cpp | 22 +++--- apps/openmw/mwworld/physicssystem.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- libs/openengine/bullet/physic.cpp | 106 +++++++------------------- libs/openengine/bullet/physic.hpp | 30 ++++---- 5 files changed, 53 insertions(+), 109 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 0af9511cf..7b75ff948 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -254,12 +254,12 @@ namespace MWWorld } void PhysicsSystem::addActor (const std::string& handle, const std::string& mesh, - const Ogre::Vector3& position) + const Ogre::Vector3& position, float scale, const Ogre::Quaternion& rotation) { //TODO:optimize this. Searching the std::map isn't very efficient i think. - mEngine->addCharacter(handle); + mEngine->addCharacter(handle, mesh, position, scale, rotation); OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle); - act->setPosition(btVector3(position.x,position.y,position.z)); + } void PhysicsSystem::removeObject (const std::string& handle) @@ -272,11 +272,12 @@ namespace MWWorld void PhysicsSystem::moveObject (const std::string& handle, Ogre::SceneNode* node) { + Ogre::Vector3 position = node->getPosition(); if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) { // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow // start positions others than 0, 0, 0 - Ogre::Vector3 position = node->getPosition(); + if(dynamic_cast(body->getCollisionShape()) == NULL){ btTransform tr = body->getWorldTransform(); @@ -288,7 +289,7 @@ namespace MWWorld } if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { - /*// TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow + // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow // start positions others than 0, 0, 0 if (handle == "player") { @@ -297,19 +298,20 @@ namespace MWWorld else { act->setPosition(btVector3(position.x,position.y,position.z)); - }*/ + } } } void PhysicsSystem::rotateObject (const std::string& handle, Ogre::SceneNode* node) { - /*if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + Ogre::Quaternion rotation = node->getOrientation(); + if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { + //Needs to be changed act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); - }*/ + } if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) { - Ogre::Quaternion rotation = node->getOrientation(); if(dynamic_cast(body->getCollisionShape()) == NULL) body->getWorldTransform().setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); else @@ -380,7 +382,7 @@ namespace MWWorld void PhysicsSystem::insertActorPhysics(const MWWorld::Ptr& ptr, const std::string model){ Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - addActor (node->getName(), model, node->getPosition()); + addActor (node->getName(), model, node->getPosition(), node->getScale().x, node->getOrientation()); } bool PhysicsSystem::getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max) diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 949aa9d45..1427060f6 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -24,7 +24,7 @@ namespace MWWorld const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position); void addActor (const std::string& handle, const std::string& mesh, - const Ogre::Vector3& position); + const Ogre::Vector3& position, float scale, const Ogre::Quaternion& rotation); void addHeightField (float* heights, int x, int y, float yoffset, diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 73efdd816..7b1c4e819 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -189,7 +189,7 @@ namespace MWWorld mPlayer = new MWWorld::Player (mStore.npcs.find ("player"), *this); mRendering->attachCameraTo(mPlayer->getPlayer()); - mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); + mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0), 0, Ogre::Quaternion::ZERO); // global variables mGlobalVariables = new Globals (mStore); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 0845864ef..584c3db75 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -26,111 +26,71 @@ namespace Physic COL_RAYCASTING = BIT(3) }; - PhysicActor::PhysicActor(std::string name) + PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale): + mName(name), mEngine(engine), mMesh(mesh), mBoxTranslation(Ogre::Vector3::ZERO), mBoxRotation(Ogre::Quaternion::ZERO), mBody(0) { - mName = name; // The capsule is at the origin btTransform transform; transform.setIdentity(); - // External capsule - externalGhostObject = new PairCachingGhostObject(name); - externalGhostObject->setWorldTransform( transform ); - - btScalar externalCapsuleHeight = 120; - btScalar externalCapsuleWidth = 19; - - externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); - externalCollisionShape->setMargin( 0.1 ); - - externalGhostObject->setCollisionShape( externalCollisionShape ); - externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); - - // Internal capsule - internalGhostObject = new PairCachingGhostObject(name); - internalGhostObject->setWorldTransform( transform ); - //internalGhostObject->getBroadphaseHandle()->s - btScalar internalCapsuleHeight = 110; - btScalar internalCapsuleWidth = 17; - - internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); - internalCollisionShape->setMargin( 0.1 ); - - internalGhostObject->setCollisionShape( internalCollisionShape ); - internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); - - mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 40 ),1,4,20,9.8,0.2 ); - mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS); - mCharacter->setUseGhostSweepTest(false); - - mCharacter->mCollision = false; - setGravity(0); - - mTranslation = btVector3(0,0,70); } PhysicActor::~PhysicActor() { - delete mCharacter; - delete internalGhostObject; - delete internalCollisionShape; - delete externalGhostObject; - delete externalCollisionShape; + if(mBody){ + mEngine->dynamicsWorld->removeRigidBody(mBody); + delete mBody; + } } void PhysicActor::setGravity(float gravity) { - mCharacter->setGravity(gravity); - //mCharacter-> + } void PhysicActor::enableCollisions(bool collision) { - mCharacter->mCollision = collision; + } void PhysicActor::setVerticalVelocity(float z) { - mCharacter->setVerticalVelocity(z); + } bool PhysicActor::getCollisionMode() { - return mCharacter->mCollision; + return false; } void PhysicActor::setWalkDirection(const btVector3& mvt) { - mCharacter->setWalkDirection( mvt ); + } - void PhysicActor::Rotate(const btQuaternion& quat) - { - externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat ); - internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat ); - } + void PhysicActor::setRotation(const btQuaternion& quat) { - externalGhostObject->getWorldTransform().setRotation( quat ); - internalGhostObject->getWorldTransform().setRotation( quat ); + //externalGhostObject->getWorldTransform().setRotation( quat ); + //internalGhostObject->getWorldTransform().setRotation( quat ); } btVector3 PhysicActor::getPosition(void) { - return internalGhostObject->getWorldTransform().getOrigin() -mTranslation; + return btVector3(0,0,0);//return internalGhostObject->getWorldTransform().getOrigin() -mTranslation; } btQuaternion PhysicActor::getRotation(void) { - return internalGhostObject->getWorldTransform().getRotation(); + return btQuaternion(0,0,0);//return btQuaternion::internalGhostObject->getWorldTransform().getRotation(); } void PhysicActor::setPosition(const btVector3& pos) { - internalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); - externalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); + //internalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); + //externalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -247,9 +207,7 @@ namespace Physic { if (pa_it->second != NULL) { - dynamicsWorld->removeCollisionObject(pa_it->second->externalGhostObject); - dynamicsWorld->removeCollisionObject(pa_it->second->internalGhostObject); - dynamicsWorld->removeAction(pa_it->second->mCharacter); + delete pa_it->second; pa_it->second = NULL; @@ -477,16 +435,17 @@ namespace Physic } } - void PhysicEngine::addCharacter(std::string name) + void PhysicEngine::addCharacter(std::string name, std::string mesh, + Ogre::Vector3 position, float scale, Ogre::Quaternion rotation) { // Remove character with given name, so we don't make memory // leak when character would be added twice removeCharacter(name); - PhysicActor* newActor = new PhysicActor(name); - dynamicsWorld->addCollisionObject( newActor->externalGhostObject, COL_ACTOR_EXTERNAL, COL_WORLD |COL_ACTOR_EXTERNAL ); - dynamicsWorld->addCollisionObject( newActor->internalGhostObject, COL_ACTOR_INTERNAL, COL_WORLD |COL_ACTOR_INTERNAL ); - dynamicsWorld->addAction( newActor->mCharacter ); + PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale); + + + //dynamicsWorld->addAction( newActor->mCharacter ); PhysicActorMap[name] = newActor; } @@ -499,20 +458,7 @@ namespace Physic PhysicActor* act = it->second; if(act != NULL) { - /*broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); - broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); - PhysicActorContainer::iterator it2 = PhysicActorMap.begin(); - for(;it2!=PhysicActorMap.end();it++) - { - it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); - it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); - it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); - it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); - }*/ - //act->externalGhostObject-> - dynamicsWorld->removeCollisionObject(act->externalGhostObject); - dynamicsWorld->removeCollisionObject(act->internalGhostObject); - dynamicsWorld->removeAction(act->mCharacter); + delete act; } PhysicActorMap.erase(it); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 403af6c6c..af4d56e33 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -15,7 +15,6 @@ class btDefaultCollisionConfiguration; class btSequentialImpulseConstraintSolver; class btCollisionDispatcher; class btDiscreteDynamicsWorld; -class btKinematicCharacterController; class btHeightfieldTerrainShape; namespace BtOgre @@ -33,6 +32,8 @@ namespace Physic { class CMotionState; struct PhysicEvent; + class PhysicEngine; + class RigidBody; /** *This is just used to be able to name objects. @@ -55,7 +56,7 @@ namespace Physic class PhysicActor { public: - PhysicActor(std::string name); + PhysicActor(std::string name, std::string mesh, PhysicEngine *engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale); ~PhysicActor(); @@ -66,8 +67,6 @@ namespace Physic */ void setWalkDirection(const btVector3& mvt); - void Rotate(const btQuaternion& quat); - void setRotation(const btQuaternion& quat); void setGravity(float gravity); @@ -84,21 +83,17 @@ namespace Physic void setPosition(const btVector3& pos); - btKinematicCharacterController* mCharacter; - - PairCachingGhostObject* internalGhostObject; - btCollisionShape* internalCollisionShape; - - PairCachingGhostObject* externalGhostObject; - btCollisionShape* externalCollisionShape; + std::string mName; - /** - *NPC scenenode is located on there feet, and you can't simply translate a btShape, so this vector is used - *each time get/setposition is called. - */ - btVector3 mTranslation; + + private: + OEngine::Physic::RigidBody* mBody; + Ogre::Vector3 mBoxTranslation; + Ogre::Quaternion mBoxRotation; + std::string mMesh; + PhysicEngine* mEngine; }; /** @@ -195,7 +190,8 @@ namespace Physic /** * Create and add a character to the scene, and add it to the ActorMap. */ - void addCharacter(std::string name); + void addCharacter(std::string name, std::string mesh, + Ogre::Vector3 position, float scale, Ogre::Quaternion rotation); /** * Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done? From 2284d76307382c5088cbb3697c68cc113e474dc5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 11:02:18 +0200 Subject: [PATCH 518/688] re-enabled local data directory support in launcher --- apps/launcher/datafilespage.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8545be835..64816fae5 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -197,7 +197,7 @@ bool DataFilesPage::setupDataFiles() desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) -// ("data-local", boost::program_options::value()->default_value("")) + ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) ("encoding", boost::program_options::value()->default_value("win1252")); @@ -206,11 +206,10 @@ bool DataFilesPage::setupDataFiles() // Put the paths in a boost::filesystem vector to use with Files::Collections mDataDirs = Files::PathContainer(variables["data"].as()); -// std::string local = variables["data-local"].as(); -// if (!local.empty()) { -// mDataLocal.push_back(Files::PathContainer::value_type(local)); -// dataDirs.push_back(Files::PathContainer::value_type(local)); -// } + std::string local = variables["data-local"].as(); + if (!local.empty()) { + mDataLocal.push_back(Files::PathContainer::value_type(local)); + } if (mDataDirs.size()>1) mDataDirs.resize (1); From 345eec1135cb5aa6012435c93790effd6d752160 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 13:25:53 +0200 Subject: [PATCH 519/688] Issue #389: added AI package base class and AI packages management class --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/aipackage.cpp | 4 ++ apps/openmw/mwmechanics/aipackage.hpp | 29 ++++++++++ apps/openmw/mwmechanics/aisequence.cpp | 79 ++++++++++++++++++++++++++ apps/openmw/mwmechanics/aisequence.hpp | 54 ++++++++++++++++++ 5 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 apps/openmw/mwmechanics/aipackage.cpp create mode 100644 apps/openmw/mwmechanics/aipackage.hpp create mode 100644 apps/openmw/mwmechanics/aisequence.cpp create mode 100644 apps/openmw/mwmechanics/aisequence.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 66d9d5090..a64ab1ed4 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -61,7 +61,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells - activespells npcstats + activespells npcstats aipackage aisequence ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp new file mode 100644 index 000000000..8193a670b --- /dev/null +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -0,0 +1,4 @@ + +#include "aipackage.hpp" + +MWMechanics::AiPackage::~AiPackage() {} diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp new file mode 100644 index 000000000..d286fbba8 --- /dev/null +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -0,0 +1,29 @@ +#ifndef GAME_MWMECHANICS_AIPACKAGE_H +#define GAME_MWMECHANICS_AIPACKAGE_H + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + /// \brief Base class for AI packages + class AiPackage + { + public: + + virtual ~AiPackage(); + + virtual AiPackage *clone() const = 0; + + virtual bool execute (const MWWorld::Ptr& actor) = 0; + ///< \return Package completed? + + virtual int getTypeId() const = 0; + ///< 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate + }; +} + +#endif + diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp new file mode 100644 index 000000000..c4dcf89af --- /dev/null +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -0,0 +1,79 @@ + +#include "aisequence.hpp" + +#include "aipackage.hpp" + +void MWMechanics::AiSequence::copy (const AiSequence& sequence) +{ + for (std::list::const_iterator iter (sequence.mPackages.begin()); + iter!=sequence.mPackages.end(); ++iter) + mPackages.push_back ((*iter)->clone()); +} + +MWMechanics::AiSequence::AiSequence() : mDone (false) {} + +MWMechanics::AiSequence::AiSequence (const AiSequence& sequence) : mDone (false) +{ + copy (sequence); +} + +MWMechanics::AiSequence& MWMechanics::AiSequence::operator= (const AiSequence& sequence) +{ + if (this!=&sequence) + { + clear(); + copy (sequence); + } + + return *this; +} + +MWMechanics::AiSequence::~AiSequence() +{ + clear(); +} + +int MWMechanics::AiSequence::getTypeId() const +{ + if (mPackages.empty()) + return -1; + + return mPackages.front()->getTypeId(); +} + +bool MWMechanics::AiSequence::isPackageDone() const +{ + return mDone; +} + +void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor) +{ + if (!mPackages.empty()) + { + if (mPackages.front()->execute (actor)) + { + mPackages.erase (mPackages.begin()); + mDone = true; + } + else + mDone = false; + } +} + +void MWMechanics::AiSequence::clear() +{ + for (std::list::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) + delete *iter; + + mPackages.clear(); +} + +void MWMechanics::AiSequence::stack (const AiPackage& package) +{ + mPackages.push_front (package.clone()); +} + +void MWMechanics::AiSequence::queue (const AiPackage& package) +{ + mPackages.push_back (package.clone()); +} diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp new file mode 100644 index 000000000..b9ec9d615 --- /dev/null +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -0,0 +1,54 @@ +#ifndef GAME_MWMECHANICS_AISEQUENCE_H +#define GAME_MWMECHANICS_AISEQUENCE_H + +#include + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + class AiPackage; + + /// \brief Sequence of AI-packages for a single actor + class AiSequence + { + std::list mPackages; + bool mDone; + + void copy (const AiSequence& sequence); + + public: + + AiSequence(); + + AiSequence (const AiSequence& sequence); + + AiSequence& operator= (const AiSequence& sequence); + + virtual ~AiSequence(); + + int getTypeId() const; + ///< -1: None, 0: Wanter, 1 Travel, 2 Escort, 3 Follow, 4 Activate + + bool isPackageDone() const; + ///< Has a package been completed during the last update? + + void execute (const MWWorld::Ptr& actor); + ///< Execute package. + + void clear(); + ///< Remove all packages. + + void stack (const AiPackage& package); + ///< Add \a package to the front of the sequence (suspends current package) + + void queue (const AiPackage& package); + ///< Add \a package to the end of the sequence (executed after all other packages have been + /// completed) + }; +} + +#endif From ead04e1cc38b2a949d9a357a51e9996f4ac9a888 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 13:32:35 +0200 Subject: [PATCH 520/688] Issue #389: added AI sequence to CreatureStats; execute AI packages during the regular actor update --- apps/openmw/mwmechanics/actors.cpp | 4 ++++ apps/openmw/mwmechanics/creaturestats.cpp | 10 ++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e1c5f855f..bd037df9f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -20,6 +20,10 @@ namespace MWMechanics adjustMagicEffects (ptr); calculateCreatureStatModifiers (ptr); calculateDynamicStats (ptr); + + // AI + CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + creatureStats.getAiSequence().execute (ptr); } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 38d2442fa..09603bff2 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -45,4 +45,14 @@ namespace MWMechanics return *this; } + + const AiSequence& CreatureStats::getAiSequence() const + { + return mAiSequence; + } + + AiSequence& CreatureStats::getAiSequence() + { + return mAiSequence; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 9d69c868f..d19972e7b 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -9,6 +9,7 @@ #include "magiceffects.hpp" #include "spells.hpp" #include "activespells.hpp" +#include "aisequence.hpp" namespace MWMechanics { @@ -27,6 +28,7 @@ namespace MWMechanics int mFight; int mFlee; int mAlarm; + AiSequence mAiSequence; public: CreatureStats(); @@ -100,6 +102,10 @@ namespace MWMechanics void setFlee(int value); void setAlarm(int value); + + const AiSequence& getAiSequence() const; + + AiSequence& getAiSequence(); }; // Inline const getters From 1cecab6e3da1f347f2508baea465bd4e94d8395c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:08:59 +0200 Subject: [PATCH 521/688] fixed RefData::getHandle --- apps/openmw/mwworld/refdata.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 14ddd3ec5..41794c2ad 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -75,6 +75,9 @@ namespace MWWorld std::string RefData::getHandle() { + if (!mBaseNode) + return ""; + return mBaseNode->getName(); } From 733654d730dcab2d26ab5d7b2f789adc16ed7c40 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:14:33 +0200 Subject: [PATCH 522/688] Issue #370: implemented target handling in action base class --- apps/openmw/mwworld/action.cpp | 15 ++++++++++----- apps/openmw/mwworld/action.hpp | 13 +++++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 748f6aff7..efce2b129 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -6,7 +6,12 @@ #include "../mwbase/soundmanager.hpp" -MWWorld::Action::Action (bool teleport) : mTeleport (teleport) +const MWWorld::Ptr& MWWorld::Action::getTarget() const +{ + return mTarget; +} + +MWWorld::Action::Action (bool teleport, const Ptr& target) : mTeleport (teleport), mTarget (target) {} MWWorld::Action::~Action() {} @@ -15,16 +20,16 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) { - if (mTeleport == true) + if (mTeleport && actor.getRefData().getHandle()=="player") { - //this is a teleport action, so we need to call playSound MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); } else { - MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, - MWBase::SoundManager::Play_NoTrack); + MWBase::Environment::get().getSoundManager()->playSound3D (mTarget.isEmpty() ? actor : mTarget, + mSoundId, 1.0, 1.0, + mTeleport ? MWBase::SoundManager::Play_NoTrack : MWBase::SoundManager::Play_Normal); } } diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index 511a7002d..c47565113 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -3,15 +3,16 @@ #include +#include "ptr.hpp" + namespace MWWorld { - class Ptr; - /// \brief Abstract base for actions class Action { std::string mSoundId; bool mTeleport; + Ptr mTarget; // not implemented Action (const Action& action); @@ -19,9 +20,13 @@ namespace MWWorld virtual void executeImp (const Ptr& actor) = 0; - public: + protected: - Action (bool teleport = false); + const Ptr& getTarget() const; + + public: + + Action (bool teleport = false, const Ptr& target = Ptr()); ///< \param teleport action will teleport the actor virtual ~Action(); From cc55056adf73f865b4480ba0617839e5a5a5ea12 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:18:59 +0200 Subject: [PATCH 523/688] Issue #370: replaced custom target handling in apply action with base class implementation --- apps/openmw/mwworld/actionapply.cpp | 10 +++++----- apps/openmw/mwworld/actionapply.hpp | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp index 595ee6cb3..f78b8f798 100644 --- a/apps/openmw/mwworld/actionapply.cpp +++ b/apps/openmw/mwworld/actionapply.cpp @@ -6,23 +6,23 @@ namespace MWWorld { ActionApply::ActionApply (const Ptr& target, const std::string& id) - : mTarget (target), mId (id) + : Action (false, target), mId (id) {} void ActionApply::executeImp (const Ptr& actor) { - MWWorld::Class::get (mTarget).apply (mTarget, mId, actor); + MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor); } ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id, int skillIndex, int usageType) - : mTarget (target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) + : Action (false, target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) {} void ActionApplyWithSkill::executeImp (const Ptr& actor) { - if (MWWorld::Class::get (mTarget).apply (mTarget, mId, actor) && mUsageType!=-1) - MWWorld::Class::get (mTarget).skillUsageSucceeded (actor, mSkillIndex, mUsageType); + if (MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor) && mUsageType!=-1) + MWWorld::Class::get (getTarget()).skillUsageSucceeded (actor, mSkillIndex, mUsageType); } } diff --git a/apps/openmw/mwworld/actionapply.hpp b/apps/openmw/mwworld/actionapply.hpp index 523bf9373..3353ae0ee 100644 --- a/apps/openmw/mwworld/actionapply.hpp +++ b/apps/openmw/mwworld/actionapply.hpp @@ -11,7 +11,6 @@ namespace MWWorld { class ActionApply : public Action { - Ptr mTarget; std::string mId; virtual void executeImp (const Ptr& actor); @@ -23,7 +22,6 @@ namespace MWWorld class ActionApplyWithSkill : public Action { - Ptr mTarget; std::string mId; int mSkillIndex; int mUsageType; From 3f181f9a9d71368bcaa847ca00df194979f5e674 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:21:56 +0200 Subject: [PATCH 524/688] Issue #370: Support for targets in containers --- apps/openmw/mwworld/action.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index efce2b129..98682e705 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -22,12 +22,15 @@ void MWWorld::Action::execute (const Ptr& actor) { if (mTeleport && actor.getRefData().getHandle()=="player") { + // sound moves with player when teleporting MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); } else { - MWBase::Environment::get().getSoundManager()->playSound3D (mTarget.isEmpty() ? actor : mTarget, + bool local = mTarget.isEmpty() || !mTarget.isInCell(); // no usable target + + MWBase::Environment::get().getSoundManager()->playSound3D (local ? actor : mTarget, mSoundId, 1.0, 1.0, mTeleport ? MWBase::SoundManager::Play_NoTrack : MWBase::SoundManager::Play_Normal); } From b51c42146f6fa99020c348172b11559060df425f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:24:55 +0200 Subject: [PATCH 525/688] Issue #370: replaced custom target handling in read action with base class implementation --- apps/openmw/mwworld/actionread.cpp | 8 ++++---- apps/openmw/mwworld/actionread.hpp | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 5c6ab93c1..7fb2b23e7 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -8,23 +8,23 @@ namespace MWWorld { - ActionRead::ActionRead (const MWWorld::Ptr& object) : mObject (object) + ActionRead::ActionRead (const MWWorld::Ptr& object) : Action (false, object) { } void ActionRead::executeImp (const MWWorld::Ptr& actor) { - LiveCellRef *ref = mObject.get(); + LiveCellRef *ref = getTarget().get(); if (ref->base->data.isScroll) { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Scroll); - MWBase::Environment::get().getWindowManager()->getScrollWindow()->open(mObject); + MWBase::Environment::get().getWindowManager()->getScrollWindow()->open(getTarget()); } else { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Book); - MWBase::Environment::get().getWindowManager()->getBookWindow()->open(mObject); + MWBase::Environment::get().getWindowManager()->getBookWindow()->open(getTarget()); } } } diff --git a/apps/openmw/mwworld/actionread.hpp b/apps/openmw/mwworld/actionread.hpp index 9bb74fb88..00a4756dd 100644 --- a/apps/openmw/mwworld/actionread.hpp +++ b/apps/openmw/mwworld/actionread.hpp @@ -8,8 +8,6 @@ namespace MWWorld { class ActionRead : public Action { - Ptr mObject; // book or scroll to read - virtual void executeImp (const MWWorld::Ptr& actor); public: From 8c2b4f996c044bc5b92a14f91b847c5759a29bb1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:27:10 +0200 Subject: [PATCH 526/688] Issue #370: replaced custom target handling in equip action with base class implementation --- apps/openmw/mwworld/actionequip.cpp | 6 +++--- apps/openmw/mwworld/actionequip.hpp | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 20e0afb68..d8c019644 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -9,7 +9,7 @@ namespace MWWorld { - ActionEquip::ActionEquip (const MWWorld::Ptr& object) : mObject (object) + ActionEquip::ActionEquip (const MWWorld::Ptr& object) : Action (false, object) { } @@ -19,13 +19,13 @@ namespace MWWorld MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); // slots that this item can be equipped in - std::pair, bool> slots = MWWorld::Class::get(mObject).getEquipmentSlots(mObject); + std::pair, bool> slots = MWWorld::Class::get(getTarget()).getEquipmentSlots(getTarget()); // retrieve ContainerStoreIterator to the item MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (*it == mObject) + if (*it == getTarget()) { break; } diff --git a/apps/openmw/mwworld/actionequip.hpp b/apps/openmw/mwworld/actionequip.hpp index 5685a294a..3b56c7402 100644 --- a/apps/openmw/mwworld/actionequip.hpp +++ b/apps/openmw/mwworld/actionequip.hpp @@ -8,8 +8,6 @@ namespace MWWorld { class ActionEquip : public Action { - Ptr mObject; - virtual void executeImp (const Ptr& actor); public: From ea1c3fe1e46de41d49c85e002da17c0f86768644 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:29:51 +0200 Subject: [PATCH 527/688] Issue #370: replaced custom target handling in open action with base class implementation --- apps/openmw/mwworld/actionopen.cpp | 6 +++--- apps/openmw/mwworld/actionopen.hpp | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index 15a9f510d..040a3856e 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -10,8 +10,8 @@ namespace MWWorld { - ActionOpen::ActionOpen (const MWWorld::Ptr& container) : mContainer (container) { - mContainer = container; + ActionOpen::ActionOpen (const MWWorld::Ptr& container) : Action (false, container) + { } void ActionOpen::executeImp (const MWWorld::Ptr& actor) @@ -20,6 +20,6 @@ namespace MWWorld return; MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getWindowManager()->getContainerWindow()->open(mContainer); + MWBase::Environment::get().getWindowManager()->getContainerWindow()->open(getTarget()); } } diff --git a/apps/openmw/mwworld/actionopen.hpp b/apps/openmw/mwworld/actionopen.hpp index 5666ff293..c49ebefa5 100644 --- a/apps/openmw/mwworld/actionopen.hpp +++ b/apps/openmw/mwworld/actionopen.hpp @@ -10,8 +10,6 @@ namespace MWWorld { class ActionOpen : public Action { - Ptr mContainer; - virtual void executeImp (const MWWorld::Ptr& actor); public: From 8ed8dd649a9ca68ef0f23c71dc77a2c086100c6f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:34:44 +0200 Subject: [PATCH 528/688] Issue #370: replaced custom target handling in take action with base class implementation --- apps/openmw/mwworld/actiontake.cpp | 8 +++----- apps/openmw/mwworld/actiontake.hpp | 2 -- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 90f3c000e..ef24f1536 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -10,7 +10,7 @@ namespace MWWorld { - ActionTake::ActionTake (const MWWorld::Ptr& object) : mObject (object) {} + ActionTake::ActionTake (const MWWorld::Ptr& object) : Action (false, object) {} void ActionTake::executeImp (const Ptr& actor) { @@ -20,10 +20,8 @@ namespace MWWorld // insert into player's inventory MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPtr ("player", true); - MWWorld::Class::get (player).getContainerStore (player).add (mObject); + MWWorld::Class::get (player).getContainerStore (player).add (getTarget()); - // remove from world, if the item is currently in the world (it could also be in a container) - if (mObject.isInCell()) - MWBase::Environment::get().getWorld()->deleteObject (mObject); + MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } } diff --git a/apps/openmw/mwworld/actiontake.hpp b/apps/openmw/mwworld/actiontake.hpp index 52a114acf..b0a9b8247 100644 --- a/apps/openmw/mwworld/actiontake.hpp +++ b/apps/openmw/mwworld/actiontake.hpp @@ -8,8 +8,6 @@ namespace MWWorld { class ActionTake : public Action { - MWWorld::Ptr mObject; - virtual void executeImp (const Ptr& actor); public: From fb8aae243dab4487e56845e45fd59f869b327f46 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:38:21 +0200 Subject: [PATCH 529/688] Issue #370: replaced custom target handling in talk action with base class implementation --- apps/openmw/mwworld/actiontalk.cpp | 4 ++-- apps/openmw/mwworld/actiontalk.hpp | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index d94cb67f4..905497f85 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -6,10 +6,10 @@ namespace MWWorld { - ActionTalk::ActionTalk (const Ptr& actor) : mActor (actor) {} + ActionTalk::ActionTalk (const Ptr& actor) : Action (false, actor) {} void ActionTalk::executeImp (const Ptr& actor) { - MWBase::Environment::get().getDialogueManager()->startDialogue (mActor); + MWBase::Environment::get().getDialogueManager()->startDialogue (getTarget()); } } diff --git a/apps/openmw/mwworld/actiontalk.hpp b/apps/openmw/mwworld/actiontalk.hpp index 53adf9e53..b88b168d8 100644 --- a/apps/openmw/mwworld/actiontalk.hpp +++ b/apps/openmw/mwworld/actiontalk.hpp @@ -8,8 +8,6 @@ namespace MWWorld { class ActionTalk : public Action { - Ptr mActor; - virtual void executeImp (const Ptr& actor); public: From 6813bafbfcf7ed6ae5354fec34e5caf5e0a376cb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 15:42:41 +0200 Subject: [PATCH 530/688] added missing sound effect for chest opening; minor cleanup --- apps/openmw/mwclass/container.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 608fc6122..4f11e1c0e 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -87,7 +87,6 @@ namespace MWClass const std::string lockedSound = "LockedChest"; const std::string trapActivationSound = "Disarm Trap Fail"; - if (ptr.getCellRef().lockLevel>0) { // TODO check for key @@ -98,12 +97,11 @@ namespace MWClass } else { - std::cout << "Unlocked container" << std::endl; if(ptr.getCellRef().trap.empty()) { - // Not trapped, Inventory GUI goes here - //return boost::shared_ptr (new MWWorld::NullAction); - return boost::shared_ptr (new MWWorld::ActionOpen(ptr)); + boost::shared_ptr action (new MWWorld::ActionOpen(ptr)); + action->setSound ("chest open"); + return action; } else { From da041f902f7a9e87e8c54df00056a60002f03004 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Sep 2012 20:56:28 +0200 Subject: [PATCH 531/688] Issue #370: fixed item taking sound --- apps/openmw/mwworld/action.cpp | 6 +++--- apps/openmw/mwworld/action.hpp | 6 +++--- apps/openmw/mwworld/actiontake.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp index 98682e705..a5199fb3e 100644 --- a/apps/openmw/mwworld/action.cpp +++ b/apps/openmw/mwworld/action.cpp @@ -11,7 +11,7 @@ const MWWorld::Ptr& MWWorld::Action::getTarget() const return mTarget; } -MWWorld::Action::Action (bool teleport, const Ptr& target) : mTeleport (teleport), mTarget (target) +MWWorld::Action::Action (bool keepSound, const Ptr& target) : mKeepSound (keepSound), mTarget (target) {} MWWorld::Action::~Action() {} @@ -20,7 +20,7 @@ void MWWorld::Action::execute (const Ptr& actor) { if (!mSoundId.empty()) { - if (mTeleport && actor.getRefData().getHandle()=="player") + if (mKeepSound && actor.getRefData().getHandle()=="player") { // sound moves with player when teleporting MWBase::Environment::get().getSoundManager()->playSound(mSoundId, 1.0, 1.0, @@ -32,7 +32,7 @@ void MWWorld::Action::execute (const Ptr& actor) MWBase::Environment::get().getSoundManager()->playSound3D (local ? actor : mTarget, mSoundId, 1.0, 1.0, - mTeleport ? MWBase::SoundManager::Play_NoTrack : MWBase::SoundManager::Play_Normal); + mKeepSound ? MWBase::SoundManager::Play_NoTrack : MWBase::SoundManager::Play_Normal); } } diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index c47565113..d8e5d93bb 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -11,7 +11,7 @@ namespace MWWorld class Action { std::string mSoundId; - bool mTeleport; + bool mKeepSound; Ptr mTarget; // not implemented @@ -26,8 +26,8 @@ namespace MWWorld public: - Action (bool teleport = false, const Ptr& target = Ptr()); - ///< \param teleport action will teleport the actor + Action (bool keepSound = false, const Ptr& target = Ptr()); + ///< \param keepSound Keep playing the sound even if the object the sound is played on is removed. virtual ~Action(); diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index ef24f1536..fd28dd52e 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -10,7 +10,7 @@ namespace MWWorld { - ActionTake::ActionTake (const MWWorld::Ptr& object) : Action (false, object) {} + ActionTake::ActionTake (const MWWorld::Ptr& object) : Action (true, object) {} void ActionTake::executeImp (const Ptr& actor) { From 7b73bfb09982cb78e773d2053a428e1b35980115 Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Wed, 5 Sep 2012 23:39:58 +0200 Subject: [PATCH 532/688] settings.cpp: fix std::runtime_error compile error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix following error: [ 11%] Building CXX object components/CMakeFiles/components.dir/nifoverrides/nifoverrides.cpp.o /home/edmondo/src/openmw/components/settings/settings.cpp: In static member function ‘static const std::string Settings::Manager::getString(const std::string&, const std::string&)’: /home/edmondo/src/openmw/components/settings/settings.cpp:82:15: error: ‘runtime_error’ is not a member of ‘std’ make[2]: *** [components/CMakeFiles/components.dir/settings/settings.cpp.o] Error 1 --- components/settings/settings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index 49fc92a7c..13015e39c 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -1,6 +1,7 @@ #include "settings.hpp" #include +#include #include #include From 23777033fdcf94fc83479af7fdc71c1e0718a8dd Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 5 Sep 2012 17:44:11 -0400 Subject: [PATCH 533/688] Starting to implement new PhysicActor --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 4 +-- apps/openmw/mwrender/renderingmanager.cpp | 4 ++- apps/openmw/mwworld/physicssystem.cpp | 8 +++-- apps/openmw/mwworld/worldimp.cpp | 4 ++- libs/openengine/bullet/physic.cpp | 44 +++++++++++++---------- libs/openengine/bullet/physic.hpp | 15 ++++---- libs/openengine/bullet/trace.cpp | 5 ++- 8 files changed, 49 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index f03c0fdb1..7c6021652 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -89,7 +89,7 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()){ - physics.insertObjectPhysics(ptr, model); + physics.insertActorPhysics(ptr, model); } MWBase::Environment::get().getMechanicsManager()->addActor (ptr); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c9b16ae1e..851cf050b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -125,7 +125,7 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - physics.insertObjectPhysics(ptr, getModel(ptr)); + physics.insertActorPhysics(ptr, getModel(ptr)); MWBase::Environment::get().getMechanicsManager()->addActor(ptr); } @@ -294,7 +294,7 @@ namespace MWClass { Ogre::Vector3 vector (0, 0, 0); - vector.x = - getMovementSettings (ptr).mLeftRight * 127; + vector.x = getMovementSettings (ptr).mLeftRight * 127; vector.y = getMovementSettings (ptr).mForwardBackward * 127; vector.z = getMovementSettings(ptr).mUpDown * 127; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index edeb0fe12..bec84e0c9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -266,13 +266,15 @@ RenderingManager::rotateObject( float *f = ptr.getRefData().getPosition().rot; rot.x += f[0], rot.y += f[1], rot.z += f[2]; } + if (!isPlayer && isActive) { Ogre::Quaternion xr(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); Ogre::Quaternion yr(Ogre::Radian(rot.y), Ogre::Vector3::UNIT_Y); Ogre::Quaternion zr(Ogre::Radian(rot.z), Ogre::Vector3::UNIT_Z); - + ptr.getRefData().getBaseNode()->setOrientation(xr * yr * zr); } + return force; } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7b75ff948..4285eeb18 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -201,8 +201,7 @@ namespace MWWorld playerphysics->ps.viewangles.y = Ogre::Radian(mPlayerData.yaw).valueDegrees() + 90; - - pm_ref.rightmove = -iter->second.x; + pm_ref.rightmove = iter->second.x; pm_ref.forwardmove = -iter->second.y; pm_ref.upmove = iter->second.z; } @@ -214,6 +213,7 @@ namespace MWWorld const std::vector >& actors) { Pmove(playerphysics); + std::vector< std::pair > response; for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) @@ -224,6 +224,7 @@ namespace MWWorld if(it->first == "player"){ coord = playerphysics->ps.origin ; + } @@ -257,8 +258,9 @@ namespace MWWorld const Ogre::Vector3& position, float scale, const Ogre::Quaternion& rotation) { //TODO:optimize this. Searching the std::map isn't very efficient i think. + std::cout << "NPC position" << position << "\n"; mEngine->addCharacter(handle, mesh, position, scale, rotation); - OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle); + } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7b1c4e819..55c2f4161 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -189,7 +189,9 @@ namespace MWWorld mPlayer = new MWWorld::Player (mStore.npcs.find ("player"), *this); mRendering->attachCameraTo(mPlayer->getPlayer()); - mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0), 0, Ogre::Quaternion::ZERO); + std::string playerCollisionFile = "meshes\\base_anim.nif"; //This is used to make a collision shape for our player + //We will need to support the 1st person file too in the future + mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), playerCollisionFile, Ogre::Vector3 (0, 0, 0), 1, Ogre::Quaternion::ZERO); // global variables mGlobalVariables = new Globals (mStore); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 584c3db75..7db0b154b 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -27,12 +27,12 @@ namespace Physic }; PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale): - mName(name), mEngine(engine), mMesh(mesh), mBoxTranslation(Ogre::Vector3::ZERO), mBoxRotation(Ogre::Quaternion::ZERO), mBody(0) + mName(name), mEngine(engine), mMesh(mesh), mBoxTranslation(Ogre::Vector3::ZERO), mBoxRotation(Ogre::Quaternion::ZERO), mBody(0), collisionMode(false) { - - // The capsule is at the origin - btTransform transform; - transform.setIdentity(); + Ogre::Vector3 test; + mBody = mEngine->createAndAdjustRigidBody(mesh, mName, scale, position, rotation, &test); + std::cout << "Test" << test << "\n"; + mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map } @@ -51,7 +51,7 @@ namespace Physic void PhysicActor::enableCollisions(bool collision) { - + collisionMode = collision; } void PhysicActor::setVerticalVelocity(float z) @@ -61,7 +61,7 @@ namespace Physic bool PhysicActor::getCollisionMode() { - return false; + return collisionMode; } void PhysicActor::setWalkDirection(const btVector3& mvt) @@ -79,12 +79,12 @@ namespace Physic btVector3 PhysicActor::getPosition(void) { - return btVector3(0,0,0);//return internalGhostObject->getWorldTransform().getOrigin() -mTranslation; + return mBody->getWorldTransform().getOrigin();//return internalGhostObject->getWorldTransform().getOrigin() -mTranslation; } btQuaternion PhysicActor::getRotation(void) { - return btQuaternion(0,0,0);//return btQuaternion::internalGhostObject->getWorldTransform().getRotation(); + return mBody->getWorldTransform().getRotation();//return btQuaternion::internalGhostObject->getWorldTransform().getRotation(); } void PhysicActor::setPosition(const btVector3& pos) @@ -290,7 +290,8 @@ namespace Physic mHeightFieldMap.erase(name); } - void PhysicEngine::adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation){ + void PhysicEngine::adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, + Ogre::Vector3 scaledBoxPosition, Ogre::Quaternion boxRotation){ btTransform tr; btBoxShape* box = dynamic_cast(body->getCollisionShape()); if(box != NULL){ @@ -317,8 +318,11 @@ namespace Physic adjustRigidBody(shape, body, scale, position, rotation); } - RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation) + RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, + Ogre::Vector3* scaledBoxPosition, Ogre::Quaternion* boxRotation) { + if(scaledBoxPosition != 0) + *scaledBoxPosition = Ogre::Vector3(0, 5, 0); std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; //std::cout << "The string" << outputstring << "\n"; @@ -348,7 +352,7 @@ namespace Physic } - void PhysicEngine::addRigidBody(RigidBody* body) + void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap) { if(body) { @@ -361,14 +365,16 @@ namespace Physic dynamicsWorld->addRigidBody(body,COL_RAYCASTING,COL_RAYCASTING|COL_WORLD); } body->setActivationState(DISABLE_DEACTIVATION); - RigidBody* oldBody = RigidBodyMap[body->mName]; - if (oldBody != NULL) - { - dynamicsWorld->removeRigidBody(oldBody); - delete oldBody; - } + if(addToMap){ + RigidBody* oldBody = RigidBodyMap[body->mName]; + if (oldBody != NULL) + { + dynamicsWorld->removeRigidBody(oldBody); + delete oldBody; + } - RigidBodyMap[body->mName] = body; + RigidBodyMap[body->mName] = body; + } } } diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index af4d56e33..f22380483 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -92,6 +92,7 @@ namespace Physic OEngine::Physic::RigidBody* mBody; Ogre::Vector3 mBoxTranslation; Ogre::Quaternion mBoxRotation; + bool collisionMode; std::string mMesh; PhysicEngine* mEngine; }; @@ -138,18 +139,18 @@ namespace Physic ~PhysicEngine(); /** - * Create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map, - * so you can get it with the getRigidBody function. - - After created, the body is set to the correct rotation, position, and scale + * Creates a RigidBody. It does not add it to the simulation. + * After created, the body is set to the correct rotation, position, and scale */ - RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); + RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, + Ogre::Vector3* scaledBoxPosition = 0, Ogre::Quaternion* boxRotation = 0); /** * Adjusts a rigid body to the right position and rotation */ - void adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); + void adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, + Ogre::Vector3 scaledBoxPosition = Ogre::Vector3::ZERO, Ogre::Quaternion boxRotation = Ogre::Quaternion::ZERO); /** Mainly used to (but not limited to) adjust rigid bodies based on box shapes to the right position and rotation. */ @@ -169,7 +170,7 @@ namespace Physic /** * Add a RigidBody to the simulation */ - void addRigidBody(RigidBody* body); + void addRigidBody(RigidBody* body, bool addToMap = true); /** * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 57b729db1..474d87ee9 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -35,9 +35,8 @@ void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogr //Ogre::Vector3 startReplace = Ogre::Vector3(650,950, 45); //Ogre::Vector3 endReplace = startReplace; //endReplace.z -= .25; - - const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0f,0.0f), isInterior, enginePass); + const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0f, 0.0f), isInterior, enginePass); if (out.fraction < 0.001f) results->startsolid = true; @@ -100,7 +99,7 @@ const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& //if(enginePass->dynamicsWorld->getCollisionObjectArray().at(60)->getCollisionShape()->isConvex()) // std::cout << "It's convex\n"; - + const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); From 21a8dc5ae7ea05775da68f70d03b4bb8698f448f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Sep 2012 11:43:04 +0200 Subject: [PATCH 534/688] silenced a completely pointless warning --- extern/oics/tinyxmlparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/oics/tinyxmlparser.cpp b/extern/oics/tinyxmlparser.cpp index 663882ac5..253cd93ff 100644 --- a/extern/oics/tinyxmlparser.cpp +++ b/extern/oics/tinyxmlparser.cpp @@ -354,7 +354,7 @@ const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) } else { - while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + while ( (*p && IsWhiteSpace( *p )) || *p == '\n' || *p =='\r' ) ++p; } From 3e3437f9c3e2ed0bdf36bf4496a04d6d76f578ea Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Thu, 6 Sep 2012 20:11:59 -0400 Subject: [PATCH 535/688] Revised create and adjust rigid body functions --- libs/openengine/bullet/physic.cpp | 38 ++++++++++++++++--------------- libs/openengine/bullet/physic.hpp | 6 ++--- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 7db0b154b..f453915c8 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -290,18 +290,13 @@ namespace Physic mHeightFieldMap.erase(name); } - void PhysicEngine::adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3 scaledBoxPosition, Ogre::Quaternion boxRotation){ + void PhysicEngine::adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation, + Ogre::Vector3 scaledBoxTranslation, Ogre::Quaternion boxRotation){ btTransform tr; - btBoxShape* box = dynamic_cast(body->getCollisionShape()); - if(box != NULL){ - Ogre::Vector3 transrot = rotation * shape->boxRotation * (shape->boxTranslation * scale); - Ogre::Vector3 newPosition = transrot + position; - tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); - rotation = rotation * shape->boxRotation; - } - else - tr.setOrigin(btVector3(position.x,position.y,position.z)); + rotation = rotation * boxRotation; + Ogre::Vector3 transrot = rotation * scaledBoxTranslation; + Ogre::Vector3 newPosition = transrot + position; + tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); body->setWorldTransform(tr); } @@ -315,14 +310,18 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - adjustRigidBody(shape, body, scale, position, rotation); + btBoxShape* box = dynamic_cast(shape->Shape); + if(box != NULL) + adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); + else + adjustRigidBody(body, position, rotation); } RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3* scaledBoxPosition, Ogre::Quaternion* boxRotation) + Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation) { - if(scaledBoxPosition != 0) - *scaledBoxPosition = Ogre::Vector3(0, 5, 0); + if(scaledBoxTranslation != 0) + *scaledBoxTranslation = Ogre::Vector3(0, 5, 0); std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; //std::cout << "The string" << outputstring << "\n"; @@ -332,6 +331,7 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); shape->Shape->setLocalScaling( btVector3(scale,scale,scale)); + // @@ -344,9 +344,11 @@ namespace Physic RigidBody* body = new RigidBody(CI,name); body->collide = shape->collide; - //Pass in BulletShape, RigidBody, scale, position, rotation - - adjustRigidBody(shape, body, scale, position, rotation); + btBoxShape* box = dynamic_cast(shape->Shape); + if(box != NULL) + adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); + else + adjustRigidBody(body, position, rotation); return body; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f22380483..f699b7d44 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -143,14 +143,14 @@ namespace Physic * After created, the body is set to the correct rotation, position, and scale */ RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3* scaledBoxPosition = 0, Ogre::Quaternion* boxRotation = 0); + Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0); /** * Adjusts a rigid body to the right position and rotation */ - void adjustRigidBody(BulletShapePtr shape, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3 scaledBoxPosition = Ogre::Vector3::ZERO, Ogre::Quaternion boxRotation = Ogre::Quaternion::ZERO); + void adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation, + Ogre::Vector3 scaledBoxTranslation = Ogre::Vector3::ZERO, Ogre::Quaternion boxRotation = Ogre::Quaternion::IDENTITY); /** Mainly used to (but not limited to) adjust rigid bodies based on box shapes to the right position and rotation. */ From b6fcd337df26ffc8796c95589eb67c7f734259c3 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 8 Sep 2012 15:08:24 +0200 Subject: [PATCH 536/688] Modified the cache path to work on windows --- components/files/fixedpath.hpp | 1 - components/files/linuxpath.cpp | 2 +- components/files/macospath.cpp | 2 +- components/files/windowspath.cpp | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 3c1f6d801..3b04b1621 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -84,7 +84,6 @@ struct FixedPath mUserPath /= suffix; mGlobalPath /= suffix; mGlobalDataPath /= suffix; - mCachePath /= suffix; } } diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 8009fd3e8..829e0f8f4 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -80,7 +80,7 @@ boost::filesystem::path LinuxPath::getCachePath() const { userPath = boost::filesystem::path(theDir); } - userPath /= "/.cache/"; + userPath /= "/.cache/openmw"; return userPath; } diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index bbd1dbcdd..3f4fdde25 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -84,7 +84,7 @@ boost::filesystem::path MacOsPath::getCachePath() const } if (theDir != NULL) { - userPath = boost::filesystem::path(theDir) / "Library/Caches/"; + userPath = boost::filesystem::path(theDir) / "Library/Caches/openmw"; } return userPath; diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 744ad8ce8..19a0cc761 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -69,7 +69,7 @@ boost::filesystem::path WindowsPath::getGlobalDataPath() const boost::filesystem::path WindowsPath::getCachePath() const { - return getUserPath() / "/cache"; + return getUserPath() / "openmw/cache"; } boost::filesystem::path WindowsPath::getInstallPath() const From 6e317f00ebb1f04a6fdbef876c5faef7fd0215df Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 8 Sep 2012 15:55:02 +0200 Subject: [PATCH 537/688] Fix the problem in a different way --- components/files/fixedpath.hpp | 4 ++++ components/files/linuxpath.cpp | 2 +- components/files/macospath.cpp | 2 +- components/files/windowspath.cpp | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 3b04b1621..3a2bdd24a 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -84,6 +84,10 @@ struct FixedPath mUserPath /= suffix; mGlobalPath /= suffix; mGlobalDataPath /= suffix; + mCachePath /= suffix; +#ifdef _WIN32 + mCachePath /= "cache"; +#endif } } diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 829e0f8f4..006749d71 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -80,7 +80,7 @@ boost::filesystem::path LinuxPath::getCachePath() const { userPath = boost::filesystem::path(theDir); } - userPath /= "/.cache/openmw"; + userPath /= ".cache"; return userPath; } diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 3f4fdde25..b4e0ba1d1 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -84,7 +84,7 @@ boost::filesystem::path MacOsPath::getCachePath() const } if (theDir != NULL) { - userPath = boost::filesystem::path(theDir) / "Library/Caches/openmw"; + userPath = boost::filesystem::path(theDir) / "Library/Caches"; } return userPath; diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 19a0cc761..672c13c69 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -69,7 +69,7 @@ boost::filesystem::path WindowsPath::getGlobalDataPath() const boost::filesystem::path WindowsPath::getCachePath() const { - return getUserPath() / "openmw/cache"; + return getUserPath(); } boost::filesystem::path WindowsPath::getInstallPath() const From 97f1be2b0586a40ebe08b7b8e109d389ab0cda66 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 8 Sep 2012 18:47:31 +0200 Subject: [PATCH 538/688] Testing a third way to solve the path issue --- components/files/fixedpath.hpp | 14 +------------- components/files/linuxpath.cpp | 14 ++++++++++---- components/files/linuxpath.hpp | 4 ++++ components/files/macospath.cpp | 13 +++++++++---- components/files/macospath.hpp | 4 ++++ components/files/windowspath.cpp | 11 ++++++++--- components/files/windowspath.hpp | 9 +++++++++ 7 files changed, 45 insertions(+), 24 deletions(-) diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 3a2bdd24a..560a1ab45 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -69,7 +69,7 @@ struct FixedPath * \param [in] application_name - Name of the application */ FixedPath(const std::string& application_name) - : mPath() + : mPath(application_name) , mUserPath(mPath.getUserPath()) , mGlobalPath(mPath.getGlobalPath()) , mLocalPath(mPath.getLocalPath()) @@ -77,18 +77,6 @@ struct FixedPath , mInstallPath(mPath.getInstallPath()) , mCachePath(mPath.getCachePath()) { - if (!application_name.empty()) - { - boost::filesystem::path suffix(application_name + std::string("/")); - - mUserPath /= suffix; - mGlobalPath /= suffix; - mGlobalDataPath /= suffix; - mCachePath /= suffix; -#ifdef _WIN32 - mCachePath /= "cache"; -#endif - } } /** diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 006749d71..0746350c8 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -36,10 +36,15 @@ namespace Files { +LinuxPath::LinuxPath(const std::string& application_name) + : mName(application_name) +{ +} + boost::filesystem::path LinuxPath::getUserPath() const { boost::filesystem::path userPath("."); - boost::filesystem::path suffix("/"); + boost::filesystem::path suffix(mName); const char* theDir = getenv("HOME"); if (theDir == NULL) @@ -65,6 +70,7 @@ boost::filesystem::path LinuxPath::getUserPath() const boost::filesystem::path LinuxPath::getCachePath() const { boost::filesystem::path userPath("."); + boost::filesystem::path suffix(mName); const char* theDir = getenv("HOME"); if (theDir == NULL) @@ -80,7 +86,7 @@ boost::filesystem::path LinuxPath::getCachePath() const { userPath = boost::filesystem::path(theDir); } - userPath /= ".cache"; + userPath /= ".cache" / suffix; return userPath; } @@ -88,7 +94,7 @@ boost::filesystem::path LinuxPath::getCachePath() const boost::filesystem::path LinuxPath::getGlobalPath() const { boost::filesystem::path globalPath("/etc/"); - return globalPath; + return globalPath / mName; } boost::filesystem::path LinuxPath::getLocalPath() const @@ -99,7 +105,7 @@ boost::filesystem::path LinuxPath::getLocalPath() const boost::filesystem::path LinuxPath::getGlobalDataPath() const { boost::filesystem::path globalDataPath("/usr/share/games/"); - return globalDataPath; + return globalDataPath / mName; } boost::filesystem::path LinuxPath::getInstallPath() const diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index ce339a5bf..09acd2be7 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -38,6 +38,8 @@ namespace Files */ struct LinuxPath { + LinuxPath(const std::string& application_name); + /** * \brief Return path to the user directory. * @@ -80,6 +82,8 @@ struct LinuxPath * \return boost::filesystem::path */ boost::filesystem::path getInstallPath() const; + + std::string mName; }; } /* namespace Files */ diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index b4e0ba1d1..d9ebb2e2d 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -39,10 +39,15 @@ namespace Files { +MacOsPath::MacOsPath(const std::string& application_name) + : mName(application_name) +{ +} + boost::filesystem::path MacOsPath::getUserPath() const { boost::filesystem::path userPath("."); - boost::filesystem::path suffix("/"); + boost::filesystem::path suffix(mName); const char* theDir = getenv("HOME"); if (theDir == NULL) @@ -66,7 +71,7 @@ boost::filesystem::path MacOsPath::getUserPath() const boost::filesystem::path MacOsPath::getGlobalPath() const { boost::filesystem::path globalPath("/Library/Preferences/"); - return globalPath; + return globalPath / mName; } boost::filesystem::path MacOsPath::getCachePath() const @@ -84,7 +89,7 @@ boost::filesystem::path MacOsPath::getCachePath() const } if (theDir != NULL) { - userPath = boost::filesystem::path(theDir) / "Library/Caches"; + userPath = boost::filesystem::path(theDir) / "Library/Caches" / mName; } return userPath; @@ -98,7 +103,7 @@ boost::filesystem::path MacOsPath::getLocalPath() const boost::filesystem::path MacOsPath::getGlobalDataPath() const { boost::filesystem::path globalDataPath("/Library/Application Support/"); - return globalDataPath; + return globalDataPath / mName; } boost::filesystem::path MacOsPath::getInstallPath() const diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 0e9136078..591c978aa 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -38,6 +38,8 @@ namespace Files */ struct MacOsPath { + MacOsPath(const std::string& application_name); + /** * \brief Return path to the local directory. * @@ -75,6 +77,8 @@ struct MacOsPath boost::filesystem::path getGlobalDataPath() const; boost::filesystem::path getInstallPath() const; + + std::string mName; }; } /* namespace Files */ diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 672c13c69..dffe3410f 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -20,10 +20,15 @@ namespace Files { +WindowsPath::WindowsPath(const std::string& application_name) + : mName(application_name) +{ +} + boost::filesystem::path WindowsPath::getUserPath() const { boost::filesystem::path userPath("."); - boost::filesystem::path suffix("/"); + boost::filesystem::path suffix(mName); TCHAR path[MAX_PATH]; memset(path, 0, sizeof(path)); @@ -42,7 +47,7 @@ boost::filesystem::path WindowsPath::getUserPath() const boost::filesystem::path WindowsPath::getGlobalPath() const { boost::filesystem::path globalPath("."); - boost::filesystem::path suffix("/"); + boost::filesystem::path suffix(mName); TCHAR path[MAX_PATH]; memset(path, 0, sizeof(path)); @@ -69,7 +74,7 @@ boost::filesystem::path WindowsPath::getGlobalDataPath() const boost::filesystem::path WindowsPath::getCachePath() const { - return getUserPath(); + return getUserPath() / "cache"; } boost::filesystem::path WindowsPath::getInstallPath() const diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index fa010730e..7fe8bc955 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -38,6 +38,13 @@ namespace Files */ struct WindowsPath { + /** + * \brief WindowsPath constructor. + * + * \param [in] application_name - The name of the application. + */ + WindowsPath(const std::string& application_name); + /** * \brief Returns user path i.e.: * "X:\Documents And Settings\\My Documents\My Games\" @@ -81,6 +88,8 @@ struct WindowsPath * \return boost::filesystem::path */ boost::filesystem::path getInstallPath() const; + + std::string mName; }; } /* namespace Files */ From cd3e780614e4cd595997c8ecc1a5900ecc70ef13 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 8 Sep 2012 20:34:43 +0200 Subject: [PATCH 539/688] Removing the unnecessary local variables --- components/files/linuxpath.cpp | 10 +++------- components/files/macospath.cpp | 5 +---- components/files/windowspath.cpp | 10 ++-------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 0746350c8..1716757c6 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -44,7 +44,6 @@ LinuxPath::LinuxPath(const std::string& application_name) boost::filesystem::path LinuxPath::getUserPath() const { boost::filesystem::path userPath("."); - boost::filesystem::path suffix(mName); const char* theDir = getenv("HOME"); if (theDir == NULL) @@ -62,15 +61,12 @@ boost::filesystem::path LinuxPath::getUserPath() const userPath = boost::filesystem::path(theDir); } - userPath /= suffix; - - return userPath; + return userPath / mName; } boost::filesystem::path LinuxPath::getCachePath() const { boost::filesystem::path userPath("."); - boost::filesystem::path suffix(mName); const char* theDir = getenv("HOME"); if (theDir == NULL) @@ -86,9 +82,9 @@ boost::filesystem::path LinuxPath::getCachePath() const { userPath = boost::filesystem::path(theDir); } - userPath /= ".cache" / suffix; + userPath /= ".cache"; - return userPath; + return userPath / mName; } boost::filesystem::path LinuxPath::getGlobalPath() const diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index d9ebb2e2d..9625612ad 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -47,7 +47,6 @@ MacOsPath::MacOsPath(const std::string& application_name) boost::filesystem::path MacOsPath::getUserPath() const { boost::filesystem::path userPath("."); - boost::filesystem::path suffix(mName); const char* theDir = getenv("HOME"); if (theDir == NULL) @@ -63,9 +62,7 @@ boost::filesystem::path MacOsPath::getUserPath() const userPath = boost::filesystem::path(theDir) / "Library/Preferences/"; } - userPath /= suffix; - - return userPath; + return userPath / mName; } boost::filesystem::path MacOsPath::getGlobalPath() const diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index dffe3410f..e8f1a2b08 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -28,7 +28,6 @@ WindowsPath::WindowsPath(const std::string& application_name) boost::filesystem::path WindowsPath::getUserPath() const { boost::filesystem::path userPath("."); - boost::filesystem::path suffix(mName); TCHAR path[MAX_PATH]; memset(path, 0, sizeof(path)); @@ -39,15 +38,12 @@ boost::filesystem::path WindowsPath::getUserPath() const userPath = boost::filesystem::path(path); } - userPath /= suffix; - - return userPath; + return userPath / mName; } boost::filesystem::path WindowsPath::getGlobalPath() const { boost::filesystem::path globalPath("."); - boost::filesystem::path suffix(mName); TCHAR path[MAX_PATH]; memset(path, 0, sizeof(path)); @@ -57,9 +53,7 @@ boost::filesystem::path WindowsPath::getGlobalPath() const globalPath = boost::filesystem::path(path); } - globalPath /= suffix; - - return globalPath; + return globalPath / mName; } boost::filesystem::path WindowsPath::getLocalPath() const From bc6d87ba324040dcb82392e3f29709351a146d1f Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sat, 8 Sep 2012 23:04:53 +0200 Subject: [PATCH 540/688] Oops, that would've broken getUserPath() on Linux... --- components/files/linuxpath.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 1716757c6..0f08b67fe 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -57,11 +57,10 @@ boost::filesystem::path LinuxPath::getUserPath() const if (theDir != NULL) { - suffix = boost::filesystem::path("/.config/"); userPath = boost::filesystem::path(theDir); } - return userPath / mName; + return userPath / ".config" / mName; } boost::filesystem::path LinuxPath::getCachePath() const @@ -82,9 +81,8 @@ boost::filesystem::path LinuxPath::getCachePath() const { userPath = boost::filesystem::path(theDir); } - userPath /= ".cache"; - return userPath / mName; + return userPath / ".cache" / mName; } boost::filesystem::path LinuxPath::getGlobalPath() const From ebf80943a974c393f321ad1701d0996d3a18fd53 Mon Sep 17 00:00:00 2001 From: Adam Hogan Date: Sat, 8 Sep 2012 18:17:03 -0400 Subject: [PATCH 541/688] Added spell buying window --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 5 + apps/openmw/mwgui/dialogue.cpp | 12 +- apps/openmw/mwgui/dialogue.hpp | 2 + apps/openmw/mwgui/mode.hpp | 1 + apps/openmw/mwgui/spellswindow.cpp | 269 ++++++++++++++++++ apps/openmw/mwgui/spellswindow.hpp | 70 +++++ apps/openmw/mwgui/tradewindow.cpp | 51 ++-- apps/openmw/mwgui/tradewindow.hpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 10 + apps/openmw/mwgui/windowmanagerimp.hpp | 4 +- extern/shiny | 2 +- files/mygui/CMakeLists.txt | 1 + files/mygui/openmw_spells_window.layout | 33 +++ 15 files changed, 439 insertions(+), 27 deletions(-) create mode 100644 apps/openmw/mwgui/spellswindow.cpp create mode 100644 apps/openmw/mwgui/spellswindow.hpp create mode 100644 files/mygui/openmw_spells_window.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a64ab1ed4..77b25e0c1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -29,7 +29,7 @@ add_openmw_dir (mwgui map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu - itemselection + itemselection spellswindow ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 389f816dc..99f1e46d2 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -42,6 +42,7 @@ namespace MWGui class Console; class SpellWindow; class TradeWindow; + class SpellsWindow; class ConfirmationDialog; class CountDialog; class ScrollWindow; @@ -106,6 +107,7 @@ namespace MWBase virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0; + virtual MWGui::SpellsWindow* getSpellsWindow() = 0; virtual MWGui::SpellWindow* getSpellWindow() = 0; virtual MWGui::Console* getConsole() = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 841e36bf4..1b7532d0a 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -794,6 +794,11 @@ namespace MWDialogue else win->setShowTrade(false); + if (services & ESM::NPC::Spells) + win->setShowSpells(true); + else + win->setShowSpells(false); + // sort again, because the previous sort was case-sensitive keywordList.sort(stringCompareNoCase); win->setKeywords(keywordList); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 4342b1130..949e745f2 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -17,6 +17,7 @@ #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" +#include "spellswindow.hpp" #include "inventorywindow.hpp" using namespace MWGui; @@ -46,6 +47,7 @@ DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_dialogue_window.layout", parWindowManager) , mEnabled(true) , mShowTrade(false) + , mShowSpells(false) { // Centre dialog center(); @@ -127,6 +129,11 @@ void DialogueWindow::onSelectTopic(std::string topic) mWindowManager.pushGuiMode(GM_Barter); mWindowManager.getTradeWindow()->startTrade(mPtr); } + else if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sSpells")->str) + { + mWindowManager.pushGuiMode(GM_Spells); + mWindowManager.getSpellsWindow()->startSpells(mPtr); + } else MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic)); @@ -148,11 +155,14 @@ void DialogueWindow::setKeywords(std::list keyWords) { mTopicsList->clear(); - bool anyService = mShowTrade; + bool anyService = mShowTrade||mShowSpells; if (mShowTrade) mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarter")->str); + if (mShowSpells) + mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sSpells")->str); + if (anyService) mTopicsList->addSeparator(); diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index e7f2b076c..a43b0d5a7 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -51,6 +51,7 @@ namespace MWGui // various service button visibilities, depending if the npc/creature talked to has these services // make sure to call these before setKeywords() void setShowTrade(bool show) { mShowTrade = show; } + void setShowSpells(bool show) { mShowSpells = show; } protected: void onSelectTopic(std::string topic); @@ -70,6 +71,7 @@ namespace MWGui // various service button visibilities, depending if the npc/creature talked to has these services bool mShowTrade; + bool mShowSpells; bool mEnabled; diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 2d6f69871..8c0a2fc5f 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -20,6 +20,7 @@ namespace MWGui GM_Dialogue, // NPC interaction GM_Barter, GM_Rest, + GM_Spells, // Startup character creation dialogs GM_Name, diff --git a/apps/openmw/mwgui/spellswindow.cpp b/apps/openmw/mwgui/spellswindow.cpp new file mode 100644 index 000000000..5e1797823 --- /dev/null +++ b/apps/openmw/mwgui/spellswindow.cpp @@ -0,0 +1,269 @@ +#include "spellswindow.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" + +#include "../mwmechanics/spells.hpp" +#include "../mwmechanics/creaturestats.hpp" + +#include "inventorywindow.hpp" +#include "tradewindow.hpp" + +namespace MWGui +{ + const int SpellsWindow::sLineHeight = 18; + + SpellsWindow::SpellsWindow(MWBase::WindowManager& parWindowManager) : + WindowBase("openmw_spells_window.layout", parWindowManager) + , ContainerBase(NULL) // no drag&drop + , mSpellsWidgetMap() + , mCurrentY(0) + , mLastPos(0) + , mSpellsWidgets() + , mSpellsPriceMap() + { + setCoord(0, 0, 450, 300); + center(); + + getWidget(mCancelButton, "CancelButton"); + getWidget(mPlayerGold, "PlayerGold"); + getWidget(mSelect, "Select"); + getWidget(mSpells, "Spells"); + getWidget(mSpellsBoxWidget, "SpellsBox"); + getWidget(mSpellsClientWidget, "SpellsClient"); + getWidget(mSpellsScrollerWidget, "SpellsScroller"); + + mSpellsClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &SpellsWindow::onMouseWheel); + + mSpellsScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &SpellsWindow::onScrollChangePosition); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellsWindow::onCancelButtonClicked); + + int cancelButtonWidth = mCancelButton->getTextSize().width + 24; + mCancelButton->setCoord(430-cancelButtonWidth, + mCancelButton->getTop(), + cancelButtonWidth, + mCancelButton->getHeight()); + mSpells->setCoord(450/2-mSpells->getTextSize().width/2, + mSpells->getTop(), + mSpells->getTextSize().width, + mSpells->getHeight()); + mSelect->setCoord(8, + mSelect->getTop(), + mSelect->getTextSize().width, + mSelect->getHeight()); + + static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &SpellsWindow::onWindowResize); + } + + void SpellsWindow::addSpell(std::string spellID) + { + MyGUI::Button* toAdd; + + toAdd = mSpellsClientWidget->createWidget("SandText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + mCurrentY += sLineHeight; + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellID); + /// \todo price adjustment depending on merchantile skill + int price = spell->data.cost*MWBase::Environment::get().getWorld()->getStore().gameSettings.search("fSpellValueMult")->f; + mSpellsPriceMap.insert(std::pair(spell,price)); + if (price>mWindowManager.getInventoryWindow()->getPlayerGold()) + toAdd->setCaption("#A3997B" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + else + toAdd->setCaption(spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + toAdd->setSize(toAdd->getTextSize().width,sLineHeight); + toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellsWindow::onMouseWheel); + toAdd->setUserString("ToolTipType", "Spell"); + toAdd->setUserString("Spell", spellID); + toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellsWindow::onSpellButtonClick); + toAdd->eventMouseSetFocus += MyGUI::newDelegate(this, &SpellsWindow::onFocus); + toAdd->eventMouseLostFocus += MyGUI::newDelegate(this, &SpellsWindow::onFocusLost); + mSpellsWidgets.push_back(toAdd); + mSpellsWidgetMap.insert(std::pair(toAdd,spell)); + } + + void SpellsWindow::onFocusLost(MyGUI::Widget* _sender, MyGUI::Widget* _old) + { + updateLabels(); + } + + void SpellsWindow::onFocus(MyGUI::Widget* _sender, MyGUI::Widget* _old) + { + updateLabels(); + MyGUI::Button* toUpdate; + toUpdate = (MyGUI::Button*) _sender; + const ESM::Spell* spell = mSpellsWidgetMap.find(toUpdate)->second; + int price = mSpellsPriceMap.find(spell)->second; + if (price>mWindowManager.getInventoryWindow()->getPlayerGold()) + toUpdate->setCaption("#A3997B" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + else + toUpdate->setCaption("#D8C09A" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + } + + void SpellsWindow::clearSpells() + { + mSpellsScrollerWidget->setScrollPosition(0); + onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); + mCurrentY = 0; + mSpellsWidgets.clear(); + while (mSpellsClientWidget->getChildCount()!=0) + { + MyGUI::Widget* toRemove; + toRemove = mSpellsClientWidget->getChildAt(0); + mSpellsClientWidget->_destroyChildWidget(toRemove); + } + mSpellsWidgetMap.clear(); + mSpellsPriceMap.clear(); + } + + void SpellsWindow::startSpells(MWWorld::Ptr actor) + { + clearSpells(); + + if (actor.getTypeName() == typeid(ESM::NPC).name()) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& playerSpells = stats.getSpells(); + std::vector spellList = actor.get()->base->spells.list; + for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) + { + bool alreadyHave = false; + for (std::vector::const_iterator it2 = playerSpells.begin(); it2 != playerSpells.end(); ++it2) + { + std::string spellname1 = MWBase::Environment::get().getWorld()->getStore().spells.find(*it)->name; + std::string spellname2 = MWBase::Environment::get().getWorld()->getStore().spells.find(*it2)->name; + if (spellname1.compare(spellname2)==0) + alreadyHave = true; + } + if (alreadyHave==false) + addSpell(*it); + } + } + updateLabels(); + updateScroller(); + } + + void SpellsWindow::onSpellButtonClick(MyGUI::Widget* _sender) + { + const ESM::Spell* spell = mSpellsWidgetMap.find(_sender)->second; + int price = mSpellsPriceMap.find(spell)->second; + + if (mWindowManager.getInventoryWindow()->getPlayerGold()>=price) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + spells.add(spell->name); + mWindowManager.getTradeWindow()->addOrRemoveGold(-price); + mSpellsWidgetMap.erase(_sender); + mSpellsPriceMap.erase(spell); + for (std::vector::iterator it = mSpellsWidgets.begin(); it != mSpellsWidgets.end(); ++it) + { + if (*it==_sender) + { + mSpellsWidgets.erase(it); + break; + } + } + mSpellsClientWidget->_destroyChildWidget(_sender); + unsigned int i; + mSpellsScrollerWidget->setScrollPosition(0); + onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); + mCurrentY = 0; + for (i=0;igetChildCount();i++) + { + MyGUI::Widget* toMove; + toMove = mSpellsClientWidget->getChildAt(i); + toMove->setPosition(0,mCurrentY); + mCurrentY+=sLineHeight; + } + } + else + { + + } + updateLabels(); + updateScroller(); + } + + void SpellsWindow::onWindowResize(MyGUI::Window* _sender) + { + + } + + void SpellsWindow::onCancelButtonClicked(MyGUI::Widget* _sender) + { + mWindowManager.removeGuiMode(GM_Spells); + } + + void SpellsWindow::updateLabels() + { + mPlayerGold->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str + + ": " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCoord(8, + mPlayerGold->getTop(), + mPlayerGold->getTextSize().width, + mPlayerGold->getHeight()); + unsigned int i; + for (i=0;igetChildCount();i++) + { + MyGUI::Button* toUpdate; + toUpdate = (MyGUI::Button*) mSpellsClientWidget->getChildAt(i); + const ESM::Spell* spell = mSpellsWidgetMap.find(toUpdate)->second; + int price = mSpellsPriceMap.find(spell)->second; + if (price>mWindowManager.getInventoryWindow()->getPlayerGold()) + toUpdate->setCaption("#A3997B" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + else + toUpdate->setCaption(spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + } + } + + void SpellsWindow::onReferenceUnavailable() + { + // remove both Spells and Dialogue (since you always trade with the NPC/creature that you have previously talked to) + mWindowManager.removeGuiMode(GM_Spells); + mWindowManager.removeGuiMode(GM_Dialogue); + } + + void SpellsWindow::updateScroller() + { + mSpellsScrollerWidget->setScrollRange(std::max(mCurrentY - mSpellsClientWidget->getHeight(), 0)); + mSpellsScrollerWidget->setScrollPage(std::max(mSpellsClientWidget->getHeight() - sLineHeight, 0)); + if (mCurrentY != 0) + mSpellsScrollerWidget->setTrackSize( (mSpellsBoxWidget->getHeight() / float(mCurrentY)) * mSpellsScrollerWidget->getLineSize() ); + } + + void SpellsWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) + { + int diff = mLastPos - pos; + // Adjust position of all widget according to difference + if (diff == 0) + return; + mLastPos = pos; + + std::vector::const_iterator end = mSpellsWidgets.end(); + for (std::vector::const_iterator it = mSpellsWidgets.begin(); it != end; ++it) + { + (*it)->setCoord((*it)->getCoord() + MyGUI::IntPoint(0, diff)); + } + } + + void SpellsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + { + if (mSpellsScrollerWidget->getScrollPosition() - _rel*0.3 < 0) + mSpellsScrollerWidget->setScrollPosition(0); + else if (mSpellsScrollerWidget->getScrollPosition() - _rel*0.3 > mSpellsScrollerWidget->getScrollRange()-1) + mSpellsScrollerWidget->setScrollPosition(mSpellsScrollerWidget->getScrollRange()-1); + else + mSpellsScrollerWidget->setScrollPosition(mSpellsScrollerWidget->getScrollPosition() - _rel*0.3); + + onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); + } +} diff --git a/apps/openmw/mwgui/spellswindow.hpp b/apps/openmw/mwgui/spellswindow.hpp new file mode 100644 index 000000000..4bc073d14 --- /dev/null +++ b/apps/openmw/mwgui/spellswindow.hpp @@ -0,0 +1,70 @@ +#ifndef MWGUI_SPELLSWINDOW_H +#define MWGUI_SPELLSWINDOW_H + +#include "container.hpp" +#include "window_base.hpp" + +#include "../mwworld/ptr.hpp" + +namespace MyGUI +{ + class Gui; + class Widget; +} + +namespace MWGui +{ + class WindowManager; +} + + +namespace MWGui +{ + class SpellsWindow : public ContainerBase, public WindowBase + { + public: + SpellsWindow(MWBase::WindowManager& parWindowManager); + + void startSpells(MWWorld::Ptr actor); + + protected: + MyGUI::Button* mCancelButton; + MyGUI::TextBox* mPlayerGold; + MyGUI::TextBox* mSpells; + MyGUI::TextBox* mSelect; + + MyGUI::WidgetPtr mSpellsBoxWidget, mSpellsClientWidget; + MyGUI::ScrollBar* mSpellsScrollerWidget; + + std::map mSpellsWidgetMap; + std::map mSpellsPriceMap; + std::vector mSpellsWidgets; + + void onWindowResize(MyGUI::Window* _sender); + void onFilterChanged(MyGUI::Widget* _sender); + void onCancelButtonClicked(MyGUI::Widget* _sender); + void onFocus(MyGUI::Widget* _sender, MyGUI::Widget* _old); + void onFocusLost(MyGUI::Widget* _sender, MyGUI::Widget* _old); + void onSpellButtonClick(MyGUI::Widget* _sender); + void addSpell(std::string spellID); + void clearSpells(); + void updateScroller(); + void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos); + void onMouseWheel(MyGUI::Widget* _sender, int _rel); + int mLastPos,mCurrentY; + + static const int sLineHeight; + + // don't show items that the NPC has equipped in his trade-window. + virtual bool ignoreEquippedItems() { return true; } + + virtual bool isTrading() { return true; } + virtual bool isTradeWindow() { return true; } + + void updateLabels(); + + virtual void onReferenceUnavailable(); + }; +} + +#endif diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 9abd97bb7..dc6c650ac 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -136,6 +136,33 @@ namespace MWGui drawItems(); } + void TradeWindow::addOrRemoveGold(int amount) + { + bool goldFound = false; + MWWorld::Ptr gold; + MWWorld::ContainerStore& playerStore = mWindowManager.getInventoryWindow()->getContainerStore(); + for (MWWorld::ContainerStoreIterator it = playerStore.begin(); + it != playerStore.end(); ++it) + { + if (MWWorld::Class::get(*it).getName(*it) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + { + goldFound = true; + gold = *it; + } + } + if (goldFound) + { + gold.getRefData().setCount(gold.getRefData().getCount() + amount); + } + else + { + assert(amount > 0); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); + ref.getPtr().getRefData().setCount(amount); + playerStore.add(ref.getPtr()); + } + } + void TradeWindow::onOfferButtonClicked(MyGUI::Widget* _sender) { // were there any items traded at all? @@ -186,29 +213,7 @@ namespace MWGui mWindowManager.getInventoryWindow()->transferBoughtItems(); // add or remove gold from the player. - bool goldFound = false; - MWWorld::Ptr gold; - MWWorld::ContainerStore& playerStore = mWindowManager.getInventoryWindow()->getContainerStore(); - for (MWWorld::ContainerStoreIterator it = playerStore.begin(); - it != playerStore.end(); ++it) - { - if (MWWorld::Class::get(*it).getName(*it) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) - { - goldFound = true; - gold = *it; - } - } - if (goldFound) - { - gold.getRefData().setCount(gold.getRefData().getCount() + mCurrentBalance); - } - else - { - assert(mCurrentBalance > 0); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); - ref.getPtr().getRefData().setCount(mCurrentBalance); - playerStore.add(ref.getPtr()); - } + addOrRemoveGold(mCurrentBalance); std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 1daeefa96..4ec55045c 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -32,6 +32,8 @@ namespace MWGui bool npcAcceptsItem(MWWorld::Ptr item); + void addOrRemoveGold(int gold); + protected: MyGUI::Button* mFilterAll; MyGUI::Button* mFilterWeapon; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index faaa41783..012a0e127 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -37,6 +37,7 @@ #include "mainmenu.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" +#include "spellswindow.hpp" #include "settingswindow.hpp" #include "confirmationdialog.hpp" #include "alchemywindow.hpp" @@ -61,6 +62,7 @@ WindowManager::WindowManager( , mScrollWindow(NULL) , mCountDialog(NULL) , mTradeWindow(NULL) + , mSpellsWindow(NULL) , mSettingsWindow(NULL) , mConfirmationDialog(NULL) , mAlchemyWindow(NULL) @@ -126,6 +128,7 @@ WindowManager::WindowManager( mMessageBoxManager = new MessageBoxManager(this); mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); mTradeWindow = new TradeWindow(*this); + mSpellsWindow = new SpellsWindow(*this); mDialogueWindow = new DialogueWindow(*this); mContainerWindow = new ContainerWindow(*this,mDragAndDrop); mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); @@ -182,6 +185,7 @@ WindowManager::~WindowManager() delete mBookWindow; delete mScrollWindow; delete mTradeWindow; + delete mSpellsWindow; delete mSettingsWindow; delete mConfirmationDialog; delete mAlchemyWindow; @@ -228,6 +232,7 @@ void WindowManager::updateVisible() mScrollWindow->setVisible(false); mBookWindow->setVisible(false); mTradeWindow->setVisible(false); + mSpellsWindow->setVisible(false); mSettingsWindow->setVisible(false); mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); @@ -317,6 +322,9 @@ void WindowManager::updateVisible() mInventoryWindow->setVisible(true); mTradeWindow->setVisible(true); break; + case GM_Spells: + mSpellsWindow->setVisible(true); + break; case GM_InterMessageBox: break; case GM_Journal: @@ -509,6 +517,7 @@ void WindowManager::onFrame (float frameDuration) mDialogueWindow->checkReferenceAvailable(); mTradeWindow->checkReferenceAvailable(); + mSpellsWindow->checkReferenceAvailable(); mContainerWindow->checkReferenceAvailable(); mConsole->checkReferenceAvailable(); } @@ -786,6 +795,7 @@ MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } +MWGui::SpellsWindow* WindowManager::getSpellsWindow() { return mSpellsWindow; } MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } MWGui::Console* WindowManager::getConsole() { return mConsole; } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 38411bb18..805107478 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -105,6 +105,7 @@ namespace MWGui virtual MWGui::CountDialog* getCountDialog(); virtual MWGui::ConfirmationDialog* getConfirmationDialog(); virtual MWGui::TradeWindow* getTradeWindow(); + virtual MWGui::SpellsWindow* getSpellsWindow(); virtual MWGui::SpellWindow* getSpellWindow(); virtual MWGui::Console* getConsole(); @@ -212,10 +213,11 @@ namespace MWGui BookWindow* mBookWindow; CountDialog* mCountDialog; TradeWindow* mTradeWindow; + SpellsWindow* mSpellsWindow; // Npc selling spells window SettingsWindow* mSettingsWindow; ConfirmationDialog* mConfirmationDialog; AlchemyWindow* mAlchemyWindow; - SpellWindow* mSpellWindow; + SpellWindow* mSpellWindow; // Player owned spells window QuickKeysMenu* mQuickKeysMenu; CharacterCreation* mCharGen; diff --git a/extern/shiny b/extern/shiny index 278c6952b..164bc8d3b 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 278c6952b5c38e4b2a10205953e379598fad71a0 +Subproject commit 164bc8d3bfe860bd16ad89c0bd1b59f465c9bb24 diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 70f7282e5..05713c5d1 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -64,6 +64,7 @@ set(MYGUI_FILES openmw_text.skin.xml openmw_tooltips.layout openmw_trade_window.layout + openmw_spells_window.layout openmw_windows.skin.xml openmw_quickkeys_menu.layout openmw_quickkeys_menu_assign.layout diff --git a/files/mygui/openmw_spells_window.layout b/files/mygui/openmw_spells_window.layout new file mode 100644 index 000000000..6b45b231c --- /dev/null +++ b/files/mygui/openmw_spells_window.layout @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From fc10649bb517fcc48af111857c54f11026262104 Mon Sep 17 00:00:00 2001 From: Adam Hogan Date: Sat, 8 Sep 2012 18:32:43 -0400 Subject: [PATCH 542/688] Updated shiny --- extern/shiny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny b/extern/shiny index 164bc8d3b..278c6952b 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 164bc8d3bfe860bd16ad89c0bd1b59f465c9bb24 +Subproject commit 278c6952b5c38e4b2a10205953e379598fad71a0 From b516813b284e8951a12ab14feab42ef7db4068fb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 9 Sep 2012 15:08:11 +0200 Subject: [PATCH 543/688] fix a problem with older boost versions --- extern/shiny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/shiny b/extern/shiny index 278c6952b..716841523 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 278c6952b5c38e4b2a10205953e379598fad71a0 +Subproject commit 7168415233905de2864eec71ed4312cb8f83059b From 7934d8abd9ff6b3d4f2eb5cea514009e660f9556 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 9 Sep 2012 16:28:02 +0200 Subject: [PATCH 544/688] Fixed an issue with some paths not concatenating properly. --- components/files/fixedpath.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 560a1ab45..dce4f96c2 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -69,7 +69,7 @@ struct FixedPath * \param [in] application_name - Name of the application */ FixedPath(const std::string& application_name) - : mPath(application_name) + : mPath(application_name + "/") , mUserPath(mPath.getUserPath()) , mGlobalPath(mPath.getGlobalPath()) , mLocalPath(mPath.getLocalPath()) From a61edb60fda6f62fe1d3f450af2f45f7db2aa2b3 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 9 Sep 2012 16:28:51 +0200 Subject: [PATCH 545/688] Fixed an issue with windows CPack failing due to trying to install linux binaries --- CMakeLists.txt | 54 ++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5b8c34ca..af5128705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -630,33 +630,35 @@ if (APPLE) include(CPack) endif (APPLE) -## Linux building -# paths -set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/bin" CACHE PATH "Where to install binaries") -set(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") -set(DATADIR "${DATAROOTDIR}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") -set(DOCDIR "${DATAROOTDIR}/doc/openmw" CACHE PATH "Sets the doc directory to a non-default location.") -set(MANDIR "${DATAROOTDIR}/man" CACHE PATH "Where to install manpages") -set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc/openmw" CACHE PATH "Set config dir") -set(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") +if (NOT WIN32) + ## Linux building + # paths + set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/bin" CACHE PATH "Where to install binaries") + set(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") + set(DATADIR "${DATAROOTDIR}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") + set(DOCDIR "${DATAROOTDIR}/doc/openmw" CACHE PATH "Sets the doc directory to a non-default location.") + set(MANDIR "${DATAROOTDIR}/man" CACHE PATH "Where to install manpages") + set(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc/openmw" CACHE PATH "Set config dir") + set(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") -# Install binaries -INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) -INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) -INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) -INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) + # Install binaries + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) -# Install icon and .desktop -INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}") -INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications") + # Install icon and .desktop + INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}") + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications") -# Install global configuration files -INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" ) -#INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${SYSCONFDIR}" ) -INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" ) -INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" ) -INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.cfg" DESTINATION "${SYSCONFDIR}" ) + # Install global configuration files + INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" ) + #INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${SYSCONFDIR}" ) + INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" ) + INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" ) + INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.cfg" DESTINATION "${SYSCONFDIR}" ) -# Install resources -INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) -INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) + # Install resources + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) + INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) +endif(NOT WIN32) \ No newline at end of file From 08410b9062b54ece3b8bb96c902291de1aa81e40 Mon Sep 17 00:00:00 2001 From: Adam Hogan Date: Sun, 9 Sep 2012 14:10:07 -0400 Subject: [PATCH 546/688] Renamed SpellBuyingWindow, took some suggestions from scrawl --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/windowmanager.hpp | 4 +- apps/openmw/mwgui/dialogue.cpp | 6 +- apps/openmw/mwgui/mode.hpp | 2 +- ...spellswindow.cpp => spellbuyingwindow.cpp} | 148 +++++------------- ...spellswindow.hpp => spellbuyingwindow.hpp} | 30 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 18 +-- apps/openmw/mwgui/windowmanagerimp.hpp | 6 +- files/mygui/CMakeLists.txt | 2 +- ...yout => openmw_spell_buying_window.layout} | 0 files/mygui/openmw_text.skin.xml | 8 + 11 files changed, 76 insertions(+), 150 deletions(-) rename apps/openmw/mwgui/{spellswindow.cpp => spellbuyingwindow.cpp} (56%) rename apps/openmw/mwgui/{spellswindow.hpp => spellbuyingwindow.hpp} (57%) rename files/mygui/{openmw_spells_window.layout => openmw_spell_buying_window.layout} (100%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index aa66ba791..4cf3bd7fd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -29,7 +29,7 @@ add_openmw_dir (mwgui map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu - itemselection spellswindow + itemselection spellbuyingwindow ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 99f1e46d2..14b1051e8 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -42,7 +42,7 @@ namespace MWGui class Console; class SpellWindow; class TradeWindow; - class SpellsWindow; + class SpellBuyingWindow; class ConfirmationDialog; class CountDialog; class ScrollWindow; @@ -107,7 +107,7 @@ namespace MWBase virtual MWGui::CountDialog* getCountDialog() = 0; virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0; virtual MWGui::TradeWindow* getTradeWindow() = 0; - virtual MWGui::SpellsWindow* getSpellsWindow() = 0; + virtual MWGui::SpellBuyingWindow* getSpellBuyingWindow() = 0; virtual MWGui::SpellWindow* getSpellWindow() = 0; virtual MWGui::Console* getConsole() = 0; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 949e745f2..20ae1fd65 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -17,7 +17,7 @@ #include "widgets.hpp" #include "list.hpp" #include "tradewindow.hpp" -#include "spellswindow.hpp" +#include "spellbuyingwindow.hpp" #include "inventorywindow.hpp" using namespace MWGui; @@ -131,8 +131,8 @@ void DialogueWindow::onSelectTopic(std::string topic) } else if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sSpells")->str) { - mWindowManager.pushGuiMode(GM_Spells); - mWindowManager.getSpellsWindow()->startSpells(mPtr); + mWindowManager.pushGuiMode(GM_SpellBuying); + mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); } else diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 8c0a2fc5f..4417f7b9c 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -20,7 +20,7 @@ namespace MWGui GM_Dialogue, // NPC interaction GM_Barter, GM_Rest, - GM_Spells, + GM_SpellBuying, // Startup character creation dialogs GM_Name, diff --git a/apps/openmw/mwgui/spellswindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp similarity index 56% rename from apps/openmw/mwgui/spellswindow.cpp rename to apps/openmw/mwgui/spellbuyingwindow.cpp index 5e1797823..af6bb182d 100644 --- a/apps/openmw/mwgui/spellswindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -1,4 +1,4 @@ -#include "spellswindow.hpp" +#include "spellbuyingwindow.hpp" #include @@ -18,19 +18,16 @@ namespace MWGui { - const int SpellsWindow::sLineHeight = 18; + const int SpellBuyingWindow::sLineHeight = 18; - SpellsWindow::SpellsWindow(MWBase::WindowManager& parWindowManager) : + SpellBuyingWindow::SpellBuyingWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_spells_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop , mSpellsWidgetMap() , mCurrentY(0) , mLastPos(0) - , mSpellsWidgets() - , mSpellsPriceMap() { setCoord(0, 0, 450, 300); - center(); getWidget(mCancelButton, "CancelButton"); getWidget(mPlayerGold, "PlayerGold"); @@ -40,11 +37,11 @@ namespace MWGui getWidget(mSpellsClientWidget, "SpellsClient"); getWidget(mSpellsScrollerWidget, "SpellsScroller"); - mSpellsClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &SpellsWindow::onMouseWheel); + mSpellsClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); - mSpellsScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &SpellsWindow::onScrollChangePosition); + mSpellsScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &SpellBuyingWindow::onScrollChangePosition); - mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellsWindow::onCancelButtonClicked); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); int cancelButtonWidth = mCancelButton->getTextSize().width + 24; mCancelButton->setCoord(430-cancelButtonWidth, @@ -59,71 +56,40 @@ namespace MWGui mSelect->getTop(), mSelect->getTextSize().width, mSelect->getHeight()); - - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &SpellsWindow::onWindowResize); } - void SpellsWindow::addSpell(std::string spellID) + void SpellBuyingWindow::addSpell(std::string spellID) { MyGUI::Button* toAdd; - - toAdd = mSpellsClientWidget->createWidget("SandText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); - mCurrentY += sLineHeight; const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellID); - /// \todo price adjustment depending on merchantile skill int price = spell->data.cost*MWBase::Environment::get().getWorld()->getStore().gameSettings.search("fSpellValueMult")->f; - mSpellsPriceMap.insert(std::pair(spell,price)); - if (price>mWindowManager.getInventoryWindow()->getPlayerGold()) - toAdd->setCaption("#A3997B" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); - else - toAdd->setCaption(spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + toAdd = mSpellsClientWidget->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SpellText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + mCurrentY += sLineHeight; + /// \todo price adjustment depending on merchantile skill + toAdd->setUserData(price); + toAdd->setCaption(spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); - toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellsWindow::onMouseWheel); + toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); toAdd->setUserString("Spell", spellID); - toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellsWindow::onSpellButtonClick); - toAdd->eventMouseSetFocus += MyGUI::newDelegate(this, &SpellsWindow::onFocus); - toAdd->eventMouseLostFocus += MyGUI::newDelegate(this, &SpellsWindow::onFocusLost); - mSpellsWidgets.push_back(toAdd); + toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick); mSpellsWidgetMap.insert(std::pair(toAdd,spell)); } - void SpellsWindow::onFocusLost(MyGUI::Widget* _sender, MyGUI::Widget* _old) - { - updateLabels(); - } - - void SpellsWindow::onFocus(MyGUI::Widget* _sender, MyGUI::Widget* _old) - { - updateLabels(); - MyGUI::Button* toUpdate; - toUpdate = (MyGUI::Button*) _sender; - const ESM::Spell* spell = mSpellsWidgetMap.find(toUpdate)->second; - int price = mSpellsPriceMap.find(spell)->second; - if (price>mWindowManager.getInventoryWindow()->getPlayerGold()) - toUpdate->setCaption("#A3997B" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); - else - toUpdate->setCaption("#D8C09A" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); - } - - void SpellsWindow::clearSpells() + void SpellBuyingWindow::clearSpells() { mSpellsScrollerWidget->setScrollPosition(0); onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); mCurrentY = 0; - mSpellsWidgets.clear(); - while (mSpellsClientWidget->getChildCount()!=0) - { - MyGUI::Widget* toRemove; - toRemove = mSpellsClientWidget->getChildAt(0); - mSpellsClientWidget->_destroyChildWidget(toRemove); - } + while (mSpellsClientWidget->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellsClientWidget->getChildAt(0)); mSpellsWidgetMap.clear(); - mSpellsPriceMap.clear(); } - void SpellsWindow::startSpells(MWWorld::Ptr actor) + void SpellBuyingWindow::startSpellBuying(MWWorld::Ptr actor) { + center(); + mActor = actor; clearSpells(); if (actor.getTypeName() == typeid(ESM::NPC).name()) @@ -150,10 +116,10 @@ namespace MWGui updateScroller(); } - void SpellsWindow::onSpellButtonClick(MyGUI::Widget* _sender) + void SpellBuyingWindow::onSpellButtonClick(MyGUI::Widget* _sender) { const ESM::Spell* spell = mSpellsWidgetMap.find(_sender)->second; - int price = mSpellsPriceMap.find(spell)->second; + int price = *_sender->getUserData(); if (mWindowManager.getInventoryWindow()->getPlayerGold()>=price) { @@ -162,48 +128,19 @@ namespace MWGui MWMechanics::Spells& spells = stats.getSpells(); spells.add(spell->name); mWindowManager.getTradeWindow()->addOrRemoveGold(-price); - mSpellsWidgetMap.erase(_sender); - mSpellsPriceMap.erase(spell); - for (std::vector::iterator it = mSpellsWidgets.begin(); it != mSpellsWidgets.end(); ++it) - { - if (*it==_sender) - { - mSpellsWidgets.erase(it); - break; - } - } - mSpellsClientWidget->_destroyChildWidget(_sender); - unsigned int i; mSpellsScrollerWidget->setScrollPosition(0); onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); - mCurrentY = 0; - for (i=0;igetChildCount();i++) - { - MyGUI::Widget* toMove; - toMove = mSpellsClientWidget->getChildAt(i); - toMove->setPosition(0,mCurrentY); - mCurrentY+=sLineHeight; - } + updateScroller(); + startSpellBuying(mActor); } - else - { - - } - updateLabels(); - updateScroller(); } - void SpellsWindow::onWindowResize(MyGUI::Window* _sender) + void SpellBuyingWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { - + mWindowManager.removeGuiMode(GM_SpellBuying); } - void SpellsWindow::onCancelButtonClicked(MyGUI::Widget* _sender) - { - mWindowManager.removeGuiMode(GM_Spells); - } - - void SpellsWindow::updateLabels() + void SpellBuyingWindow::updateLabels() { mPlayerGold->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str + ": " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); @@ -211,28 +148,16 @@ namespace MWGui mPlayerGold->getTop(), mPlayerGold->getTextSize().width, mPlayerGold->getHeight()); - unsigned int i; - for (i=0;igetChildCount();i++) - { - MyGUI::Button* toUpdate; - toUpdate = (MyGUI::Button*) mSpellsClientWidget->getChildAt(i); - const ESM::Spell* spell = mSpellsWidgetMap.find(toUpdate)->second; - int price = mSpellsPriceMap.find(spell)->second; - if (price>mWindowManager.getInventoryWindow()->getPlayerGold()) - toUpdate->setCaption("#A3997B" + spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); - else - toUpdate->setCaption(spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); - } } - void SpellsWindow::onReferenceUnavailable() + void SpellBuyingWindow::onReferenceUnavailable() { // remove both Spells and Dialogue (since you always trade with the NPC/creature that you have previously talked to) - mWindowManager.removeGuiMode(GM_Spells); + mWindowManager.removeGuiMode(GM_SpellBuying); mWindowManager.removeGuiMode(GM_Dialogue); } - void SpellsWindow::updateScroller() + void SpellBuyingWindow::updateScroller() { mSpellsScrollerWidget->setScrollRange(std::max(mCurrentY - mSpellsClientWidget->getHeight(), 0)); mSpellsScrollerWidget->setScrollPage(std::max(mSpellsClientWidget->getHeight() - sLineHeight, 0)); @@ -240,7 +165,7 @@ namespace MWGui mSpellsScrollerWidget->setTrackSize( (mSpellsBoxWidget->getHeight() / float(mCurrentY)) * mSpellsScrollerWidget->getLineSize() ); } - void SpellsWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) + void SpellBuyingWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) { int diff = mLastPos - pos; // Adjust position of all widget according to difference @@ -248,14 +173,16 @@ namespace MWGui return; mLastPos = pos; - std::vector::const_iterator end = mSpellsWidgets.end(); - for (std::vector::const_iterator it = mSpellsWidgets.begin(); it != end; ++it) + unsigned int i; + for (i=0;igetChildCount();i++) { - (*it)->setCoord((*it)->getCoord() + MyGUI::IntPoint(0, diff)); + MyGUI::Widget* toMove; + toMove = mSpellsClientWidget->getChildAt(i); + toMove->setCoord(toMove->getCoord() + MyGUI::IntPoint(0, diff)); } } - void SpellsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) + void SpellBuyingWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { if (mSpellsScrollerWidget->getScrollPosition() - _rel*0.3 < 0) mSpellsScrollerWidget->setScrollPosition(0); @@ -267,3 +194,4 @@ namespace MWGui onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); } } + diff --git a/apps/openmw/mwgui/spellswindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp similarity index 57% rename from apps/openmw/mwgui/spellswindow.hpp rename to apps/openmw/mwgui/spellbuyingwindow.hpp index 4bc073d14..7c16b116a 100644 --- a/apps/openmw/mwgui/spellswindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -1,5 +1,5 @@ -#ifndef MWGUI_SPELLSWINDOW_H -#define MWGUI_SPELLSWINDOW_H +#ifndef MWGUI_SpellBuyingWINDOW_H +#define MWGUI_SpellBuyingWINDOW_H #include "container.hpp" #include "window_base.hpp" @@ -20,12 +20,12 @@ namespace MWGui namespace MWGui { - class SpellsWindow : public ContainerBase, public WindowBase + class SpellBuyingWindow : public ContainerBase, public WindowBase { public: - SpellsWindow(MWBase::WindowManager& parWindowManager); + SpellBuyingWindow(MWBase::WindowManager& parWindowManager); - void startSpells(MWWorld::Ptr actor); + void startSpellBuying(MWWorld::Ptr actor); protected: MyGUI::Button* mCancelButton; @@ -36,31 +36,21 @@ namespace MWGui MyGUI::WidgetPtr mSpellsBoxWidget, mSpellsClientWidget; MyGUI::ScrollBar* mSpellsScrollerWidget; - std::map mSpellsWidgetMap; - std::map mSpellsPriceMap; - std::vector mSpellsWidgets; + MWWorld::Ptr mActor; + + std::map mSpellsWidgetMap; - void onWindowResize(MyGUI::Window* _sender); - void onFilterChanged(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender); - void onFocus(MyGUI::Widget* _sender, MyGUI::Widget* _old); - void onFocusLost(MyGUI::Widget* _sender, MyGUI::Widget* _old); void onSpellButtonClick(MyGUI::Widget* _sender); - void addSpell(std::string spellID); - void clearSpells(); void updateScroller(); void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos); void onMouseWheel(MyGUI::Widget* _sender, int _rel); + void addSpell(std::string spellID); + void clearSpells(); int mLastPos,mCurrentY; static const int sLineHeight; - // don't show items that the NPC has equipped in his trade-window. - virtual bool ignoreEquippedItems() { return true; } - - virtual bool isTrading() { return true; } - virtual bool isTradeWindow() { return true; } - void updateLabels(); virtual void onReferenceUnavailable(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 012a0e127..dcefd1e71 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -37,7 +37,7 @@ #include "mainmenu.hpp" #include "countdialog.hpp" #include "tradewindow.hpp" -#include "spellswindow.hpp" +#include "spellbuyingwindow.hpp" #include "settingswindow.hpp" #include "confirmationdialog.hpp" #include "alchemywindow.hpp" @@ -62,7 +62,7 @@ WindowManager::WindowManager( , mScrollWindow(NULL) , mCountDialog(NULL) , mTradeWindow(NULL) - , mSpellsWindow(NULL) + , mSpellBuyingWindow(NULL) , mSettingsWindow(NULL) , mConfirmationDialog(NULL) , mAlchemyWindow(NULL) @@ -128,7 +128,7 @@ WindowManager::WindowManager( mMessageBoxManager = new MessageBoxManager(this); mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); mTradeWindow = new TradeWindow(*this); - mSpellsWindow = new SpellsWindow(*this); + mSpellBuyingWindow = new SpellBuyingWindow(*this); mDialogueWindow = new DialogueWindow(*this); mContainerWindow = new ContainerWindow(*this,mDragAndDrop); mHud = new HUD(w,h, mShowFPSLevel, mDragAndDrop); @@ -185,7 +185,7 @@ WindowManager::~WindowManager() delete mBookWindow; delete mScrollWindow; delete mTradeWindow; - delete mSpellsWindow; + delete mSpellBuyingWindow; delete mSettingsWindow; delete mConfirmationDialog; delete mAlchemyWindow; @@ -232,7 +232,7 @@ void WindowManager::updateVisible() mScrollWindow->setVisible(false); mBookWindow->setVisible(false); mTradeWindow->setVisible(false); - mSpellsWindow->setVisible(false); + mSpellBuyingWindow->setVisible(false); mSettingsWindow->setVisible(false); mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); @@ -322,8 +322,8 @@ void WindowManager::updateVisible() mInventoryWindow->setVisible(true); mTradeWindow->setVisible(true); break; - case GM_Spells: - mSpellsWindow->setVisible(true); + case GM_SpellBuying: + mSpellBuyingWindow->setVisible(true); break; case GM_InterMessageBox: break; @@ -517,7 +517,7 @@ void WindowManager::onFrame (float frameDuration) mDialogueWindow->checkReferenceAvailable(); mTradeWindow->checkReferenceAvailable(); - mSpellsWindow->checkReferenceAvailable(); + mSpellBuyingWindow->checkReferenceAvailable(); mContainerWindow->checkReferenceAvailable(); mConsole->checkReferenceAvailable(); } @@ -795,7 +795,7 @@ MWGui::ScrollWindow* WindowManager::getScrollWindow() { return mScrollWindow; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } -MWGui::SpellsWindow* WindowManager::getSpellsWindow() { return mSpellsWindow; } +MWGui::SpellBuyingWindow* WindowManager::getSpellBuyingWindow() { return mSpellBuyingWindow; } MWGui::SpellWindow* WindowManager::getSpellWindow() { return mSpellWindow; } MWGui::Console* WindowManager::getConsole() { return mConsole; } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 805107478..a91478bbc 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -105,7 +105,7 @@ namespace MWGui virtual MWGui::CountDialog* getCountDialog(); virtual MWGui::ConfirmationDialog* getConfirmationDialog(); virtual MWGui::TradeWindow* getTradeWindow(); - virtual MWGui::SpellsWindow* getSpellsWindow(); + virtual MWGui::SpellBuyingWindow* getSpellBuyingWindow(); virtual MWGui::SpellWindow* getSpellWindow(); virtual MWGui::Console* getConsole(); @@ -213,11 +213,11 @@ namespace MWGui BookWindow* mBookWindow; CountDialog* mCountDialog; TradeWindow* mTradeWindow; - SpellsWindow* mSpellsWindow; // Npc selling spells window + SpellBuyingWindow* mSpellBuyingWindow; SettingsWindow* mSettingsWindow; ConfirmationDialog* mConfirmationDialog; AlchemyWindow* mAlchemyWindow; - SpellWindow* mSpellWindow; // Player owned spells window + SpellWindow* mSpellWindow; QuickKeysMenu* mQuickKeysMenu; CharacterCreation* mCharGen; diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 05713c5d1..4a215c50f 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -64,7 +64,7 @@ set(MYGUI_FILES openmw_text.skin.xml openmw_tooltips.layout openmw_trade_window.layout - openmw_spells_window.layout + openmw_spell_buying_window.layout openmw_windows.skin.xml openmw_quickkeys_menu.layout openmw_quickkeys_menu_assign.layout diff --git a/files/mygui/openmw_spells_window.layout b/files/mygui/openmw_spell_buying_window.layout similarity index 100% rename from files/mygui/openmw_spells_window.layout rename to files/mygui/openmw_spell_buying_window.layout diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index e29483e35..3e1a977e5 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -17,6 +17,14 @@ + + + + + + + + From 3fad1c07868a769931492f253450793386767d75 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Sep 2012 20:43:21 +0200 Subject: [PATCH 547/688] fixed a crash --- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 +- files/mygui/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index af6bb182d..5750bb869 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -21,7 +21,7 @@ namespace MWGui const int SpellBuyingWindow::sLineHeight = 18; SpellBuyingWindow::SpellBuyingWindow(MWBase::WindowManager& parWindowManager) : - WindowBase("openmw_spells_window.layout", parWindowManager) + WindowBase("openmw_spell_buying_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop , mSpellsWidgetMap() , mCurrentY(0) diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 4a215c50f..82fb64c76 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -70,6 +70,7 @@ set(MYGUI_FILES openmw_quickkeys_menu_assign.layout openmw_itemselection_dialog.layout openmw_magicselection_dialog.layout + openmw_spell_buying_window.layout smallbars.png VeraMono.ttf markers.png From 06677a0fc8220c21ce30cc3b79a3918a598a5d61 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Sep 2012 21:03:39 +0200 Subject: [PATCH 548/688] fixed ID handling and some misc cleanup --- apps/openmw/mwgui/spellbuyingwindow.cpp | 43 ++++++++++++------------- apps/openmw/mwgui/spellbuyingwindow.hpp | 2 +- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 5750bb869..970651372 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -58,12 +58,11 @@ namespace MWGui mSelect->getHeight()); } - void SpellBuyingWindow::addSpell(std::string spellID) + void SpellBuyingWindow::addSpell(std::string spellId) { - MyGUI::Button* toAdd; - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellID); + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); int price = spell->data.cost*MWBase::Environment::get().getWorld()->getStore().gameSettings.search("fSpellValueMult")->f; - toAdd = mSpellsClientWidget->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SpellText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mSpellsClientWidget->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SpellText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; /// \todo price adjustment depending on merchantile skill toAdd->setUserData(price); @@ -71,9 +70,9 @@ namespace MWGui toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); - toAdd->setUserString("Spell", spellID); + toAdd->setUserString("Spell", spellId); toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick); - mSpellsWidgetMap.insert(std::pair(toAdd,spell)); + mSpellsWidgetMap.insert(std::make_pair (toAdd, spellId)); } void SpellBuyingWindow::clearSpells() @@ -92,33 +91,33 @@ namespace MWGui mActor = actor; clearSpells(); - if (actor.getTypeName() == typeid(ESM::NPC).name()) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); + MWMechanics::Spells& playerSpells = stats.getSpells(); + /// \todo get spell list via class interface + std::vector spellList = actor.get()->base->spells.list; + for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& playerSpells = stats.getSpells(); - std::vector spellList = actor.get()->base->spells.list; - for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) + bool alreadyHave = false; + for (std::vector::const_iterator it2 = playerSpells.begin(); it2 != playerSpells.end(); ++it2) { - bool alreadyHave = false; - for (std::vector::const_iterator it2 = playerSpells.begin(); it2 != playerSpells.end(); ++it2) + if (*it==*it2) { - std::string spellname1 = MWBase::Environment::get().getWorld()->getStore().spells.find(*it)->name; - std::string spellname2 = MWBase::Environment::get().getWorld()->getStore().spells.find(*it2)->name; - if (spellname1.compare(spellname2)==0) - alreadyHave = true; + alreadyHave = true; + break; } - if (alreadyHave==false) - addSpell(*it); } + + if (alreadyHave==false) + addSpell(*it); } + updateLabels(); updateScroller(); } void SpellBuyingWindow::onSpellButtonClick(MyGUI::Widget* _sender) { - const ESM::Spell* spell = mSpellsWidgetMap.find(_sender)->second; int price = *_sender->getUserData(); if (mWindowManager.getInventoryWindow()->getPlayerGold()>=price) @@ -126,7 +125,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& spells = stats.getSpells(); - spells.add(spell->name); + spells.add (mSpellsWidgetMap.find(_sender)->second); mWindowManager.getTradeWindow()->addOrRemoveGold(-price); mSpellsScrollerWidget->setScrollPosition(0); onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 7c16b116a..66cec2f2a 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -38,7 +38,7 @@ namespace MWGui MWWorld::Ptr mActor; - std::map mSpellsWidgetMap; + std::map mSpellsWidgetMap; void onCancelButtonClicked(MyGUI::Widget* _sender); void onSpellButtonClick(MyGUI::Widget* _sender); From 9c49ca1468a03c4e80e724f35c1949ed3ec10482 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Sep 2012 21:08:57 +0200 Subject: [PATCH 549/688] a bit more random cleanup --- apps/openmw/mwgui/spellbuyingwindow.cpp | 10 ++++------ apps/openmw/mwgui/spellbuyingwindow.hpp | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 970651372..4d89ee8c2 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -23,7 +23,6 @@ namespace MWGui SpellBuyingWindow::SpellBuyingWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_spell_buying_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop - , mSpellsWidgetMap() , mCurrentY(0) , mLastPos(0) { @@ -58,7 +57,7 @@ namespace MWGui mSelect->getHeight()); } - void SpellBuyingWindow::addSpell(std::string spellId) + void SpellBuyingWindow::addSpell(const std::string& spellId) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); int price = spell->data.cost*MWBase::Environment::get().getWorld()->getStore().gameSettings.search("fSpellValueMult")->f; @@ -85,7 +84,7 @@ namespace MWGui mSpellsWidgetMap.clear(); } - void SpellBuyingWindow::startSpellBuying(MWWorld::Ptr actor) + void SpellBuyingWindow::startSpellBuying(const MWWorld::Ptr& actor) { center(); mActor = actor; @@ -94,7 +93,7 @@ namespace MWGui MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::Spells& playerSpells = stats.getSpells(); - /// \todo get spell list via class interface + /// \todo get spell list via MWWorld::Class interface std::vector spellList = actor.get()->base->spells.list; for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) { @@ -172,8 +171,7 @@ namespace MWGui return; mLastPos = pos; - unsigned int i; - for (i=0;igetChildCount();i++) + for (unsigned int i=0;igetChildCount();i++) { MyGUI::Widget* toMove; toMove = mSpellsClientWidget->getChildAt(i); diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 66cec2f2a..6f94e91b2 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -25,7 +25,7 @@ namespace MWGui public: SpellBuyingWindow(MWBase::WindowManager& parWindowManager); - void startSpellBuying(MWWorld::Ptr actor); + void startSpellBuying(const MWWorld::Ptr& actor); protected: MyGUI::Button* mCancelButton; @@ -45,7 +45,7 @@ namespace MWGui void updateScroller(); void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos); void onMouseWheel(MyGUI::Widget* _sender, int _rel); - void addSpell(std::string spellID); + void addSpell(const std::string& spellID); void clearSpells(); int mLastPos,mCurrentY; From bcf3a81a6ce98f4e3939c1bb074d407fdcfd7631 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Sep 2012 21:18:47 +0200 Subject: [PATCH 550/688] and a tiny bit more of cleanup --- apps/openmw/mwgui/spellbuyingwindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 4d89ee8c2..481dd5dd8 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -173,8 +173,7 @@ namespace MWGui for (unsigned int i=0;igetChildCount();i++) { - MyGUI::Widget* toMove; - toMove = mSpellsClientWidget->getChildAt(i); + MyGUI::Widget* toMove = mSpellsClientWidget->getChildAt(i); toMove->setCoord(toMove->getCoord() + MyGUI::IntPoint(0, diff)); } } From 432c08943429729b092cf3738135c5a1a1b7b469 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 9 Sep 2012 23:06:24 +0200 Subject: [PATCH 551/688] fix the spell tooltips to show the spell target --- apps/openmw/mwclass/potion.cpp | 1 + apps/openmw/mwgui/tooltips.cpp | 3 ++- apps/openmw/mwgui/tooltips.hpp | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 8fef163b1..0b79e11ca 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -137,6 +137,7 @@ namespace MWClass text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->base->effects); + info.isPotion = true; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 16ee27923..7aee26bcc 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -186,6 +186,7 @@ void ToolTips::onFrame(float frameDuration) params.mMagnMax = it->magnMax; params.mRange = it->range; params.mIsConstant = (spell->data.type == ESM::Spell::ST_Ability); + params.mNoTarget = false; effects.push_back(params); } info.effects = effects; @@ -420,7 +421,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) effectsWidget->setEffectList(info.effects); std::vector effectItems; - effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, Widgets::MWEffectList::EF_NoTarget); + effectsWidget->createEffectWidgets(effectItems, effectArea, coord, true, info.isPotion ? Widgets::MWEffectList::EF_NoTarget : 0); totalSize.height += coord.top-6; totalSize.width = std::max(totalSize.width, coord.width); } diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 700f5f723..f67b6ea5c 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -13,6 +13,10 @@ namespace MWGui struct ToolTipInfo { public: + ToolTipInfo() + : isPotion(false) + {} + std::string caption; std::string text; std::string icon; @@ -22,6 +26,8 @@ namespace MWGui // effects (for potions, ingredients) Widgets::SpellEffectList effects; + + bool isPotion; // potions do not show target in the tooltip }; class ToolTips : public OEngine::GUI::Layout From bb267dddf690d1e6ba5089849a62a6e460ef65cb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 9 Sep 2012 23:50:57 +0200 Subject: [PATCH 552/688] some stats window cleanup --- apps/openmw/mwgui/stats_window.cpp | 62 ++++++-------------------- apps/openmw/mwgui/stats_window.hpp | 6 +-- files/mygui/openmw_stats_window.layout | 3 +- 3 files changed, 16 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 32dac71de..3bd74ba8f 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -25,9 +25,7 @@ const int StatsWindow::sLineHeight = 18; StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) , mSkillAreaWidget(NULL) - , mSkillClientWidget(NULL) - , mSkillScrollerWidget(NULL) - , mLastPos(0) + , mSkillView(NULL) , mClientHeight(0) , mMajorSkills() , mMinorSkills() @@ -64,16 +62,10 @@ StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) } getWidget(mSkillAreaWidget, "Skills"); - getWidget(mSkillClientWidget, "SkillClient"); - getWidget(mSkillScrollerWidget, "SkillScroller"); + getWidget(mSkillView, "SkillView"); getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); - mSkillClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - - mSkillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &StatsWindow::onScrollChangePosition); - updateScroller(); - for (int i = 0; i < ESM::Skill::Length; ++i) { mSkillValues.insert(std::pair >(i, MWMechanics::Stat())); @@ -84,38 +76,18 @@ StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize); } -void StatsWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) -{ - int diff = mLastPos - pos; - // Adjust position of all widget according to difference - if (diff == 0) - return; - mLastPos = pos; - - std::vector::const_iterator end = mSkillWidgets.end(); - for (std::vector::const_iterator it = mSkillWidgets.begin(); it != end; ++it) - { - (*it)->setCoord((*it)->getCoord() + MyGUI::IntPoint(0, diff)); - } -} - void StatsWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 < 0) - mSkillScrollerWidget->setScrollPosition(0); - else if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 > mSkillScrollerWidget->getScrollRange()-1) - mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollRange()-1); + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); else - mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollPosition() - _rel*0.3); - - onScrollChangePosition(mSkillScrollerWidget, mSkillScrollerWidget->getScrollPosition()); + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); } void StatsWindow::onWindowResize(MyGUI::Window* window) { mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); - updateScroller(); } void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) @@ -299,7 +271,7 @@ void StatsWindow::setBirthSign (const std::string& signId) void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::ImageBox* separator = mSkillClientWidget->createWidget("MW_HLine", + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); separator->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); @@ -311,7 +283,7 @@ void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::TextBox* groupWidget = mSkillClientWidget->createWidget("SandBrightText", + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); groupWidget->setCaption(label); @@ -326,11 +298,11 @@ MyGUI::TextBox* StatsWindow::addValueItem(const std::string& text, const std::st { MyGUI::TextBox *skillNameWidget, *skillValueWidget; - skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); - skillValueWidget = mSkillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Right | MyGUI::Align::Top); skillValueWidget->setCaption(value); skillValueWidget->_setWidgetState(state); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); @@ -348,7 +320,7 @@ MyGUI::Widget* StatsWindow::addItem(const std::string& text, MyGUI::IntCoord &co { MyGUI::TextBox* skillNameWidget; - skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel); @@ -426,12 +398,11 @@ void StatsWindow::updateSkillArea() } mSkillWidgets.clear(); - mSkillScrollerWidget->setScrollPosition(0); - onScrollChangePosition(mSkillScrollerWidget, 0); + mSkillView->setViewOffset (MyGUI::IntPoint(0,0)); mClientHeight = 0; const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillClientWidget->getWidth() - (10 + valueSize), 18); + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); if (!mMajorSkills.empty()) @@ -555,15 +526,8 @@ void StatsWindow::updateSkillArea() } mClientHeight = coord1.top; - updateScroller(); -} -void StatsWindow::updateScroller() -{ - mSkillScrollerWidget->setScrollRange(std::max(mClientHeight - mSkillClientWidget->getHeight(), 0)); - mSkillScrollerWidget->setScrollPage(std::max(mSkillClientWidget->getHeight() - sLineHeight, 0)); - if (mClientHeight != 0) - mSkillScrollerWidget->setTrackSize( (mSkillAreaWidget->getHeight() / float(mClientHeight)) * mSkillScrollerWidget->getLineSize() ); + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); } void StatsWindow::onPinToggled() diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index a46ab7531..fea89ba2d 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -48,12 +48,10 @@ namespace MWGui void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); MyGUI::Widget* addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); - void updateScroller(); void setFactions (const FactionList& factions); void setBirthSign (const std::string &signId); - void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos); void onWindowResize(MyGUI::Window* window); void onMouseWheel(MyGUI::Widget* _sender, int _rel); @@ -62,8 +60,8 @@ namespace MWGui MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; - MyGUI::WidgetPtr mSkillAreaWidget, mSkillClientWidget; - MyGUI::ScrollBar* mSkillScrollerWidget; + MyGUI::WidgetPtr mSkillAreaWidget; + MyGUI::ScrollView* mSkillView; int mLastPos, mClientHeight; SkillList mMajorSkills, mMinorSkills, mMiscSkills; diff --git a/files/mygui/openmw_stats_window.layout b/files/mygui/openmw_stats_window.layout index 81d70f704..c2c8a5dae 100644 --- a/files/mygui/openmw_stats_window.layout +++ b/files/mygui/openmw_stats_window.layout @@ -218,8 +218,7 @@ - - + From 56061474c464255a3ed545a07e95c9d8a04b6a2b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 9 Sep 2012 23:53:23 +0200 Subject: [PATCH 553/688] another 3 unnecessary lines --- apps/openmw/mwgui/stats_window.cpp | 2 -- apps/openmw/mwgui/stats_window.hpp | 1 - 2 files changed, 3 deletions(-) diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 3bd74ba8f..e90b0539f 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -24,7 +24,6 @@ const int StatsWindow::sLineHeight = 18; StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) : WindowPinnableBase("openmw_stats_window.layout", parWindowManager) - , mSkillAreaWidget(NULL) , mSkillView(NULL) , mClientHeight(0) , mMajorSkills() @@ -61,7 +60,6 @@ StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) setText (names[i][0], store.gameSettings.find (names[i][1])->str); } - getWidget(mSkillAreaWidget, "Skills"); getWidget(mSkillView, "SkillView"); getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index fea89ba2d..73c4706dd 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -60,7 +60,6 @@ namespace MWGui MyGUI::Widget* mLeftPane; MyGUI::Widget* mRightPane; - MyGUI::WidgetPtr mSkillAreaWidget; MyGUI::ScrollView* mSkillView; int mLastPos, mClientHeight; From 35dd49f94854e9403a7afc3d050174030c18c513 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 09:10:50 +0200 Subject: [PATCH 554/688] fixed some "using namespace" in headers --- apps/openmw/mwgui/alchemywindow.cpp | 2 +- apps/openmw/mwgui/birth.hpp | 5 +--- apps/openmw/mwgui/class.hpp | 17 ++++++------- apps/openmw/mwgui/container.cpp | 6 ++--- apps/openmw/mwgui/dialogue.cpp | 8 +++--- apps/openmw/mwgui/dialogue_history.cpp | 22 ++++++++--------- apps/openmw/mwgui/dialogue_history.hpp | 9 +++---- apps/openmw/mwgui/inventorywindow.cpp | 4 +-- apps/openmw/mwgui/race.hpp | 4 +-- apps/openmw/mwgui/review.cpp | 2 +- apps/openmw/mwgui/review.hpp | 6 ++--- apps/openmw/mwgui/text_input.hpp | 2 -- apps/openmw/mwgui/widgets.hpp | 34 +++++++++++--------------- 13 files changed, 51 insertions(+), 70 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 5cf09b18a..804ccb2ba 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -493,7 +493,7 @@ namespace MWGui MyGUI::IntCoord coord(0, 0, mEffectsBox->getWidth(), 24); Widgets::MWEffectListPtr effectsWidget = mEffectsBox->createWidget - ("MW_StatName", coord, Align::Left | Align::Top); + ("MW_StatName", coord, MyGUI::Align::Left | MyGUI::Align::Top); effectsWidget->setWindowManager(&mWindowManager); effectsWidget->setEffectList(effects); diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index 5bd36e828..f16f92325 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -10,9 +10,6 @@ namespace MWGui { - /// \todo remove - using namespace MyGUI; - class BirthDialog : public WindowBase { public: @@ -31,7 +28,7 @@ namespace MWGui virtual void open(); // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 43511ccca..94e8558d0 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -12,9 +12,6 @@ namespace MWGui { - /// \todo remove! - using namespace MyGUI; - class InfoBoxDialog : public WindowBase { public: @@ -30,7 +27,7 @@ namespace MWGui int getChosenButton() const; // Events - typedef delegates::CMultiDelegate1 EventHandle_Int; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; /** Event : Button was clicked.\n signature : void method(int index)\n @@ -75,7 +72,7 @@ namespace MWGui void setClassId(const std::string &classId); // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n @@ -105,7 +102,7 @@ namespace MWGui virtual void open(); // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n @@ -141,7 +138,7 @@ namespace MWGui ESM::Class::Specialization getSpecializationId() const { return mSpecializationId; } // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Cancel button clicked.\n signature : void method()\n @@ -174,7 +171,7 @@ namespace MWGui void setAffectedWidget(Widgets::MWAttributePtr widget) { mAffectedWidget = widget; } // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Cancel button clicked.\n signature : void method()\n @@ -207,7 +204,7 @@ namespace MWGui void setAffectedWidget(Widgets::MWSkillPtr widget) { mAffectedWidget = widget; } // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Cancel button clicked.\n signature : void method()\n @@ -264,7 +261,7 @@ namespace MWGui void setNextButtonShow(bool shown); // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 75e2bb3b8..8454094c1 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -76,7 +76,7 @@ ContainerBase::ContainerBase(DragAndDrop* dragAndDrop) : { } -void ContainerBase::setWidgets(Widget* containerWidget, ScrollView* itemView) +void ContainerBase::setWidgets(MyGUI::Widget* containerWidget, MyGUI::ScrollView* itemView) { mContainerWidget = containerWidget; mItemView = itemView; @@ -471,7 +471,7 @@ void ContainerBase::drawItems() // background widget (for the "equipped" frame and magic item background image) bool isMagic = (MWWorld::Class::get(*iter).getEnchantment(*iter) != ""); - MyGUI::ImageBox* backgroundWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); + MyGUI::ImageBox* backgroundWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); backgroundWidget->setUserString("ToolTipType", "ItemPtr"); backgroundWidget->setUserData(*iter); @@ -503,7 +503,7 @@ void ContainerBase::drawItems() backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ContainerBase::onMouseWheel); // image - ImageBox* image = backgroundWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + MyGUI::ImageBox* image = backgroundWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); int pos = path.rfind("."); path.erase(pos); path.append(".dds"); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 4342b1130..00a786a2c 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -54,7 +54,7 @@ DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) getWidget(mHistory, "History"); mHistory->setOverflowToTheLeft(true); mHistory->setMaxTextLength(1000000); - Widget* eventbox; + MyGUI::Widget* eventbox; //An EditBox cannot receive mouse click events, so we use an //invisible widget on top of the editbox to receive them @@ -78,11 +78,11 @@ DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) { - ISubWidgetText* t = mHistory->getClient()->getSubWidgetText(); + MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText(); if(t == nullptr) return; - const IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); size_t cursorPosition = t->getCursorPosition(lastPressed); MyGUI::UString color = mHistory->getColorAtPos(cursorPosition); @@ -92,7 +92,7 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) if(color != "#B29154") { - UString key = mHistory->getColorTextAt(cursorPosition); + MyGUI::UString key = mHistory->getColorTextAt(cursorPosition); if(color == "#686EBA") MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); if(color == "#572D21") MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); diff --git a/apps/openmw/mwgui/dialogue_history.cpp b/apps/openmw/mwgui/dialogue_history.cpp index f72f199ea..4ca764c82 100644 --- a/apps/openmw/mwgui/dialogue_history.cpp +++ b/apps/openmw/mwgui/dialogue_history.cpp @@ -14,10 +14,10 @@ using namespace MWGui; using namespace Widgets; -UString DialogueHistory::getColorAtPos(size_t _pos) +MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos) { - UString colour = TextIterator::convertTagColour(getTextColour()); - TextIterator iterator(getCaption()); + MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); + MyGUI::TextIterator iterator(getCaption()); while(iterator.moveNext()) { size_t pos = iterator.getPosition(); @@ -30,13 +30,13 @@ UString DialogueHistory::getColorAtPos(size_t _pos) return colour; } -UString DialogueHistory::getColorTextAt(size_t _pos) +MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos) { bool breakOnNext = false; - UString colour = TextIterator::convertTagColour(getTextColour()); - UString colour2 = colour; - TextIterator iterator(getCaption()); - TextIterator col_start = iterator; + MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour()); + MyGUI::UString colour2 = colour; + MyGUI::TextIterator iterator(getCaption()); + MyGUI::TextIterator col_start = iterator; while(iterator.moveNext()) { size_t pos = iterator.getPosition(); @@ -60,15 +60,15 @@ UString DialogueHistory::getColorTextAt(size_t _pos) return ""; } -void DialogueHistory::addDialogHeading(const UString& parText) +void DialogueHistory::addDialogHeading(const MyGUI::UString& parText) { - UString head("\n#D8C09A"); + MyGUI::UString head("\n#D8C09A"); head.append(parText); head.append("#B29154\n"); addText(head); } -void DialogueHistory::addDialogText(const UString& parText) +void DialogueHistory::addDialogText(const MyGUI::UString& parText) { addText(parText); addText("\n"); diff --git a/apps/openmw/mwgui/dialogue_history.hpp b/apps/openmw/mwgui/dialogue_history.hpp index 12a9c53e7..c37504af7 100644 --- a/apps/openmw/mwgui/dialogue_history.hpp +++ b/apps/openmw/mwgui/dialogue_history.hpp @@ -4,16 +4,15 @@ namespace MWGui { - using namespace MyGUI; class DialogueHistory : public MyGUI::EditBox { MYGUI_RTTI_DERIVED( DialogueHistory ) public: Widget* getClient() { return mClient; } - UString getColorAtPos(size_t _pos); - UString getColorTextAt(size_t _pos); - void addDialogHeading(const UString& parText); - void addDialogText(const UString& parText); + MyGUI::UString getColorAtPos(size_t _pos); + MyGUI::UString getColorTextAt(size_t _pos); + void addDialogHeading(const MyGUI::UString& parText); + void addDialogText(const MyGUI::UString& parText); }; } #endif diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7b68f7b6d..21ed7f702 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -328,12 +328,12 @@ namespace MWGui std::string path = std::string("icons\\"); path += MWWorld::Class::get(newObject).getInventoryIcon(newObject); - MyGUI::ImageBox* baseWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default); + MyGUI::ImageBox* baseWidget = mContainerWidget->createWidget("ImageBox", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default); baseWidget->detachFromWidget(); baseWidget->attachToWidget(mDragAndDrop->mDragAndDropWidget); baseWidget->setUserData(newObject); mDragAndDrop->mDraggedWidget = baseWidget; - ImageBox* image = baseWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); + MyGUI::ImageBox* image = baseWidget->createWidget("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); int pos = path.rfind("."); path.erase(pos); path.append(".dds"); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 2e9e09a3e..4aded28c3 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -19,8 +19,6 @@ namespace MWGui namespace MWGui { - using namespace MyGUI; - class RaceDialog : public WindowBase { public: @@ -46,7 +44,7 @@ namespace MWGui virtual void open(); // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; /** Event : Back button clicked.\n signature : void method()\n diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 4a9cc778a..b1925178d 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -30,7 +30,7 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) center(); // Setup static stats - ButtonPtr button; + MyGUI::Button* button; getWidget(mNameWidget, "NameText"); getWidget(button, "NameButton"); adjustButtonSize(button); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index f6d373232..5685d194a 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -17,8 +17,6 @@ Layout is defined by resources/mygui/openmw_chargen_review.layout. namespace MWGui { - using namespace MyGUI; - class ReviewDialog : public WindowBase { public: @@ -49,8 +47,8 @@ namespace MWGui virtual void open(); // Events - typedef delegates::CMultiDelegate0 EventHandle_Void; - typedef delegates::CMultiDelegate1 EventHandle_Int; + typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; /** Event : Back button clicked.\n signature : void method()\n diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index 8fb0e5819..848310369 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -13,8 +13,6 @@ namespace MWGui namespace MWGui { - using namespace MyGUI; - class TextInputDialog : public WindowBase { public: diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 9a2762369..d085b54cb 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -7,9 +7,6 @@ #include "../mwmechanics/stat.hpp" -#undef MYGUI_EXPORT -#define MYGUI_EXPORT - namespace MWBase { class WindowManager; @@ -21,9 +18,6 @@ namespace MWBase namespace MWGui { - /// \todo remove! - using namespace MyGUI; - namespace Widgets { class MWEffectList; @@ -78,9 +72,9 @@ namespace MWGui typedef std::vector SpellEffectList; - class MYGUI_EXPORT MWSkill : public MyGUI::Widget + class MWSkill : public MyGUI::Widget { - MYGUI_RTTI_DERIVED( MWSkill ); + MYGUI_RTTI_DERIVED( MWSkill ) public: MWSkill(); @@ -96,7 +90,7 @@ namespace MWGui const SkillValue& getSkillValue() const { return mValue; } // Events - typedef delegates::CMultiDelegate1 EventHandle_SkillVoid; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_SkillVoid; /** Event : Skill clicked.\n signature : void method(MWSkill* _sender)\n @@ -121,9 +115,9 @@ namespace MWGui }; typedef MWSkill* MWSkillPtr; - class MYGUI_EXPORT MWAttribute : public MyGUI::Widget + class MWAttribute : public MyGUI::Widget { - MYGUI_RTTI_DERIVED( MWAttribute ); + MYGUI_RTTI_DERIVED( MWAttribute ) public: MWAttribute(); @@ -138,7 +132,7 @@ namespace MWGui const AttributeValue& getAttributeValue() const { return mValue; } // Events - typedef delegates::CMultiDelegate1 EventHandle_AttributeVoid; + typedef MyGUI::delegates::CMultiDelegate1 EventHandle_AttributeVoid; /** Event : Attribute clicked.\n signature : void method(MWAttribute* _sender)\n @@ -167,9 +161,9 @@ namespace MWGui * @todo remove this class and use MWEffectList instead */ class MWSpellEffect; - class MYGUI_EXPORT MWSpell : public MyGUI::Widget + class MWSpell : public MyGUI::Widget { - MYGUI_RTTI_DERIVED( MWSpell ); + MYGUI_RTTI_DERIVED( MWSpell ) public: MWSpell(); @@ -203,9 +197,9 @@ namespace MWGui }; typedef MWSpell* MWSpellPtr; - class MYGUI_EXPORT MWEffectList : public MyGUI::Widget + class MWEffectList : public MyGUI::Widget { - MYGUI_RTTI_DERIVED( MWEffectList ); + MYGUI_RTTI_DERIVED( MWEffectList ) public: MWEffectList(); @@ -244,9 +238,9 @@ namespace MWGui }; typedef MWEffectList* MWEffectListPtr; - class MYGUI_EXPORT MWSpellEffect : public MyGUI::Widget + class MWSpellEffect : public MyGUI::Widget { - MYGUI_RTTI_DERIVED( MWSpellEffect ); + MYGUI_RTTI_DERIVED( MWSpellEffect ) public: MWSpellEffect(); @@ -280,9 +274,9 @@ namespace MWGui }; typedef MWSpellEffect* MWSpellEffectPtr; - class MYGUI_EXPORT MWDynamicStat : public MyGUI::Widget + class MWDynamicStat : public MyGUI::Widget { - MYGUI_RTTI_DERIVED( MWDynamicStat ); + MYGUI_RTTI_DERIVED( MWDynamicStat ) public: MWDynamicStat(); From b66c8099ebbdd2f107db2a21fecfeb47d893d788 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 11:20:27 +0200 Subject: [PATCH 555/688] new system for dynamic sizing of buttons & text boxes --- apps/openmw/mwgui/inventorywindow.cpp | 20 --- apps/openmw/mwgui/widgets.cpp | 162 +++++++++++++++++++++ apps/openmw/mwgui/widgets.hpp | 79 ++++++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 4 + files/mygui/openmw_inventory_window.layout | 12 +- files/mygui/openmw_settings_window.layout | 16 +- 6 files changed, 261 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 21ed7f702..6dc19944f 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -66,26 +66,6 @@ namespace MWGui getWidget(itemView, "ItemView"); setWidgets(containerWidget, itemView); - // adjust size of buttons to fit text - int curX = 0; - mFilterAll->setSize( mFilterAll->getTextSize().width + 24, mFilterAll->getSize().height ); - curX += mFilterAll->getTextSize().width + 24 + 4; - - mFilterWeapon->setPosition(curX, mFilterWeapon->getPosition().top); - mFilterWeapon->setSize( mFilterWeapon->getTextSize().width + 24, mFilterWeapon->getSize().height ); - curX += mFilterWeapon->getTextSize().width + 24 + 4; - - mFilterApparel->setPosition(curX, mFilterApparel->getPosition().top); - mFilterApparel->setSize( mFilterApparel->getTextSize().width + 24, mFilterApparel->getSize().height ); - curX += mFilterApparel->getTextSize().width + 24 + 4; - - mFilterMagic->setPosition(curX, mFilterMagic->getPosition().top); - mFilterMagic->setSize( mFilterMagic->getTextSize().width + 24, mFilterMagic->getSize().height ); - curX += mFilterMagic->getTextSize().width + 24 + 4; - - mFilterMisc->setPosition(curX, mFilterMisc->getPosition().top); - mFilterMisc->setSize( mFilterMisc->getTextSize().width + 24, mFilterMisc->getSize().height ); - mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterWeapon->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); mFilterApparel->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onFilterChanged); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 40ee1c7ad..66488adf0 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -789,3 +789,165 @@ void MWDynamicStat::initialiseOverride() assignWidget(mBarWidget, "Bar"); assignWidget(mBarTextWidget, "BarText"); } + + + + +// --------------------------------------------------------------------------------------------------------------------- + + +void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) +{ + if (w->getParent () != 0) + { + Box* b = dynamic_cast(w->getParent()); + if (b) + b->notifyChildrenSizeChanged (); + } +} + + +MyGUI::IntSize AutoSizedTextBox::getRequestedSize() +{ + return getTextSize(); +} + +void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) +{ + TextBox::setCaption(_value); + + notifySizeChange (this); +} + + +MyGUI::IntSize AutoSizedButton::getRequestedSize() +{ + return getTextSize() + MyGUI::IntSize(24,0); +} + +void AutoSizedButton::setCaption(const MyGUI::UString& _value) +{ + Button::setCaption(_value); + + notifySizeChange (this); +} + + +Box::Box() + : mSpacing(4) +{ + +} + +void Box::setPropertyOverride(const std::string& _key, const std::string& _value) +{ + if (_key == "Spacing") + { + mSpacing = MyGUI::utility::parseValue(_value); + } +} + +void Box::notifyChildrenSizeChanged () +{ + align(); +} + + +void HBox::align () +{ + unsigned int count = getChildCount (); + size_t h_stretched_count = 0; + int total_width = 0; + std::vector< std::pair > sizes; + + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool hstretch = w->getUserString ("HStretch") == "true"; + h_stretched_count += hstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); + total_width += aw->getRequestedSize ().width; + if (i != count-1) + total_width += mSpacing; + } + else + sizes.push_back (std::make_pair(MyGUI::IntSize(0,0), hstretch)); + } + + int curX = 0; + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + MyGUI::IntCoord widgetCoord; + widgetCoord.left = curX; + widgetCoord.top = 0; /// \todo + int width = sizes[i].second ? sizes[i].first.width + (getSize().width - total_width)/h_stretched_count + : sizes[i].first.width; + widgetCoord.width = width; + widgetCoord.height = getSize().height; /// \todo + w->setCoord(widgetCoord); + curX += width; + + if (i != count-1) + curX += mSpacing; + } +} + +void HBox::setSize (const MyGUI::IntSize& _value) +{ + MyGUI::Widget::setSize (_value); + align(); +} + +MyGUI::IntSize HBox::getRequestedSize () +{ + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.height = std::max(size.height, requested.height); + size.width = size.width + requested.width; + if (i != getChildCount()-1) + size.width += mSpacing; + } + } + return size; +} + + + + +void VBox::align () +{ + +} + +void VBox::setSize (const MyGUI::IntSize& _value) +{ + MyGUI::Widget::setSize (_value); + align(); +} + +MyGUI::IntSize VBox::getRequestedSize () +{ + MyGUI::IntSize size(0,0); + for (unsigned int i = 0; i < getChildCount (); ++i) + { + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); + if (w) + { + MyGUI::IntSize requested = w->getRequestedSize (); + size.width = std::max(size.width, requested.width); + size.height = size.height + requested.height; + if (i != getChildCount()-1) + size.height += mSpacing; + } + } + return size; +} diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index d085b54cb..d7cc4afcf 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -299,6 +299,85 @@ namespace MWGui MyGUI::TextBox* mBarTextWidget; }; typedef MWDynamicStat* MWDynamicStatPtr; + + + + + + // --------------------------------------------------------------------------------------------------------------------- + + + + class AutoSizedWidget + { + public: + virtual MyGUI::IntSize getRequestedSize() = 0; + + protected: + void notifySizeChange(MyGUI::Widget* w); + }; + + class AutoSizedTextBox : public AutoSizedWidget, public MyGUI::TextBox + { + MYGUI_RTTI_DERIVED( AutoSizedTextBox ) + + public: + virtual MyGUI::IntSize getRequestedSize(); + virtual void setCaption(const MyGUI::UString& _value); + }; + + class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button + { + MYGUI_RTTI_DERIVED( AutoSizedButton ) + + public: + virtual MyGUI::IntSize getRequestedSize(); + virtual void setCaption(const MyGUI::UString& _value); + }; + + /** + * @brief A container widget that automatically sizes its children + * @note the box being an AutoSizedWidget as well allows to put boxes inside a box + */ + class Box : public AutoSizedWidget + { + public: + Box(); + + void notifyChildrenSizeChanged(); + + protected: + virtual void align() = 0; + + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + + + int mSpacing; // how much space to put between elements + }; + + class HBox : public Box, public MyGUI::Widget + { + MYGUI_RTTI_DERIVED( HBox ) + + public: + virtual void setSize (const MyGUI::IntSize &_value); + + protected: + virtual void align(); + virtual MyGUI::IntSize getRequestedSize(); + }; + + class VBox : public Box, public MyGUI::Widget + { + MYGUI_RTTI_DERIVED( VBox) + + public: + virtual void setSize (const MyGUI::IntSize &_value); + + protected: + virtual void align(); + virtual MyGUI::IntSize getRequestedSize(); + }; } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index faaa41783..82406fe34 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -102,6 +102,10 @@ WindowManager::WindowManager( MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index b38e15fc7..1ffabe3b6 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -30,20 +30,20 @@ - - + + - + - + - + - + diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index c8eae518f..077dbac2d 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -43,14 +43,18 @@ - - - + + + + + - - - + + + + + From 6c928e93d38dbe8073505fd9ec841bd6a8495842 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Sep 2012 13:04:00 +0200 Subject: [PATCH 556/688] added missing initialisation of NPC/creature spell lists from ESX records --- apps/openmw/mwclass/creature.cpp | 5 +++++ apps/openmw/mwclass/npc.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 7c6021652..d80a5c788 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -66,6 +66,11 @@ namespace MWClass data->mCreatureStats.setFlee(ref->base->mAiData.mFlee); data->mCreatureStats.setAlarm(ref->base->mAiData.mAlarm); + // spells + for (std::vector::const_iterator iter (ref->base->mSpells.list.begin()); + iter!=ref->base->mSpells.list.end(); ++iter) + data->mCreatureStats.getSpells().add (*iter); + // store ptr.getRefData().setCustomData (data.release()); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 909c681a5..8aab9da56 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -105,6 +105,11 @@ namespace MWClass data->mCreatureStats.setFlee(ref->base->mAiData.mFlee); data->mCreatureStats.setAlarm(ref->base->mAiData.mAlarm); + // spells + for (std::vector::const_iterator iter (ref->base->spells.list.begin()); + iter!=ref->base->spells.list.end(); ++iter) + data->mCreatureStats.getSpells().add (*iter); + // store ptr.getRefData().setCustomData (data.release()); } From f17756712a9c1c5b8da37052e1daa7d122933f11 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Sep 2012 13:15:55 +0200 Subject: [PATCH 557/688] fixed spell list handling --- apps/openmw/mwgui/spellbuyingwindow.cpp | 29 +++++++++++-------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 481dd5dd8..48e181545 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -91,24 +91,21 @@ namespace MWGui clearSpells(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); - MWMechanics::Spells& playerSpells = stats.getSpells(); - /// \todo get spell list via MWWorld::Class interface - std::vector spellList = actor.get()->base->spells.list; - for (std::vector::const_iterator it = spellList.begin(); it != spellList.end(); ++it) + + MWMechanics::Spells& playerSpells = MWWorld::Class::get (player).getCreatureStats (player).getSpells(); + MWMechanics::Spells& merchantSpells = MWWorld::Class::get (actor).getCreatureStats (actor).getSpells(); + + for (MWMechanics::Spells::TIterator iter = merchantSpells.begin(); iter!=merchantSpells.end(); ++iter) { - bool alreadyHave = false; - for (std::vector::const_iterator it2 = playerSpells.begin(); it2 != playerSpells.end(); ++it2) - { - if (*it==*it2) - { - alreadyHave = true; - break; - } - } + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find (*iter); - if (alreadyHave==false) - addSpell(*it); + if (spell->data.type!=ESM::Spell::ST_Spell) + continue; // don't try to sell diseases, curses or powers + + if (std::find (playerSpells.begin(), playerSpells.end(), *iter)!=playerSpells.end()) + continue; // we have that spell already + + addSpell (*iter); } updateLabels(); From 61c8a627aaed9adf6f0a026192ce8b834ee1e16b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Sep 2012 13:19:15 +0200 Subject: [PATCH 558/688] added a missing include --- apps/openmw/mwgui/spellbuyingwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 48e181545..59d811335 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -1,5 +1,7 @@ #include "spellbuyingwindow.hpp" +#include + #include #include "../mwbase/environment.hpp" From bc6a1979b0fdafdc32ae3958b13b3fcfc483889f Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 13:39:22 +0200 Subject: [PATCH 559/688] replaced everything with the new method --- apps/openmw/mwgui/alchemywindow.cpp | 8 - apps/openmw/mwgui/birth.cpp | 9 - apps/openmw/mwgui/class.cpp | 38 ---- apps/openmw/mwgui/confirmationdialog.cpp | 12 -- apps/openmw/mwgui/container.cpp | 6 - apps/openmw/mwgui/countdialog.cpp | 12 -- apps/openmw/mwgui/itemselection.cpp | 6 - apps/openmw/mwgui/map_window.cpp | 4 - apps/openmw/mwgui/quickkeysmenu.cpp | 14 +- apps/openmw/mwgui/race.cpp | 9 - apps/openmw/mwgui/review.cpp | 67 ++----- apps/openmw/mwgui/review.hpp | 7 +- apps/openmw/mwgui/settingswindow.cpp | 5 - apps/openmw/mwgui/text_input.cpp | 4 - apps/openmw/mwgui/tradewindow.cpp | 34 ---- apps/openmw/mwgui/widgets.cpp | 52 +++++- apps/openmw/mwgui/widgets.hpp | 8 + files/mygui/openmw_alchemy_window.layout | 8 +- files/mygui/openmw_chargen_birth.layout | 13 +- files/mygui/openmw_chargen_class.layout | 13 +- .../openmw_chargen_class_description.layout | 3 +- .../mygui/openmw_chargen_create_class.layout | 19 +- ...penmw_chargen_generate_class_result.layout | 13 +- files/mygui/openmw_chargen_race.layout | 13 +- files/mygui/openmw_chargen_review.layout | 16 +- .../openmw_chargen_select_attribute.layout | 3 +- .../mygui/openmw_chargen_select_skill.layout | 3 +- ...penmw_chargen_select_specialization.layout | 3 +- files/mygui/openmw_confirmation_dialog.layout | 13 +- files/mygui/openmw_container_window.layout | 14 +- files/mygui/openmw_count_window.layout | 14 +- .../mygui/openmw_itemselection_dialog.layout | 3 +- .../mygui/openmw_magicselection_dialog.layout | 3 +- files/mygui/openmw_map_window.layout | 4 +- files/mygui/openmw_quickkeys_menu.layout | 3 +- files/mygui/openmw_settings_window.layout | 164 +++++++++++------- files/mygui/openmw_text_input.layout | 3 +- files/mygui/openmw_trade_window.layout | 28 +-- 38 files changed, 284 insertions(+), 367 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 804ccb2ba..308029810 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -48,14 +48,6 @@ namespace MWGui mIngredient3->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); mIngredient4->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onIngredientSelected); - MyGUI::Widget* buttonBox = mCancelButton->getParent(); - int cancelButtonWidth = mCancelButton->getTextSize().width + 24; - mCancelButton->setCoord(buttonBox->getWidth() - cancelButtonWidth, - mCancelButton->getTop(), cancelButtonWidth, mCancelButton->getHeight()); - int createButtonWidth = mCreateButton->getTextSize().width + 24; - mCreateButton->setCoord(buttonBox->getWidth() - createButtonWidth - cancelButtonWidth - 4, - mCreateButton->getTop(), createButtonWidth, mCreateButton->getHeight()); - mCreateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCreateButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked); diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index 1a5c2d0f6..043fcd62c 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -45,9 +45,6 @@ BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) void BirthDialog::setNextButtonShow(bool shown) { - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); @@ -55,12 +52,6 @@ void BirthDialog::setNextButtonShow(bool shown) okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); else okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - - int okButtonWidth = okButton->getTextSize().width + 24; - int backButtonWidth = backButton->getTextSize().width + 24; - - okButton->setCoord(473 - okButtonWidth, 340, okButtonWidth, 23); - backButton->setCoord(473 - okButtonWidth - backButtonWidth - 6, 340, backButtonWidth, 23); } void BirthDialog::open() diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 9ea7b8052..309aa5a5d 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -39,11 +39,6 @@ GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parW getWidget(okButton, "OKButton"); okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked); - - int okButtonWidth = okButton->getTextSize().width + 24; - int backButtonWidth = backButton->getTextSize().width + 24; - okButton->setCoord(315 - okButtonWidth, 219, okButtonWidth, 23); - backButton->setCoord(315 - okButtonWidth - backButtonWidth - 6, 219, backButtonWidth, 23); } std::string GenerateClassResultDialog::getClassId() const @@ -116,9 +111,6 @@ PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) void PickClassDialog::setNextButtonShow(bool shown) { - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); @@ -126,12 +118,6 @@ void PickClassDialog::setNextButtonShow(bool shown) okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); else okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - - int okButtonWidth = okButton->getTextSize().width + 24; - int backButtonWidth = backButton->getTextSize().width + 24; - - okButton->setCoord(476 - okButtonWidth, 265, okButtonWidth, 23); - backButton->setCoord(476 - okButtonWidth - backButtonWidth - 6, 265, backButtonWidth, 23); } void PickClassDialog::open() @@ -422,7 +408,6 @@ CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) MyGUI::ButtonPtr descriptionButton; getWidget(descriptionButton, "DescriptionButton"); - descriptionButton->setCaption(mWindowManager.getGameSettingString("sCreateClassMenu1", "")); descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked); MyGUI::ButtonPtr backButton; @@ -519,27 +504,13 @@ std::vector CreateClassDialog::getMinorSkills() const void CreateClassDialog::setNextButtonShow(bool shown) { - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); - MyGUI::ButtonPtr descriptionButton; - getWidget(descriptionButton, "DescriptionButton"); - if (shown) okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); else okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - - int okButtonWidth = okButton->getTextSize().width + 24; - int backButtonWidth = backButton->getTextSize().width + 24; - int descriptionButtonWidth = descriptionButton->getTextSize().width + 24; - - okButton->setCoord(459 - okButtonWidth, 158, okButtonWidth, 23); - backButton->setCoord(459 - okButtonWidth - backButtonWidth - 6, 158, backButtonWidth, 23); - descriptionButton->setCoord(459 - okButtonWidth - backButtonWidth - descriptionButtonWidth - 12, 158, descriptionButtonWidth, 23); } // widget controls @@ -713,8 +684,6 @@ SelectSpecializationDialog::SelectSpecializationDialog(MWBase::WindowManager& pa getWidget(cancelButton, "CancelButton"); cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked); - int buttonWidth = cancelButton->getTextSize().width + 24; - cancelButton->setCoord(216 - buttonWidth, 90, buttonWidth, 21); } SelectSpecializationDialog::~SelectSpecializationDialog() @@ -768,8 +737,6 @@ SelectAttributeDialog::SelectAttributeDialog(MWBase::WindowManager& parWindowMan getWidget(cancelButton, "CancelButton"); cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked); - int buttonWidth = cancelButton->getTextSize().width + 24; - cancelButton->setCoord(186 - buttonWidth, 180, buttonWidth, 21); } SelectAttributeDialog::~SelectAttributeDialog() @@ -863,9 +830,6 @@ SelectSkillDialog::SelectSkillDialog(MWBase::WindowManager& parWindowManager) getWidget(cancelButton, "CancelButton"); cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", "")); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked); - int buttonWidth = cancelButton->getTextSize().width + 24; - cancelButton->setCoord(447 - buttonWidth, 218, buttonWidth, 21); - } SelectSkillDialog::~SelectSkillDialog() @@ -899,8 +863,6 @@ DescriptionDialog::DescriptionDialog(MWBase::WindowManager& parWindowManager) getWidget(okButton, "OKButton"); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked); okButton->setCaption(mWindowManager.getGameSettingString("sInputMenu1", "")); - int buttonWidth = okButton->getTextSize().width + 24; - okButton->setCoord(234 - buttonWidth, 214, buttonWidth, 24); // Make sure the edit box has focus MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); diff --git a/apps/openmw/mwgui/confirmationdialog.cpp b/apps/openmw/mwgui/confirmationdialog.cpp index d22181d60..31f4989e0 100644 --- a/apps/openmw/mwgui/confirmationdialog.cpp +++ b/apps/openmw/mwgui/confirmationdialog.cpp @@ -31,18 +31,6 @@ namespace MWGui mMessage->setSize(mMessage->getWidth(), mMessage->getTextSize().height+24); center(); - - int okButtonWidth = mOkButton->getTextSize().width + 24; - mOkButton->setCoord(mMainWidget->getWidth() - 30 - okButtonWidth, - mOkButton->getTop(), - okButtonWidth, - mOkButton->getHeight()); - - int cancelButtonWidth = mCancelButton->getTextSize().width + 24; - mCancelButton->setCoord(mMainWidget->getWidth() - 30 - okButtonWidth - cancelButtonWidth - 8, - mCancelButton->getTop(), - cancelButtonWidth, - mCancelButton->getHeight()); } void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 8454094c1..93ec71343 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -607,12 +607,6 @@ ContainerWindow::ContainerWindow(MWBase::WindowManager& parWindowManager,DragAnd mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onCloseButtonClicked); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ContainerWindow::onTakeAllButtonClicked); - // adjust buttons size to fit text - int closeButtonWidth = mCloseButton->getTextSize().width+24; - int takeButtonWidth = mTakeButton->getTextSize().width+24; - mCloseButton->setCoord(600-20-closeButtonWidth, mCloseButton->getCoord().top, closeButtonWidth, mCloseButton->getCoord().height); - mTakeButton->setCoord(600-20-closeButtonWidth-takeButtonWidth-8, mTakeButton->getCoord().top, takeButtonWidth, mTakeButton->getCoord().height); - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ContainerWindow::onWindowResize); setCoord(200,0,600,300); diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index 0ed9cf8ca..baf14e3fb 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -44,18 +44,6 @@ namespace MWGui mSlider->setScrollPosition(maxCount-1); mItemEdit->setCaption(boost::lexical_cast(maxCount)); - - int okButtonWidth = mOkButton->getTextSize().width + 24; - mOkButton->setCoord(width - 30 - okButtonWidth, - mOkButton->getTop(), - okButtonWidth, - mOkButton->getHeight()); - - int cancelButtonWidth = mCancelButton->getTextSize().width + 24; - mCancelButton->setCoord(width - 30 - okButtonWidth - cancelButtonWidth - 8, - mCancelButton->getTop(), - cancelButtonWidth, - mCancelButton->getHeight()); } void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/itemselection.cpp b/apps/openmw/mwgui/itemselection.cpp index d93a621ac..14b1cf8ee 100644 --- a/apps/openmw/mwgui/itemselection.cpp +++ b/apps/openmw/mwgui/itemselection.cpp @@ -23,12 +23,6 @@ namespace MWGui getWidget(cancelButton, "CancelButton"); cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemSelectionDialog::onCancelButtonClicked); - int dx = (cancelButton->getTextSize().width + 24) - cancelButton->getWidth(); - cancelButton->setCoord(cancelButton->getLeft() - dx, - cancelButton->getTop(), - cancelButton->getTextSize ().width + 24, - cancelButton->getHeight()); - center(); } diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 0d6300baa..f92e4ee4f 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -259,8 +259,6 @@ MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : getWidget(mButton, "WorldButton"); mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); mButton->setCaptionWithReplacing("#{sWorld}"); - int width = mButton->getTextSize().width + 24; - mButton->setCoord(mMainWidget->getSize().width - width - 22, mMainWidget->getSize().height - 64, width, 22); MyGUI::Button* eventbox; getWidget(eventbox, "EventBox"); @@ -303,8 +301,6 @@ void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : "#{sWorld}"); - int width = mButton->getTextSize().width + 24; - mButton->setCoord(mMainWidget->getSize().width - width - 22, mMainWidget->getSize().height - 64, width, 22); } void MapWindow::onPinToggled() diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 5b7115dbe..c46445465 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -52,13 +52,8 @@ namespace MWGui mMainWidget->setSize(mMainWidget->getWidth(), mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight())); - int okButtonWidth = mOkButton->getTextSize ().width + 24; - mOkButton->setCoord(mOkButton->getLeft() - (okButtonWidth - mOkButton->getWidth()), - mOkButton->getTop(), - okButtonWidth, - mOkButton->getHeight()); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked); - + std::cout << "real ok button is : " << mOkButton->getName() << std::endl; center(); @@ -72,7 +67,6 @@ namespace MWGui unassign(button, i); mQuickKeyButtons.push_back(button); - } } @@ -407,12 +401,6 @@ namespace MWGui getWidget(mMagicList, "MagicList"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MagicSelectionDialog::onCancelButtonClicked); - int dx = (mCancelButton->getTextSize().width + 24) - mCancelButton->getWidth(); - mCancelButton->setCoord(mCancelButton->getLeft() - dx, - mCancelButton->getTop(), - mCancelButton->getTextSize ().width + 24, - mCancelButton->getHeight()); - center(); } diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index a597dcadb..370b30733 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -90,9 +90,6 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) void RaceDialog::setNextButtonShow(bool shown) { - MyGUI::ButtonPtr backButton; - getWidget(backButton, "BackButton"); - MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); @@ -100,12 +97,6 @@ void RaceDialog::setNextButtonShow(bool shown) okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); else okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - - int okButtonWidth = okButton->getTextSize().width + 24; - int backButtonWidth = backButton->getTextSize().width + 24; - - okButton->setCoord(574 - okButtonWidth, 397, okButtonWidth, 23); - backButton->setCoord(574 - okButtonWidth - backButtonWidth - 6, 397, backButtonWidth, 23); } void RaceDialog::open() diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index b1925178d..76b211b18 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -77,12 +77,8 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) } // Setup skills - getWidget(mSkillAreaWidget, "Skills"); - getWidget(mSkillClientWidget, "SkillClient"); - getWidget(mSkillScrollerWidget, "SkillScroller"); - mSkillClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - mSkillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &ReviewDialog::onScrollChangePosition); - updateScroller(); + getWidget(mSkillView, "SkillView"); + mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); for (int i = 0; i < ESM::Skill::Length; ++i) { @@ -90,21 +86,13 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) mSkillWidgetMap.insert(std::make_pair(i, static_cast (0))); } - static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ReviewDialog::onWindowResize); - MyGUI::ButtonPtr backButton; getWidget(backButton, "BackButton"); backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked); MyGUI::ButtonPtr okButton; getWidget(okButton, "OKButton"); - okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked); - - int backButtonWidth = backButton->getTextSize().width + 24; - int okButtonWidth = okButton->getTextSize().width + 24; - okButton->setCoord(502 - okButtonWidth, 372, okButtonWidth, 23); - backButton->setCoord(502 - okButtonWidth - backButtonWidth - 6, 372, backButtonWidth, 23); } void ReviewDialog::open() @@ -112,26 +100,6 @@ void ReviewDialog::open() updateSkillArea(); } -void ReviewDialog::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) -{ - int diff = mLastPos - pos; - // Adjust position of all widget according to difference - if (diff == 0) - return; - mLastPos = pos; - - std::vector::const_iterator end = mSkillWidgets.end(); - for (std::vector::const_iterator it = mSkillWidgets.begin(); it != end; ++it) - { - (*it)->setCoord((*it)->getCoord() + MyGUI::IntPoint(0, diff)); - } -} - -void ReviewDialog::onWindowResize(MyGUI::Window* window) -{ - updateScroller(); -} - void ReviewDialog::setPlayerName(const std::string &name) { mNameWidget->setCaption(name); @@ -239,7 +207,7 @@ void ReviewDialog::configureSkills(const std::vector& major, const std::vec void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::ImageBox* separator = mSkillClientWidget->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); + MyGUI::ImageBox* separator = mSkillView->createWidget("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default); separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); mSkillWidgets.push_back(separator); @@ -250,7 +218,7 @@ void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2 void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2) { - MyGUI::TextBox* groupWidget = mSkillClientWidget->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); + MyGUI::TextBox* groupWidget = mSkillView->createWidget("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default); groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); groupWidget->setCaption(label); mSkillWidgets.push_back(groupWidget); @@ -264,11 +232,11 @@ MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::s MyGUI::TextBox* skillNameWidget; MyGUI::TextBox* skillValueWidget; - skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1, MyGUI::Align::Default); + skillNameWidget = mSkillView->createWidget("SandText", coord1, MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - skillValueWidget = mSkillClientWidget->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); + skillValueWidget = mSkillView->createWidget("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right); skillValueWidget->setCaption(value); skillValueWidget->_setWidgetState(state); skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); @@ -286,7 +254,7 @@ void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyG { MyGUI::TextBox* skillNameWidget; - skillNameWidget = mSkillClientWidget->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); + skillNameWidget = mSkillView->createWidget("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default); skillNameWidget->setCaption(text); skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); @@ -343,7 +311,7 @@ void ReviewDialog::updateSkillArea() mSkillWidgets.clear(); const int valueSize = 40; - MyGUI::IntCoord coord1(10, 0, mSkillClientWidget->getWidth() - (10 + valueSize), 18); + MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18); MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height); if (!mMajorSkills.empty()) @@ -356,15 +324,8 @@ void ReviewDialog::updateSkillArea() addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); mClientHeight = coord1.top; - updateScroller(); -} -void ReviewDialog::updateScroller() -{ - mSkillScrollerWidget->setScrollRange(std::max(mClientHeight - mSkillClientWidget->getHeight(), 0)); - mSkillScrollerWidget->setScrollPage(std::max(mSkillClientWidget->getHeight() - sLineHeight, 0)); - if (mClientHeight != 0) - mSkillScrollerWidget->setTrackSize( (mSkillAreaWidget->getHeight() / float(mClientHeight)) * mSkillScrollerWidget->getLineSize() ); + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); } // widget controls @@ -401,12 +362,8 @@ void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender) void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 < 0) - mSkillScrollerWidget->setScrollPosition(0); - else if (mSkillScrollerWidget->getScrollPosition() - _rel*0.3 > mSkillScrollerWidget->getScrollRange()-1) - mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollRange()-1); + if (mSkillView->getViewOffset().top + _rel*0.3 > 0) + mSkillView->setViewOffset(MyGUI::IntPoint(0, 0)); else - mSkillScrollerWidget->setScrollPosition(mSkillScrollerWidget->getScrollPosition() - _rel*0.3); - - onScrollChangePosition(mSkillScrollerWidget, mSkillScrollerWidget->getScrollPosition()); + mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3)); } diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 5685d194a..058e3cff3 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -74,17 +74,12 @@ namespace MWGui void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); MyGUI::TextBox* addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); - void updateScroller(); void updateSkillArea(); - void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos); - void onWindowResize(MyGUI::Window* window); - static const int sLineHeight; MyGUI::TextBox *mNameWidget, *mRaceWidget, *mClassWidget, *mBirthSignWidget; - MyGUI::WidgetPtr mSkillAreaWidget, mSkillClientWidget; - MyGUI::ScrollBar* mSkillScrollerWidget; + MyGUI::ScrollView* mSkillView; int mLastPos, mClientHeight; Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 2599ea0b2..d1e206a95 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -160,11 +160,6 @@ namespace MWGui center(); - int okSize = mOkButton->getTextSize().width + 24; - mOkButton->setCoord(mMainWidget->getWidth()-16-okSize, mOkButton->getTop(), - okSize, mOkButton->getHeight()); - - mResetControlsButton->setSize (mResetControlsButton->getTextSize ().width + 24, mResetControlsButton->getHeight()); mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); // fill resolution list diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index b724bab15..9d5ba9896 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -30,10 +30,6 @@ void TextInputDialog::setNextButtonShow(bool shown) okButton->setCaption(mWindowManager.getGameSettingString("sNext", "")); else okButton->setCaption(mWindowManager.getGameSettingString("sOK", "")); - - int okButtonWidth = okButton->getTextSize().width + 24; - - okButton->setCoord(306 - okButtonWidth, 60, okButtonWidth, 23); } void TextInputDialog::setTextLabel(const std::string &label) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 9abd97bb7..b980b834e 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -42,26 +42,6 @@ namespace MWGui getWidget(mTotalBalanceLabel, "TotalBalanceLabel"); getWidget(mBottomPane, "BottomPane"); - // adjust size of buttons to fit text - int curX = 0; - mFilterAll->setSize( mFilterAll->getTextSize().width + 24, mFilterAll->getSize().height ); - curX += mFilterAll->getTextSize().width + 24 + 4; - - mFilterWeapon->setPosition(curX, mFilterWeapon->getPosition().top); - mFilterWeapon->setSize( mFilterWeapon->getTextSize().width + 24, mFilterWeapon->getSize().height ); - curX += mFilterWeapon->getTextSize().width + 24 + 4; - - mFilterApparel->setPosition(curX, mFilterApparel->getPosition().top); - mFilterApparel->setSize( mFilterApparel->getTextSize().width + 24, mFilterApparel->getSize().height ); - curX += mFilterApparel->getTextSize().width + 24 + 4; - - mFilterMagic->setPosition(curX, mFilterMagic->getPosition().top); - mFilterMagic->setSize( mFilterMagic->getTextSize().width + 24, mFilterMagic->getSize().height ); - curX += mFilterMagic->getTextSize().width + 24 + 4; - - mFilterMisc->setPosition(curX, mFilterMisc->getPosition().top); - mFilterMisc->setSize( mFilterMisc->getTextSize().width + 24, mFilterMisc->getSize().height ); - mFilterAll->setStateSelected(true); mFilterAll->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onFilterChanged); @@ -73,20 +53,6 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onCancelButtonClicked); mOfferButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TradeWindow::onOfferButtonClicked); - mMaxSaleButton->setSize(MyGUI::IntSize(mMaxSaleButton->getTextSize().width + 24, mMaxSaleButton->getHeight())); - - int cancelButtonWidth = mCancelButton->getTextSize().width + 24; - mCancelButton->setCoord(mBottomPane->getWidth()-cancelButtonWidth, - mCancelButton->getTop(), - cancelButtonWidth, - mCancelButton->getHeight()); - - int offerButtonWidth = mOfferButton->getTextSize().width + 24; - mOfferButton->setCoord(mBottomPane->getWidth()-cancelButtonWidth-offerButtonWidth-8, - mOfferButton->getTop(), - offerButtonWidth, - mOfferButton->getHeight()); - setCoord(400, 0, 400, 300); static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &TradeWindow::onWindowResize); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 66488adf0..97a5b2eb5 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -803,6 +803,15 @@ void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) Box* b = dynamic_cast(w->getParent()); if (b) b->notifyChildrenSizeChanged (); + else + { + if (mExpandDirection == MyGUI::Align::Left) + { + int hdiff = getRequestedSize ().width - w->getSize().width; + w->setPosition(w->getPosition() - MyGUI::IntPoint(hdiff, 0)); + } + w->setSize(getRequestedSize ()); + } } } @@ -819,10 +828,24 @@ void AutoSizedTextBox::setCaption(const MyGUI::UString& _value) notifySizeChange (this); } +void AutoSizedTextBox::setPropertyOverride(const std::string& _key, const std::string& _value) +{ + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + TextBox::setPropertyOverride (_key, _value); + } +} + MyGUI::IntSize AutoSizedButton::getRequestedSize() { - return getTextSize() + MyGUI::IntSize(24,0); + MyGUI::IntSize size = getTextSize() + MyGUI::IntSize(24,0); + size.height = std::max(24, size.height); + return size; } void AutoSizedButton::setCaption(const MyGUI::UString& _value) @@ -832,6 +855,17 @@ void AutoSizedButton::setCaption(const MyGUI::UString& _value) notifySizeChange (this); } +void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::string& _value) +{ + if (_key == "ExpandDirection") + { + mExpandDirection = MyGUI::Align::parse (_value); + } + else + { + Button::setPropertyOverride (_key, _value); + } +} Box::Box() : mSpacing(4) @@ -870,11 +904,15 @@ void HBox::align () { sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); total_width += aw->getRequestedSize ().width; - if (i != count-1) - total_width += mSpacing; } else - sizes.push_back (std::make_pair(MyGUI::IntSize(0,0), hstretch)); + { + if (!hstretch) h_stretched_count ++; + sizes.push_back (std::make_pair(MyGUI::IntSize(0,0), true)); + } + + if (i != count-1) + total_width += mSpacing; } int curX = 0; @@ -883,11 +921,11 @@ void HBox::align () MyGUI::Widget* w = getChildAt(i); MyGUI::IntCoord widgetCoord; widgetCoord.left = curX; - widgetCoord.top = 0; /// \todo + widgetCoord.top = (getSize().height - sizes[i].first.height) / 2; int width = sizes[i].second ? sizes[i].first.width + (getSize().width - total_width)/h_stretched_count : sizes[i].first.width; widgetCoord.width = width; - widgetCoord.height = getSize().height; /// \todo + widgetCoord.height = sizes[i].first.height; w->setCoord(widgetCoord); curX += width; @@ -925,7 +963,7 @@ MyGUI::IntSize HBox::getRequestedSize () void VBox::align () { - + // not yet implemented } void VBox::setSize (const MyGUI::IntSize& _value) diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index d7cc4afcf..16c2adec9 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -315,6 +315,8 @@ namespace MWGui protected: void notifySizeChange(MyGUI::Widget* w); + + MyGUI::Align mExpandDirection; }; class AutoSizedTextBox : public AutoSizedWidget, public MyGUI::TextBox @@ -324,6 +326,9 @@ namespace MWGui public: virtual MyGUI::IntSize getRequestedSize(); virtual void setCaption(const MyGUI::UString& _value); + + protected: + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; class AutoSizedButton : public AutoSizedWidget, public MyGUI::Button @@ -333,6 +338,9 @@ namespace MWGui public: virtual MyGUI::IntSize getRequestedSize(); virtual void setCaption(const MyGUI::UString& _value); + + protected: + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); }; /** diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index 68df748b1..458d24b2c 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -76,11 +76,13 @@ - - + + + + - + diff --git a/files/mygui/openmw_chargen_birth.layout b/files/mygui/openmw_chargen_birth.layout index 0599ab863..5edec72d3 100644 --- a/files/mygui/openmw_chargen_birth.layout +++ b/files/mygui/openmw_chargen_birth.layout @@ -14,11 +14,14 @@ - - - - - + + + + + + + + diff --git a/files/mygui/openmw_chargen_class.layout b/files/mygui/openmw_chargen_class.layout index 613143d54..baa36b24a 100644 --- a/files/mygui/openmw_chargen_class.layout +++ b/files/mygui/openmw_chargen_class.layout @@ -60,11 +60,14 @@ - - - - - + + + + + + + + diff --git a/files/mygui/openmw_chargen_class_description.layout b/files/mygui/openmw_chargen_class_description.layout index 2ffc7d707..11031eb4e 100644 --- a/files/mygui/openmw_chargen_class_description.layout +++ b/files/mygui/openmw_chargen_class_description.layout @@ -11,7 +11,8 @@ - + + diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout index dde74a6a2..5e08db6e5 100644 --- a/files/mygui/openmw_chargen_create_class.layout +++ b/files/mygui/openmw_chargen_create_class.layout @@ -58,14 +58,17 @@ - - - - - - - - + + + + + + + + + + + diff --git a/files/mygui/openmw_chargen_generate_class_result.layout b/files/mygui/openmw_chargen_generate_class_result.layout index 1cef9c9ce..65dbf016c 100644 --- a/files/mygui/openmw_chargen_generate_class_result.layout +++ b/files/mygui/openmw_chargen_generate_class_result.layout @@ -20,11 +20,14 @@ - - - - - + + + + + + + + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index df0d29f78..3d32ffd9a 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -54,11 +54,14 @@ - - - - - + + + + + + + + diff --git a/files/mygui/openmw_chargen_review.layout b/files/mygui/openmw_chargen_review.layout index 2071cac88..dbe7a4780 100644 --- a/files/mygui/openmw_chargen_review.layout +++ b/files/mygui/openmw_chargen_review.layout @@ -107,16 +107,18 @@ - - + - - - - - + + + + + + + + diff --git a/files/mygui/openmw_chargen_select_attribute.layout b/files/mygui/openmw_chargen_select_attribute.layout index e1ab4d888..09c2a4aae 100644 --- a/files/mygui/openmw_chargen_select_attribute.layout +++ b/files/mygui/openmw_chargen_select_attribute.layout @@ -20,7 +20,8 @@ - + + diff --git a/files/mygui/openmw_chargen_select_skill.layout b/files/mygui/openmw_chargen_select_skill.layout index 2c7dae80c..2cf81e6c6 100644 --- a/files/mygui/openmw_chargen_select_skill.layout +++ b/files/mygui/openmw_chargen_select_skill.layout @@ -55,7 +55,8 @@ - + + diff --git a/files/mygui/openmw_chargen_select_specialization.layout b/files/mygui/openmw_chargen_select_specialization.layout index 1b5295bc9..e9cb76825 100644 --- a/files/mygui/openmw_chargen_select_specialization.layout +++ b/files/mygui/openmw_chargen_select_specialization.layout @@ -22,7 +22,8 @@ - + + diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index 7b8bd2a1f..ba98d3220 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -13,11 +13,14 @@ - - - - - + + + + + + + + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index ae9b0bfdf..896566fdd 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -11,12 +11,14 @@ - - - - - - + + + + + + + + diff --git a/files/mygui/openmw_count_window.layout b/files/mygui/openmw_count_window.layout index ae6635dff..59b0a9c30 100644 --- a/files/mygui/openmw_count_window.layout +++ b/files/mygui/openmw_count_window.layout @@ -20,11 +20,15 @@ - - - - - + + + + + + + + + diff --git a/files/mygui/openmw_itemselection_dialog.layout b/files/mygui/openmw_itemselection_dialog.layout index 81376d6d5..39c048303 100644 --- a/files/mygui/openmw_itemselection_dialog.layout +++ b/files/mygui/openmw_itemselection_dialog.layout @@ -11,7 +11,8 @@ - + + diff --git a/files/mygui/openmw_magicselection_dialog.layout b/files/mygui/openmw_magicselection_dialog.layout index 31ad7b4c9..a89795473 100644 --- a/files/mygui/openmw_magicselection_dialog.layout +++ b/files/mygui/openmw_magicselection_dialog.layout @@ -11,7 +11,8 @@ - + + diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 63fd03b14..8b64de22e 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -19,7 +19,9 @@ - + + + diff --git a/files/mygui/openmw_quickkeys_menu.layout b/files/mygui/openmw_quickkeys_menu.layout index a45f2896f..dcb10404d 100644 --- a/files/mygui/openmw_quickkeys_menu.layout +++ b/files/mygui/openmw_quickkeys_menu.layout @@ -30,7 +30,8 @@ - + + diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 077dbac2d..38d2786e5 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -103,16 +103,17 @@ - + - - - + + + + + - @@ -157,26 +158,35 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -200,7 +210,7 @@ - + @@ -230,74 +240,99 @@ - - + + + + + + - + - - + + + + + - - - + + + + + - - - + + + + + - - - - + + + + + - - - + + + + + - - - - - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + + + + + + + + - - @@ -307,8 +342,9 @@ - - + + + diff --git a/files/mygui/openmw_text_input.layout b/files/mygui/openmw_text_input.layout index c8f76b257..a2ed94c67 100644 --- a/files/mygui/openmw_text_input.layout +++ b/files/mygui/openmw_text_input.layout @@ -10,7 +10,8 @@ - + + diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index c8a9f2523..7de6c85e6 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -5,20 +5,20 @@ - - + + - + - + - + - + @@ -51,14 +51,18 @@ - + - - - - - + + + + + + + + + From ba0acd4056292e4d18d56f9f5fa0ee1d86c7ccea Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 13:52:37 +0200 Subject: [PATCH 560/688] changed some text colors --- apps/openmw/mwgui/spellbuyingwindow.cpp | 5 ----- files/mygui/openmw_button.skin.xml | 2 +- files/mygui/openmw_spell_buying_window.layout | 3 ++- files/mygui/openmw_text.skin.xml | 10 +++++----- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 59d811335..55a885512 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -44,11 +44,6 @@ namespace MWGui mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); - int cancelButtonWidth = mCancelButton->getTextSize().width + 24; - mCancelButton->setCoord(430-cancelButtonWidth, - mCancelButton->getTop(), - cancelButtonWidth, - mCancelButton->getHeight()); mSpells->setCoord(450/2-mSpells->getTextSize().width/2, mSpells->getTop(), mSpells->getTextSize().width, diff --git a/files/mygui/openmw_button.skin.xml b/files/mygui/openmw_button.skin.xml index 491b3f47b..2895d52cc 100644 --- a/files/mygui/openmw_button.skin.xml +++ b/files/mygui/openmw_button.skin.xml @@ -74,7 +74,7 @@ - + diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout index 6b45b231c..ccbaf5f3a 100644 --- a/files/mygui/openmw_spell_buying_window.layout +++ b/files/mygui/openmw_spell_buying_window.layout @@ -24,7 +24,8 @@ - + + diff --git a/files/mygui/openmw_text.skin.xml b/files/mygui/openmw_text.skin.xml index 3e1a977e5..13b22ff93 100644 --- a/files/mygui/openmw_text.skin.xml +++ b/files/mygui/openmw_text.skin.xml @@ -81,7 +81,7 @@ - + @@ -96,7 +96,7 @@ - + @@ -111,7 +111,7 @@ - + @@ -126,8 +126,8 @@ - - + + From 465159d6c0e7cefffc84218d74dfe9a477981e38 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Sep 2012 14:03:57 +0200 Subject: [PATCH 561/688] updated credits.txt --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 62e41ff0b..2968e3487 100644 --- a/credits.txt +++ b/credits.txt @@ -10,6 +10,7 @@ please notify a developer. Programmers: Marc Zinnschlag (Zini) - Lead Programmer/Project Manager +Adam Hogan (aurix) Aleksandar Jovanov Alexander Olofsson (Ace) Artem Kotsynyak (greye) From 996e6280d87031385f1633293aa09321f31de815 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 14:10:01 +0200 Subject: [PATCH 562/688] more spell buying window cleanup --- apps/openmw/mwgui/spellbuyingwindow.cpp | 58 ++++--------------- apps/openmw/mwgui/spellbuyingwindow.hpp | 5 +- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + files/mygui/openmw_spell_buying_window.layout | 7 ++- 4 files changed, 18 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 55a885512..a41869e32 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -34,13 +34,7 @@ namespace MWGui getWidget(mPlayerGold, "PlayerGold"); getWidget(mSelect, "Select"); getWidget(mSpells, "Spells"); - getWidget(mSpellsBoxWidget, "SpellsBox"); - getWidget(mSpellsClientWidget, "SpellsClient"); - getWidget(mSpellsScrollerWidget, "SpellsScroller"); - - mSpellsClientWidget->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); - - mSpellsScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &SpellBuyingWindow::onScrollChangePosition); + getWidget(mSpellsView, "SpellsView"); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onCancelButtonClicked); @@ -58,7 +52,7 @@ namespace MWGui { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); int price = spell->data.cost*MWBase::Environment::get().getWorld()->getStore().gameSettings.search("fSpellValueMult")->f; - MyGUI::Button* toAdd = mSpellsClientWidget->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SpellText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); + MyGUI::Button* toAdd = mSpellsView->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SpellText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; /// \todo price adjustment depending on merchantile skill toAdd->setUserData(price); @@ -73,11 +67,10 @@ namespace MWGui void SpellBuyingWindow::clearSpells() { - mSpellsScrollerWidget->setScrollPosition(0); - onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); + mSpellsView->setViewOffset(MyGUI::IntPoint(0,0)); mCurrentY = 0; - while (mSpellsClientWidget->getChildCount()) - MyGUI::Gui::getInstance().destroyWidget(mSpellsClientWidget->getChildAt(0)); + while (mSpellsView->getChildCount()) + MyGUI::Gui::getInstance().destroyWidget(mSpellsView->getChildAt(0)); mSpellsWidgetMap.clear(); } @@ -106,7 +99,8 @@ namespace MWGui } updateLabels(); - updateScroller(); + + mSpellsView->setCanvasSize (MyGUI::IntSize(mSpellsView->getWidth(), std::max(mSpellsView->getHeight(), mCurrentY))); } void SpellBuyingWindow::onSpellButtonClick(MyGUI::Widget* _sender) @@ -120,10 +114,9 @@ namespace MWGui MWMechanics::Spells& spells = stats.getSpells(); spells.add (mSpellsWidgetMap.find(_sender)->second); mWindowManager.getTradeWindow()->addOrRemoveGold(-price); - mSpellsScrollerWidget->setScrollPosition(0); - onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); - updateScroller(); startSpellBuying(mActor); + + MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0); } } @@ -149,39 +142,12 @@ namespace MWGui mWindowManager.removeGuiMode(GM_Dialogue); } - void SpellBuyingWindow::updateScroller() - { - mSpellsScrollerWidget->setScrollRange(std::max(mCurrentY - mSpellsClientWidget->getHeight(), 0)); - mSpellsScrollerWidget->setScrollPage(std::max(mSpellsClientWidget->getHeight() - sLineHeight, 0)); - if (mCurrentY != 0) - mSpellsScrollerWidget->setTrackSize( (mSpellsBoxWidget->getHeight() / float(mCurrentY)) * mSpellsScrollerWidget->getLineSize() ); - } - - void SpellBuyingWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos) - { - int diff = mLastPos - pos; - // Adjust position of all widget according to difference - if (diff == 0) - return; - mLastPos = pos; - - for (unsigned int i=0;igetChildCount();i++) - { - MyGUI::Widget* toMove = mSpellsClientWidget->getChildAt(i); - toMove->setCoord(toMove->getCoord() + MyGUI::IntPoint(0, diff)); - } - } - void SpellBuyingWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel) { - if (mSpellsScrollerWidget->getScrollPosition() - _rel*0.3 < 0) - mSpellsScrollerWidget->setScrollPosition(0); - else if (mSpellsScrollerWidget->getScrollPosition() - _rel*0.3 > mSpellsScrollerWidget->getScrollRange()-1) - mSpellsScrollerWidget->setScrollPosition(mSpellsScrollerWidget->getScrollRange()-1); + if (mSpellsView->getViewOffset().top + _rel*0.3 > 0) + mSpellsView->setViewOffset(MyGUI::IntPoint(0, 0)); else - mSpellsScrollerWidget->setScrollPosition(mSpellsScrollerWidget->getScrollPosition() - _rel*0.3); - - onScrollChangePosition(mSpellsScrollerWidget, mSpellsScrollerWidget->getScrollPosition()); + mSpellsView->setViewOffset(MyGUI::IntPoint(0, mSpellsView->getViewOffset().top + _rel*0.3)); } } diff --git a/apps/openmw/mwgui/spellbuyingwindow.hpp b/apps/openmw/mwgui/spellbuyingwindow.hpp index 6f94e91b2..970498cd9 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.hpp +++ b/apps/openmw/mwgui/spellbuyingwindow.hpp @@ -33,8 +33,7 @@ namespace MWGui MyGUI::TextBox* mSpells; MyGUI::TextBox* mSelect; - MyGUI::WidgetPtr mSpellsBoxWidget, mSpellsClientWidget; - MyGUI::ScrollBar* mSpellsScrollerWidget; + MyGUI::ScrollView* mSpellsView; MWWorld::Ptr mActor; @@ -42,8 +41,6 @@ namespace MWGui void onCancelButtonClicked(MyGUI::Widget* _sender); void onSpellButtonClick(MyGUI::Widget* _sender); - void updateScroller(); - void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos); void onMouseWheel(MyGUI::Widget* _sender, int _rel); void addSpell(const std::string& spellID); void clearSpells(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9545304af..02718f365 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -678,6 +678,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mScrollWindow->center(); mBookWindow->center(); mQuickKeysMenu->center(); + mSpellBuyingWindow->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); mInputBlocker->setSize(MyGUI::IntSize(x,y)); } diff --git a/files/mygui/openmw_spell_buying_window.layout b/files/mygui/openmw_spell_buying_window.layout index ccbaf5f3a..1e18fda23 100644 --- a/files/mygui/openmw_spell_buying_window.layout +++ b/files/mygui/openmw_spell_buying_window.layout @@ -15,9 +15,10 @@ - - - + + + + From d581ac426e16b231cb89f1ea3cbd1b2f5acae831 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 14:10:56 +0200 Subject: [PATCH 563/688] useless cout message --- apps/openmw/mwgui/quickkeysmenu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index c46445465..89b43e0ec 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -53,7 +53,6 @@ namespace MWGui mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight())); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked); - std::cout << "real ok button is : " << mOkButton->getName() << std::endl; center(); From ad66ebf84f3887b2791c2a3dadf0fdd136fcd16d Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 14:31:19 +0200 Subject: [PATCH 564/688] fix button align on 2 windows --- files/mygui/openmw_confirmation_dialog.layout | 2 +- files/mygui/openmw_count_window.layout | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index ba98d3220..fe7f7cbf6 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -13,7 +13,7 @@ - + diff --git a/files/mygui/openmw_count_window.layout b/files/mygui/openmw_count_window.layout index 59b0a9c30..a021d7df9 100644 --- a/files/mygui/openmw_count_window.layout +++ b/files/mygui/openmw_count_window.layout @@ -21,7 +21,7 @@ - + From fdc2a7433c91fd93c982e6ccce5d1d5fd93a5b50 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 15:01:51 +0200 Subject: [PATCH 565/688] remove the wrong container open sound --- apps/openmw/mwclass/container.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 4f11e1c0e..6fa23a98a 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -100,7 +100,6 @@ namespace MWClass if(ptr.getCellRef().trap.empty()) { boost::shared_ptr action (new MWWorld::ActionOpen(ptr)); - action->setSound ("chest open"); return action; } else From 89ad2af1d903bf34d12e5cba9b8f9d839b6805ef Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 17:44:59 +0200 Subject: [PATCH 566/688] key usage to open doors or containers to test: player->additem, "key_arrile", 1 (door in arille's tradehouse) player->additem, "key_alvur", 1 (chest in the back of beshara) --- apps/openmw/mwclass/container.cpp | 38 +++++++++-- apps/openmw/mwclass/door.cpp | 103 ++++++++++++++++++------------ apps/openmw/mwgui/tradewindow.cpp | 3 +- 3 files changed, 95 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 6fa23a98a..1164873a0 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -14,6 +14,8 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/actionopen.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwgui/tooltips.hpp" @@ -87,15 +89,31 @@ namespace MWClass const std::string lockedSound = "LockedChest"; const std::string trapActivationSound = "Disarm Trap Fail"; - if (ptr.getCellRef().lockLevel>0) + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); + + bool needKey = ptr.getCellRef().lockLevel>0; + bool hasKey = false; + std::string keyName; + for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { - // TODO check for key - std::cout << "Locked container" << std::endl; - boost::shared_ptr action(new MWWorld::NullAction); - action->setSound(lockedSound); - return action; + if (it->getCellRef ().refID == ptr.getCellRef().key) + { + hasKey = true; + keyName = MWWorld::Class::get(*it).getName(*it); + } } - else + + if (needKey && hasKey) + { + MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}", std::vector()); + ptr.getCellRef().lockLevel = 0; + // using a key disarms the trap + ptr.getCellRef().trap = ""; + } + + + if (!needKey || hasKey) { if(ptr.getCellRef().trap.empty()) { @@ -112,6 +130,12 @@ namespace MWClass return action; } } + else + { + boost::shared_ptr action(new MWWorld::NullAction); + action->setSound(lockedSound); + return action; + } } std::string Container::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index f483aa3a8..96c6eba5f 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -13,6 +13,7 @@ #include "../mwworld/actionteleport.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwgui/tooltips.hpp" @@ -74,60 +75,80 @@ namespace MWClass const std::string lockedSound = "LockedDoor"; const std::string trapActivationSound = "Disarm Trap Fail"; - if (ptr.getCellRef().lockLevel>0) + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); + + bool needKey = ptr.getCellRef().lockLevel>0; + bool hasKey = false; + std::string keyName; + for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { - // TODO check for key - // TODO report failure to player (message, sound?). Look up behaviour of original MW. - std::cout << "Locked!" << std::endl; - - boost::shared_ptr action(new MWWorld::NullAction); - - action->setSound(lockedSound); - - return action; - } - - if(!ptr.getCellRef().trap.empty()) - { - // Trap activation - std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; - - boost::shared_ptr action(new MWWorld::NullAction); - - action->setSound(trapActivationSound); - ptr.getCellRef().trap = ""; - - return action; - } - - if (ref->ref.teleport) - { - // teleport door - /// \todo remove this if clause once ActionTeleport can also support other actors - if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) + if (it->getCellRef ().refID == ptr.getCellRef().key) { - boost::shared_ptr action(new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); + hasKey = true; + keyName = MWWorld::Class::get(*it).getName(*it); + } + } - action->setSound(openSound); + if (needKey && hasKey) + { + MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}", std::vector()); + ptr.getCellRef().lockLevel = 0; + // using a key disarms the trap + ptr.getCellRef().trap = ""; + } + + if (!needKey || hasKey) + { + if(!ptr.getCellRef().trap.empty()) + { + // Trap activation + std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl; + + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(trapActivationSound); + ptr.getCellRef().trap = ""; return action; } + + if (ref->ref.teleport) + { + // teleport door + /// \todo remove this if clause once ActionTeleport can also support other actors + if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) + { + boost::shared_ptr action(new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); + + action->setSound(openSound); + + return action; + } + else + { + // another NPC or a creature is using the door + return boost::shared_ptr (new MWWorld::NullAction); + } + } else { - // another NPC or a creature is using the door - return boost::shared_ptr (new MWWorld::NullAction); + // animated door + // TODO return action for rotating the door + + // This is a little pointless, but helps with testing + boost::shared_ptr action(new MWWorld::NullAction); + + action->setSound(openSound); + + return action; } } else { - // animated door - // TODO return action for rotating the door - - // This is a little pointless, but helps with testing + // locked, and we can't open. boost::shared_ptr action(new MWWorld::NullAction); - - action->setSound(openSound); - + action->setSound(lockedSound); return action; } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 6d7286dd0..27a24c22c 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -179,7 +179,8 @@ namespace MWGui mWindowManager.getInventoryWindow()->transferBoughtItems(); // add or remove gold from the player. - addOrRemoveGold(mCurrentBalance); + if (mCurrentBalance != 0) + addOrRemoveGold(mCurrentBalance); std::string sound = "Item Gold Up"; MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); From 9448ce5968228a86d827cc14b9d4923920e16578 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 18:44:59 +0200 Subject: [PATCH 567/688] implemented "getDisabled" functions --- apps/openmw/mwbase/inputmanager.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 5 +++++ apps/openmw/mwinput/inputmanagerimp.hpp | 1 + apps/openmw/mwscript/controlextensions.cpp | 19 +++++++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 1 + 5 files changed, 27 insertions(+) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 4c73c72b9..424501229 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -31,6 +31,7 @@ namespace MWBase virtual void setDragDrop(bool dragDrop) = 0; virtual void toggleControlSwitch (const std::string& sw, bool value) = 0; + virtual bool getControlSwitch (const std::string& sw) = 0; virtual std::string getActionDescription (int action) = 0; virtual std::string getActionBindingName (int action) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f47ea8bb1..022d7df3c 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -380,6 +380,11 @@ namespace MWInput adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); } + bool InputManager::getControlSwitch (const std::string& sw) + { + return mControlSwitch[sw]; + } + void InputManager::toggleControlSwitch (const std::string& sw, bool value) { if (mControlSwitch[sw] == value) { diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 2572ec115..9963c73c2 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -75,6 +75,7 @@ namespace MWInput virtual void setDragDrop(bool dragDrop); virtual void toggleControlSwitch (const std::string& sw, bool value); + virtual bool getControlSwitch (const std::string& sw); virtual std::string getActionDescription (int action); virtual std::string getActionBindingName (int action); diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index bd14e7b8d..0096c69ab 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -41,6 +41,22 @@ namespace MWScript } }; + class OpGetDisabled : public Interpreter::Opcode0 + { + std::string mControl; + + public: + + OpGetDisabled (const std::string& control) + : mControl (control) + {} + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push(!MWBase::Environment::get().getInputManager()->getControlSwitch (mControl)); + } + }; + class OpToggleCollision : public Interpreter::Opcode0 { public: @@ -103,6 +119,7 @@ namespace MWScript const int opcodeClearForceSneakExplicit = 0x2000159; const int opcodeForceSneak = 0x200015a; const int opcodeForceSneakExplicit = 0x200015b; + const int opcodeGetDisabled = 0x2000175; const char *controls[numberOfControls] = { @@ -119,6 +136,7 @@ namespace MWScript { extensions.registerInstruction (enable + controls[i], "", opcodeEnable+i); extensions.registerInstruction (disable + controls[i], "", opcodeDisable+i); + extensions.registerFunction (std::string("get") + controls[i] + std::string("disabled"), 'l', "", opcodeGetDisabled+i); } extensions.registerInstruction ("togglecollision", "", opcodeToggleCollision); @@ -141,6 +159,7 @@ namespace MWScript { interpreter.installSegment5 (opcodeEnable+i, new OpSetControl (controls[i], true)); interpreter.installSegment5 (opcodeDisable+i, new OpSetControl (controls[i], false)); + interpreter.installSegment5 (opcodeGetDisabled+i, new OpGetDisabled (controls[i])); } interpreter.installSegment5 (opcodeToggleCollision, new OpToggleCollision); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index dbc8159d1..a91752200 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -182,4 +182,5 @@ op 0x2000171: user4 (implicit reference, console only, requires --script-console op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference op 0x2000174: ToggleVanityMode +op 0x2000175-0x200018B: Get controls disabled opcodes 0x2000175-0x3ffffff unused From 6c03d7aec40e618a7b0a1e9fb0c00c5b2b6406ed Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Sep 2012 21:12:35 +0200 Subject: [PATCH 568/688] documentation fix --- apps/openmw/mwscript/docs/vmformat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index a91752200..59b9bca35 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -183,4 +183,4 @@ op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference op 0x2000174: ToggleVanityMode op 0x2000175-0x200018B: Get controls disabled -opcodes 0x2000175-0x3ffffff unused +opcodes 0x200018C-0x3ffffff unused From 4977c33e4c248991266f06a0c59abe8caae037b5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Sep 2012 21:58:19 +0200 Subject: [PATCH 569/688] fixed a stats window resizing problem that I introduced today --- apps/openmw/mwgui/stats_window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index e90b0539f..ffbfd7d78 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -86,6 +86,7 @@ void StatsWindow::onWindowResize(MyGUI::Window* window) { mLeftPane->setCoord( MyGUI::IntCoord(0, 0, 0.44*window->getSize().width, window->getSize().height) ); mRightPane->setCoord( MyGUI::IntCoord(0.44*window->getSize().width, 0, 0.56*window->getSize().width, window->getSize().height) ); + mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight)); } void StatsWindow::setBar(const std::string& name, const std::string& tname, int val, int max) From 3c2efbe966a94241b0e112ad6cda2fb527612ded Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 10 Sep 2012 23:03:11 +0300 Subject: [PATCH 570/688] Disable "make install" for Debian based linux distributions. Signed-off-by: Lukasz Gromanowski --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index af5128705..d89addbd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -630,8 +630,8 @@ if (APPLE) include(CPack) endif (APPLE) -if (NOT WIN32) - ## Linux building +if (NOT WIN32 AND NOT DPKG_PROGRAM) + ## Non Debian based Linux building # paths set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/bin" CACHE PATH "Where to install binaries") set(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") From 4076c19cc6965e2ad2da01f8ff2de73bdee313c8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Sep 2012 22:30:05 +0200 Subject: [PATCH 571/688] silencing a cmake warning --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d89addbd4..b5b0884e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -661,4 +661,4 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM) # Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) -endif(NOT WIN32) \ No newline at end of file +endif(NOT WIN32 AND NOT DPKG_PROGRAM) From c5b25ef70b9264c2a9036bcbb04da7fb50336b25 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 10 Sep 2012 20:29:24 -0400 Subject: [PATCH 572/688] Very basic actor physics (no set scale/rotate functions) --- apps/openmw/mwrender/npcanimation.cpp | 1 + components/nifbullet/bullet_nif_loader.cpp | 1 + libs/openengine/bullet/physic.cpp | 46 +++++++++++----------- libs/openengine/bullet/physic.hpp | 7 ++-- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index e509bd170..e0304e06e 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -122,6 +122,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere } + if(isFemale) mInsert->scale(race->data.height.female, race->data.height.female, race->data.height.female); else diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 6fec6241f..1dc22a0d3 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -74,6 +74,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) cShape->collide = false; mBoundingBox = NULL; cShape->boxTranslation = Ogre::Vector3(0,0,0); + cShape->boxRotation = Ogre::Quaternion::IDENTITY; mTriMesh = new btTriangleMesh(); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index f453915c8..8fa7681f5 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -27,13 +27,10 @@ namespace Physic }; PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale): - mName(name), mEngine(engine), mMesh(mesh), mBoxTranslation(Ogre::Vector3::ZERO), mBoxRotation(Ogre::Quaternion::ZERO), mBody(0), collisionMode(false) + mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0), mBody(0), collisionMode(false), mBoxRotation(0,0,0,0) { - Ogre::Vector3 test; - mBody = mEngine->createAndAdjustRigidBody(mesh, mName, scale, position, rotation, &test); - std::cout << "Test" << test << "\n"; + mBody = mEngine->createAndAdjustRigidBody(mesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, &mBoxRotationInverse); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map - } PhysicActor::~PhysicActor() @@ -79,12 +76,17 @@ namespace Physic btVector3 PhysicActor::getPosition(void) { - return mBody->getWorldTransform().getOrigin();//return internalGhostObject->getWorldTransform().getOrigin() -mTranslation; + btVector3 vec = mBody->getWorldTransform().getOrigin(); + Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(), + mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ()); + Ogre::Vector3 transrot = rotation * mBoxScaledTranslation; + btVector3 visualPosition = vec - btVector3(transrot.x, transrot.y, transrot.z); + return visualPosition; } btQuaternion PhysicActor::getRotation(void) { - return mBody->getWorldTransform().getRotation();//return btQuaternion::internalGhostObject->getWorldTransform().getRotation(); + return mBody->getWorldTransform().getRotation() * mBoxRotationInverse; } void PhysicActor::setPosition(const btVector3& pos) @@ -296,6 +298,7 @@ namespace Physic rotation = rotation * boxRotation; Ogre::Vector3 transrot = rotation * scaledBoxTranslation; Ogre::Vector3 newPosition = transrot + position; + tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); body->setWorldTransform(tr); @@ -310,21 +313,14 @@ namespace Physic BulletShapeManager::getSingletonPtr()->load(outputstring,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); - btBoxShape* box = dynamic_cast(shape->Shape); - if(box != NULL) - adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); - else - adjustRigidBody(body, position, rotation); + adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); } RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation) + Ogre::Vector3* scaledBoxTranslation, btQuaternion* boxRotation, btQuaternion* boxRotationInverse) { - if(scaledBoxTranslation != 0) - *scaledBoxTranslation = Ogre::Vector3(0, 5, 0); std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; - //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif mShapeLoader->load(outputstring,"General"); @@ -332,9 +328,6 @@ namespace Physic BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(outputstring,"General"); shape->Shape->setLocalScaling( btVector3(scale,scale,scale)); - - - // //create the motionState CMotionState* newMotionState = new CMotionState(this,name); @@ -344,11 +337,16 @@ namespace Physic RigidBody* body = new RigidBody(CI,name); body->collide = shape->collide; - btBoxShape* box = dynamic_cast(shape->Shape); - if(box != NULL) - adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); - else - adjustRigidBody(body, position, rotation); + if(scaledBoxTranslation != 0) + *scaledBoxTranslation = shape->boxTranslation * scale; + if(boxRotation != 0) + *boxRotation = btQuaternion(shape->boxRotation.x, shape->boxRotation.y, shape->boxRotation.z,shape->boxRotation.w); + if(boxRotationInverse != 0){ + Ogre::Quaternion inverse = shape->boxRotation.Inverse(); + *boxRotationInverse = btQuaternion(inverse.x,inverse.y,inverse.z,inverse.w); + } + + adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); return body; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f699b7d44..1ff706146 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -90,8 +90,9 @@ namespace Physic private: OEngine::Physic::RigidBody* mBody; - Ogre::Vector3 mBoxTranslation; - Ogre::Quaternion mBoxRotation; + Ogre::Vector3 mBoxScaledTranslation; + btQuaternion mBoxRotationInverse; + btQuaternion mBoxRotation; bool collisionMode; std::string mMesh; PhysicEngine* mEngine; @@ -143,7 +144,7 @@ namespace Physic * After created, the body is set to the correct rotation, position, and scale */ RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0); + Ogre::Vector3* scaledBoxTranslation = 0, btQuaternion* boxRotation = 0, btQuaternion* boxRotationInverse = 0); /** * Adjusts a rigid body to the right position and rotation From 2b339f6c0fd9242cd5bf800e68963acfa3abf479 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Sep 2012 16:37:54 +0200 Subject: [PATCH 573/688] loading screen --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 13 ++- apps/openmw/mwbase/inputmanager.hpp | 2 +- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwgui/loadingscreen.cpp | 108 +++++++++++++++++++++++ apps/openmw/mwgui/loadingscreen.hpp | 44 +++++++++ apps/openmw/mwgui/windowmanagerimp.cpp | 11 +++ apps/openmw/mwgui/windowmanagerimp.hpp | 4 + apps/openmw/mwinput/inputmanagerimp.cpp | 5 +- apps/openmw/mwinput/inputmanagerimp.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 68 ++++++++++++++ files/mygui/CMakeLists.txt | 1 + files/mygui/openmw_layers.xml | 1 + files/mygui/openmw_loading_screen.layout | 16 ++++ libs/openengine/gui/manager.cpp | 50 ++++++----- libs/openengine/gui/manager.hpp | 35 +++++--- 16 files changed, 314 insertions(+), 50 deletions(-) create mode 100644 apps/openmw/mwgui/loadingscreen.cpp create mode 100644 apps/openmw/mwgui/loadingscreen.hpp create mode 100644 files/mygui/openmw_loading_screen.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4cf3bd7fd..cffbc60c7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -29,7 +29,7 @@ add_openmw_dir (mwgui map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu - itemselection spellbuyingwindow + itemselection spellbuyingwindow loadingscreen ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 171fcde7c..82bd3b69b 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -67,7 +67,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.setFrameDuration (evt.timeSinceLastFrame); // update input - MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame); + MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame, false); // sound if (mUseSound) @@ -351,6 +351,11 @@ void OMW::Engine::go() mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions)); + // Sets up the input system + mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, + MWBase::Environment::get().getWorld()->getPlayer(), + *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); + // load cell ESM::Position pos; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; @@ -370,12 +375,6 @@ void OMW::Engine::go() MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, pos); } - // Sets up the input system - - mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, - MWBase::Environment::get().getWorld()->getPlayer(), - *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); - std::cout << "\nPress Q/ESC or close window to exit.\n"; mOgre->getRoot()->addFrameListener (this); diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 424501229..8293cbfa7 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -22,7 +22,7 @@ namespace MWBase virtual ~InputManager() {} - virtual void update(float dt) = 0; + virtual void update(float dt, bool loading) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 14b1051e8..bd8b7eb18 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -216,6 +216,8 @@ namespace MWBase virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; virtual void executeInConsole (const std::string& path) = 0; + + virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0; }; } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp new file mode 100644 index 000000000..efda76caa --- /dev/null +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -0,0 +1,108 @@ +#include "loadingscreen.hpp" + +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/inputmanager.hpp" + +namespace MWGui +{ + + LoadingScreen::LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw, MWBase::WindowManager& parWindowManager) + : mSceneMgr(sceneMgr) + , mWindow(rw) + , WindowBase("openmw_loading_screen.layout", parWindowManager) + , mLoadingOn(false) + , mLastRenderTime(0.f) + { + getWidget(mLoadingText, "LoadingText"); + } + + LoadingScreen::~LoadingScreen() + { + } + + void LoadingScreen::setLoadingProgress (const std::string& stage, int depth, int current, int total) + { + if (!mLoadingOn) + loadingOn(); + + if (depth == 0) + { + mCurrentCellLoading = current; + mTotalCellsLoading = total; + + mCurrentRefLoading = 0; + + } + if (depth == 1) + { + mCurrentRefLoading = current; + mTotalRefsLoading = total; + } + + if (mTotalCellsLoading == 0) + { + loadingOff(); + return; + } + + float refProgress; + if (mTotalRefsLoading <= 1) + refProgress = 0; + else + refProgress = float(mCurrentRefLoading) / float(mTotalRefsLoading-1); + + float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading); + assert(progress <= 1 && progress >= 0); + if (progress >= 1) + { + loadingOff(); + return; + } + + mLoadingText->setCaption(stage + "... " + Ogre::StringConverter::toString(progress)); + + static float loadingScreenFps = 40.f; + + //if (mTimer.getMilliseconds () > mLastRenderTime + (1.f/loadingScreenFps) * 1000.f) + { + mLastRenderTime = mTimer.getMilliseconds (); + + + // Turn off rendering except the GUI + mSceneMgr->clearSpecialCaseRenderQueues(); + // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. + for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i) + { + if (i > 10 && i < 90) + mSceneMgr->addSpecialCaseRenderQueue(i); + } + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + + // always update input before rendering something, otherwise mygui goes crazy when something was entered in the frame before + // (e.g. when using "coc" console command, it would enter an infinite loop and crash due to overflow) + MWBase::Environment::get().getInputManager()->update(0, true); + + mWindow->update(); + + // resume 3d rendering + mSceneMgr->clearSpecialCaseRenderQueues(); + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + } + } + + void LoadingScreen::loadingOn() + { + setVisible(true); + mLoadingOn = true; + } + + + void LoadingScreen::loadingOff() + { + setVisible(false); + mLoadingOn = false; + } +} diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp new file mode 100644 index 000000000..199bde372 --- /dev/null +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -0,0 +1,44 @@ +#ifndef MWGUI_LOADINGSCREEN_H +#define MWGUI_LOADINGSCREEN_H + +#include +#include + +#include "window_base.hpp" + +namespace MWGui +{ + + class LoadingScreen : public WindowBase + { + public: + LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw, MWBase::WindowManager& parWindowManager); + virtual ~LoadingScreen(); + + void setLoadingProgress (const std::string& stage, int depth, int current, int total); + + private: + Ogre::SceneManager* mSceneMgr; + Ogre::RenderWindow* mWindow; + + unsigned long mLastRenderTime; + Ogre::Timer mTimer; + + MyGUI::TextBox* mLoadingText; + + int mCurrentCellLoading; + int mTotalCellsLoading; + int mCurrentRefLoading; + int mTotalRefsLoading; + + + bool mLoadingOn; + + void loadingOn(); + void loadingOff(); + }; + +} + + +#endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 02718f365..196e898b0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -43,6 +43,7 @@ #include "alchemywindow.hpp" #include "spellwindow.hpp" #include "quickkeysmenu.hpp" +#include "loadingscreen.hpp" using namespace MWGui; @@ -67,6 +68,7 @@ WindowManager::WindowManager( , mConfirmationDialog(NULL) , mAlchemyWindow(NULL) , mSpellWindow(NULL) + , mLoadingScreen(NULL) , mCharGen(NULL) , mPlayerClass() , mPlayerName() @@ -146,6 +148,9 @@ WindowManager::WindowManager( mSpellWindow = new SpellWindow(*this); mQuickKeysMenu = new QuickKeysMenu(*this); + mLoadingScreen = new LoadingScreen(mOgre->getScene (), mOgre->getWindow (), *this); + mLoadingScreen->setCoord (0,0,w,h); + mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); // The HUD is always on @@ -679,6 +684,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mBookWindow->center(); mQuickKeysMenu->center(); mSpellBuyingWindow->center(); + mLoadingScreen->setCoord (0,0,x,y); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); mInputBlocker->setSize(MyGUI::IntSize(x,y)); } @@ -897,3 +903,8 @@ void WindowManager::toggleHud () mHudEnabled = !mHudEnabled; mHud->setVisible (mHudEnabled); } + +void WindowManager::setLoadingProgress (const std::string& stage, int depth, int current, int total) +{ + mLoadingScreen->setLoadingProgress (stage, depth, current, total); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a91478bbc..5327bb868 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -61,6 +61,7 @@ namespace MWGui class SettingsWindow; class AlchemyWindow; class QuickKeysMenu; + class LoadingScreen; class WindowManager : public MWBase::WindowManager { @@ -195,6 +196,8 @@ namespace MWGui virtual void executeInConsole (const std::string& path); + virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total); + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; @@ -219,6 +222,7 @@ namespace MWGui AlchemyWindow* mAlchemyWindow; SpellWindow* mSpellWindow; QuickKeysMenu* mQuickKeysMenu; + LoadingScreen* mLoadingScreen; CharacterCreation* mCharGen; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 022d7df3c..b198785d2 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -233,14 +233,15 @@ namespace MWInput } } - void InputManager::update(float dt) + void InputManager::update(float dt, bool loading) { // Tell OIS to handle all input events mKeyboard->capture(); mMouse->capture(); // update values of channels (as a result of pressed keys) - mInputCtrl->update(dt); + if (!loading) + mInputCtrl->update(dt); // Update windows/gui as a result of input events // For instance this could mean opening a new window/dialog, diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 9963c73c2..7d03f1d5b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -66,7 +66,7 @@ namespace MWInput virtual ~InputManager(); - virtual void update(float dt); + virtual void update(float dt, bool loading); virtual void changeInputMode(bool guiMode); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index eb03e9a7b..bcf4a2002 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -25,9 +25,14 @@ namespace const MWWorld::Class& class_ = MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.list.begin(), &cell)); + int numRefs = cellRefList.list.size(); + int current = 0; for (typename T::List::iterator it = cellRefList.list.begin(); it != cellRefList.list.end(); it++) { + MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, current, numRefs); + ++current; + if (it->mData.getCount() || it->mData.isEnabled()) { MWWorld::Ptr ptr (&*it, &cell); @@ -175,6 +180,26 @@ namespace MWWorld CellStoreCollection::iterator active = mActiveCells.begin(); + // get the number of cells to unload + int numUnload = 0; + while (active!=mActiveCells.end()) + { + if (!((*active)->cell->data.flags & ESM::Cell::Interior)) + { + if (std::abs (X-(*active)->cell->data.gridX)<=1 && + std::abs (Y-(*active)->cell->data.gridY)<=1) + { + // keep cells within the new 3x3 grid + ++active; + continue; + } + } + ++active; + ++numUnload; + } + + int current = 0; + active = mActiveCells.begin(); while (active!=mActiveCells.end()) { if (!((*active)->cell->data.flags & ESM::Cell::Interior)) @@ -188,10 +213,35 @@ namespace MWWorld } } + MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload); unloadCell (active++); + ++current; } + int numLoad = 0; + // get the number of cells to load + for (int x=X-1; x<=X+1; ++x) + for (int y=Y-1; y<=Y+1; ++y) + { + CellStoreCollection::iterator iter = mActiveCells.begin(); + + while (iter!=mActiveCells.end()) + { + assert (!((*iter)->cell->data.flags & ESM::Cell::Interior)); + + if (x==(*iter)->cell->data.gridX && + y==(*iter)->cell->data.gridY) + break; + + ++iter; + } + + if (iter==mActiveCells.end()) + ++numLoad; + } + // Load cells + current = 0; for (int x=X-1; x<=X+1; ++x) for (int y=Y-1; y<=Y+1; ++y) { @@ -212,7 +262,9 @@ namespace MWWorld { CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); + MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, current, numLoad); loadCell (cell); + ++current; } } @@ -275,14 +327,30 @@ namespace MWWorld // remove active CellStoreCollection::iterator active = mActiveCells.begin(); + // count number of cells to unload + int numUnload = 0; while (active!=mActiveCells.end()) { + ++active; + ++numUnload; + } + + // unload + int current = 0; + active = mActiveCells.begin(); + while (active!=mActiveCells.end()) + { + MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload); + unloadCell (active++); + ++current; } // Load cell. std::cout << "cellName:" << cellName << std::endl; + + MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, 0, 1); loadCell (cell); // adjust player diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 82fb64c76..fdf447e69 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -71,6 +71,7 @@ set(MYGUI_FILES openmw_itemselection_dialog.layout openmw_magicselection_dialog.layout openmw_spell_buying_window.layout + openmw_loading_screen.layout smallbars.png VeraMono.ttf markers.png diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 7a8d586d2..33a3ca40c 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -8,5 +8,6 @@ + diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout new file mode 100644 index 000000000..1b99c91bd --- /dev/null +++ b/files/mygui/openmw_loading_screen.layout @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 58929ba8b..acb4ed9df 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -8,41 +8,43 @@ using namespace OEngine::GUI; void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) { - assert(wnd); - assert(mgr); + assert(wnd); + assert(mgr); - using namespace MyGUI; + mSceneMgr = mgr; - // Enable/disable MyGUI logging to stdout. (Logging to MyGUI.log is - // still enabled.) In order to do this we have to initialize the log - // manager before the main gui system itself, otherwise the main - // object will get the chance to spit out a few messages before we - // can able to disable it. + using namespace MyGUI; - std::string theLogFile = std::string(MYGUI_PLATFORM_LOG_FILENAME); - if(!logDir.empty()) - theLogFile.insert(0, logDir); + // Enable/disable MyGUI logging to stdout. (Logging to MyGUI.log is + // still enabled.) In order to do this we have to initialize the log + // manager before the main gui system itself, otherwise the main + // object will get the chance to spit out a few messages before we + // can able to disable it. - // Set up OGRE platform. We might make this more generic later. - mPlatform = new OgrePlatform(); - LogManager::getInstance().setSTDOutputEnabled(logging); - mPlatform->initialise(wnd, mgr, "General", theLogFile); + std::string theLogFile = std::string(MYGUI_PLATFORM_LOG_FILENAME); + if(!logDir.empty()) + theLogFile.insert(0, logDir); + + // Set up OGRE platform. We might make this more generic later. + mPlatform = new OgrePlatform(); + LogManager::getInstance().setSTDOutputEnabled(logging); + mPlatform->initialise(wnd, mgr, "General", theLogFile); - // Create GUI - mGui = new Gui(); - mGui->initialise("core.xml"); + // Create GUI + mGui = new Gui(); + mGui->initialise("core.xml"); } void MyGUIManager::shutdown() { mGui->shutdown (); - delete mGui; - if(mPlatform) + delete mGui; + if(mPlatform) { - mPlatform->shutdown(); - delete mPlatform; + mPlatform->shutdown(); + delete mPlatform; } - mGui = NULL; - mPlatform = NULL; + mGui = NULL; + mPlatform = NULL; } diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index 781685acf..1ec2e2fcf 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -16,21 +16,28 @@ namespace Ogre namespace OEngine { namespace GUI { - class MyGUIManager - { - MyGUI::OgrePlatform *mPlatform; - MyGUI::Gui *mGui; + class MyGUIManager + { + MyGUI::OgrePlatform *mPlatform; + MyGUI::Gui *mGui; + Ogre::SceneManager* mSceneMgr; - public: - MyGUIManager() : mPlatform(NULL), mGui(NULL) {} - MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) - { setup(wnd,mgr,logging, logDir); } - ~MyGUIManager() { shutdown(); } + public: + MyGUIManager() : mPlatform(NULL), mGui(NULL) {} + MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) + { + setup(wnd,mgr,logging, logDir); + } + ~MyGUIManager() + { + shutdown(); + } - void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")); - void shutdown(); + void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")); + void shutdown(); - MyGUI::Gui *getGui() { return mGui; } - }; -}} + MyGUI::Gui *getGui() { return mGui; } + }; +} +} #endif From 369db182deb613835966ed6ab0cc47817df980fe Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Sep 2012 16:53:49 +0200 Subject: [PATCH 574/688] actual progress bar, performance optimization --- apps/openmw/mwgui/loadingscreen.cpp | 12 +++++++----- apps/openmw/mwgui/loadingscreen.hpp | 1 + files/mygui/openmw_loading_screen.layout | 7 +++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index efda76caa..79cbd4752 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -17,6 +17,7 @@ namespace MWGui , mLastRenderTime(0.f) { getWidget(mLoadingText, "LoadingText"); + getWidget(mProgressBar, "ProgressBar"); } LoadingScreen::~LoadingScreen() @@ -50,10 +51,10 @@ namespace MWGui float refProgress; if (mTotalRefsLoading <= 1) - refProgress = 0; + refProgress = 1; else refProgress = float(mCurrentRefLoading) / float(mTotalRefsLoading-1); - +std::cout << refProgress << " (" << mCurrentRefLoading << ", " << mTotalRefsLoading-1 << std::endl; float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading); assert(progress <= 1 && progress >= 0); if (progress >= 1) @@ -62,11 +63,12 @@ namespace MWGui return; } - mLoadingText->setCaption(stage + "... " + Ogre::StringConverter::toString(progress)); + mLoadingText->setCaption(stage + "... "); + mProgressBar->setProgressPosition (static_cast(progress * 1000)); - static float loadingScreenFps = 40.f; + static float loadingScreenFps = 30.f; - //if (mTimer.getMilliseconds () > mLastRenderTime + (1.f/loadingScreenFps) * 1000.f) + if (mTimer.getMilliseconds () > mLastRenderTime + (1.f/loadingScreenFps) * 1000.f) { mLastRenderTime = mTimer.getMilliseconds (); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 199bde372..ec01714f8 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -25,6 +25,7 @@ namespace MWGui Ogre::Timer mTimer; MyGUI::TextBox* mLoadingText; + MyGUI::ProgressBar* mProgressBar; int mCurrentCellLoading; int mTotalCellsLoading; diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index 1b99c91bd..7096115f6 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -6,8 +6,11 @@ - - + + + + + From d5a08e31e7548b8fc6798fc1552152fe5b552902 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Sep 2012 17:36:20 +0200 Subject: [PATCH 575/688] scene does not disappear anymore during load --- apps/openmw/mwgui/loadingscreen.cpp | 19 +++++++++++++++---- apps/openmw/mwgui/loadingscreen.hpp | 1 + apps/openmw/mwworld/scene.cpp | 4 ++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 79cbd4752..848cc1c71 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -29,15 +29,17 @@ namespace MWGui if (!mLoadingOn) loadingOn(); + const int numRefLists = 20; + if (depth == 0) { mCurrentCellLoading = current; mTotalCellsLoading = total; mCurrentRefLoading = 0; - + mCurrentRefList = 0; } - if (depth == 1) + else if (depth == 1) { mCurrentRefLoading = current; mTotalRefsLoading = total; @@ -54,7 +56,14 @@ namespace MWGui refProgress = 1; else refProgress = float(mCurrentRefLoading) / float(mTotalRefsLoading-1); -std::cout << refProgress << " (" << mCurrentRefLoading << ", " << mTotalRefsLoading-1 << std::endl; + refProgress += mCurrentRefList; + refProgress /= numRefLists; + + assert(refProgress <= 1 && refProgress >= 0); + + if (depth == 1 && mCurrentRefLoading == mTotalRefsLoading-1) + ++mCurrentRefList; + float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading); assert(progress <= 1 && progress >= 0); if (progress >= 1) @@ -78,7 +87,7 @@ std::cout << refProgress << " (" << mCurrentRefLoading << ", " << mTotalRefsLoa // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i) { - if (i > 10 && i < 90) + if (i > 0 && i < 90) mSceneMgr->addSpecialCaseRenderQueue(i); } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); @@ -87,7 +96,9 @@ std::cout << refProgress << " (" << mCurrentRefLoading << ", " << mTotalRefsLoa // (e.g. when using "coc" console command, it would enter an infinite loop and crash due to overflow) MWBase::Environment::get().getInputManager()->update(0, true); + mWindow->getViewport(0)->setClearEveryFrame(false); mWindow->update(); + mWindow->getViewport(0)->setClearEveryFrame(true); // resume 3d rendering mSceneMgr->clearSpecialCaseRenderQueues(); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index ec01714f8..2256bca6b 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -31,6 +31,7 @@ namespace MWGui int mTotalCellsLoading; int mCurrentRefLoading; int mTotalRefsLoading; + int mCurrentRefList; bool mLoadingOn; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bcf4a2002..c3843b831 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -50,6 +50,10 @@ namespace } } } + else + { + MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, 0, 1); + } } } From a8d9aa98b1be801e5f67b0553067f4438299f983 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Sep 2012 19:18:26 +0200 Subject: [PATCH 576/688] some fixes --- apps/openmw/mwgui/loadingscreen.cpp | 67 ++++++++++++++++++++++- apps/openmw/mwgui/loadingscreen.hpp | 7 ++- apps/openmw/mwgui/windowmanagerimp.cpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 1 + files/mygui/openmw_loading_screen.layout | 14 +++-- 5 files changed, 82 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 848cc1c71..d93cc50c3 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -2,6 +2,11 @@ #include #include +#include +#include +#include + + #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" @@ -18,10 +23,37 @@ namespace MWGui { getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); + getWidget(mBackgroundImage, "BackgroundImage"); + + + mBackgroundMaterial = Ogre::MaterialManager::getSingleton().create("BackgroundMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + mBackgroundMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + mBackgroundMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mBackgroundMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); + + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mRectangle->setMaterial("BackgroundMaterial"); + // Render the background before everything else + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY-1); + // Use infinite AAB to always stay visible + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + mRectangle->setBoundingBox(aabInf); + // Attach background to the scene + Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(mRectangle); + mRectangle->setVisible(false); } LoadingScreen::~LoadingScreen() { + delete mRectangle; + } + + void LoadingScreen::onResChange(int w, int h) + { + setCoord(0,0,w,h); } void LoadingScreen::setLoadingProgress (const std::string& stage, int depth, int current, int total) @@ -87,7 +119,7 @@ namespace MWGui // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i) { - if (i > 0 && i < 90) + if (i > 0 && i < 96) mSceneMgr->addSpecialCaseRenderQueue(i); } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); @@ -96,9 +128,38 @@ namespace MWGui // (e.g. when using "coc" console command, it would enter an infinite loop and crash due to overflow) MWBase::Environment::get().getInputManager()->update(0, true); - mWindow->getViewport(0)->setClearEveryFrame(false); + Ogre::CompositorChain* chain = Ogre::CompositorManager::getSingleton().getCompositorChain(mWindow->getViewport(0)); + + bool hasCompositor = chain->getCompositor ("gbufferFinalizer"); + + + if (!hasCompositor) + { + mWindow->getViewport(0)->setClearEveryFrame(false); + } + else + { + mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); + mRectangle->setVisible(true); + + for (unsigned int i = 0; igetNumCompositors(); ++i) + { + Ogre::CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0), chain->getCompositor(i)->getCompositor()->getName(), false); + } + } + mWindow->update(); - mWindow->getViewport(0)->setClearEveryFrame(true); + + if (!hasCompositor) + mWindow->getViewport(0)->setClearEveryFrame(true); + else + { + for (unsigned int i = 0; igetNumCompositors(); ++i) + { + Ogre::CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0), chain->getCompositor(i)->getCompositor()->getName(), true); + } + mRectangle->setVisible(false); + } // resume 3d rendering mSceneMgr->clearSpecialCaseRenderQueues(); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 2256bca6b..abd59db49 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -8,7 +8,6 @@ namespace MWGui { - class LoadingScreen : public WindowBase { public: @@ -17,6 +16,8 @@ namespace MWGui void setLoadingProgress (const std::string& stage, int depth, int current, int total); + void onResChange(int w, int h); + private: Ogre::SceneManager* mSceneMgr; Ogre::RenderWindow* mWindow; @@ -26,6 +27,7 @@ namespace MWGui MyGUI::TextBox* mLoadingText; MyGUI::ProgressBar* mProgressBar; + MyGUI::ImageBox* mBackgroundImage; int mCurrentCellLoading; int mTotalCellsLoading; @@ -33,6 +35,9 @@ namespace MWGui int mTotalRefsLoading; int mCurrentRefList; + Ogre::Rectangle2D* mRectangle; + Ogre::MaterialPtr mBackgroundMaterial; + bool mLoadingOn; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 196e898b0..8d12950a0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -149,7 +149,7 @@ WindowManager::WindowManager( mQuickKeysMenu = new QuickKeysMenu(*this); mLoadingScreen = new LoadingScreen(mOgre->getScene (), mOgre->getWindow (), *this); - mLoadingScreen->setCoord (0,0,w,h); + mLoadingScreen->onResChange (w,h); mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); @@ -684,7 +684,7 @@ void WindowManager::processChangedSettings(const Settings::CategorySettingVector mBookWindow->center(); mQuickKeysMenu->center(); mSpellBuyingWindow->center(); - mLoadingScreen->setCoord (0,0,x,y); + mLoadingScreen->onResChange (x,y); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); mInputBlocker->setSize(MyGUI::IntSize(x,y)); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e983353cb..70d7de552 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -734,6 +734,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec sh::Factory::getInstance ().setGlobalSetting ("mrt_output", useMRT() ? "true" : "false"); sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); mObjects.rebuildStaticGeometry (); + mRendering.getViewport ()->setClearEveryFrame (true); } else if (it->second == "underwater effect" && it->first == "Water") { diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index 7096115f6..6862702ca 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -4,13 +4,17 @@ - + - - + + + + + + + + - - From e9e30c0fce60482cc11f46c5021e383dd26e6565 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 11 Sep 2012 20:34:54 +0200 Subject: [PATCH 577/688] fixed a memory leak --- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8d12950a0..b29980b21 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -199,6 +199,7 @@ WindowManager::~WindowManager() delete mConfirmationDialog; delete mAlchemyWindow; delete mSpellWindow; + delete mLoadingScreen; cleanupGarbage(); From c3966a7c5adfe3ec84272be32fd149f2a79d1ac3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Sep 2012 22:45:41 +0200 Subject: [PATCH 578/688] oops, I used a float texture for water reflection. this change should give a little performance boost. --- apps/openmw/mwrender/water.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index e79f308cd..7100540fc 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -309,7 +309,7 @@ void Water::applyRTT() if (Settings::Manager::getBool("shader", "Water")) { mReflectionTexture = TextureManager::getSingleton().createManual("WaterReflection", - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, rttsize, rttsize, 0, PF_FLOAT16_RGBA, TU_RENDERTARGET); + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, rttsize, rttsize, 0, PF_A8R8G8B8, TU_RENDERTARGET); RenderTarget* rtt = mReflectionTexture->getBuffer()->getRenderTarget(); Viewport* vp = rtt->addViewport(mReflectionCamera); From 906d290935904a85d87f760eaaf14324eec3fab0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Sep 2012 12:41:12 +0200 Subject: [PATCH 579/688] Markers are actually hidden now. Inspecting the markers in NifSkope revealed why it didn't work previously: the flag that is being looked for is not present in any of the markers, nor any other flag or extra data to identify them. However, the root node name always starts with "marker", making it possible to do a string search. --- components/nifbullet/bullet_nif_loader.cpp | 5 +++++ components/nifogre/ogre_nif_loader.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 071f03630..b70404635 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -164,6 +164,11 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // the flags we currently use, at least. flags |= node->flags; + // Marker objects: just skip the entire node + /// \todo don't do this in the editor + if (node->name.find("marker") != std::string::npos) + return; + // Check for extra data Nif::Extra *e = node; while (!e->extra.empty()) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 5a5a614ef..5127af966 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -940,6 +940,11 @@ public: { flags |= node->flags; + // Marker objects: just skip the entire node + /// \todo don't do this in the editor + if (node->name.find("marker") != std::string::npos) + return; + Nif::ExtraPtr e = node->extra; while(!e.empty()) { From a96ed6ac61fe4cc2ddce70c9437389b1b1b44982 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Sep 2012 19:15:29 +0200 Subject: [PATCH 580/688] splash screens on startup --- apps/openmw/mwgui/loadingscreen.cpp | 35 +++++++++++++++++++++++++- apps/openmw/mwgui/loadingscreen.hpp | 2 ++ apps/openmw/mwgui/mode.hpp | 3 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 9 +++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index d93cc50c3..c25fad6bc 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -11,6 +11,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/inputmanager.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { @@ -20,6 +22,7 @@ namespace MWGui , WindowBase("openmw_loading_screen.layout", parWindowManager) , mLoadingOn(false) , mLastRenderTime(0.f) + , mFirstLoad(true) { getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); @@ -139,7 +142,8 @@ namespace MWGui } else { - mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); + if (!mFirstLoad) + mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); mRectangle->setVisible(true); for (unsigned int i = 0; igetNumCompositors(); ++i) @@ -171,6 +175,30 @@ namespace MWGui { setVisible(true); mLoadingOn = true; + + if (mFirstLoad) + { + /// \todo use a directory listing here + std::vector splash; + splash.push_back ("Splash/Splash_Bonelord.tga"); + splash.push_back ("Splash/Splash_ClannDaddy.tga"); + splash.push_back ("Splash/Splash_ClannFear.tga"); + splash.push_back ("Splash/Splash_Daedroth.tga"); + splash.push_back ("Splash/Splash_Hunger.tga"); + splash.push_back ("Splash/Splash_KwamaWarrior.tga"); + splash.push_back ("Splash/Splash_Netch.tga"); + splash.push_back ("Splash/Splash_NixHound.tga"); + splash.push_back ("Splash/Splash_Siltstriker.tga"); + splash.push_back ("Splash/Splash_Skeleton.tga"); + splash.push_back ("Splash/Splash_SphereCenturion.tga"); + + mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(splash[rand() % splash.size()]); + mRectangle->setVisible(true); + + mWindowManager.pushGuiMode(GM_LoadingWallpaper); + } + else + mWindowManager.pushGuiMode(GM_Loading); } @@ -178,5 +206,10 @@ namespace MWGui { setVisible(false); mLoadingOn = false; + mFirstLoad = false; + mRectangle->setVisible(false); + + mWindowManager.removeGuiMode(GM_Loading); + mWindowManager.removeGuiMode(GM_LoadingWallpaper); } } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index abd59db49..5cee37a51 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -19,6 +19,8 @@ namespace MWGui void onResChange(int w, int h); private: + bool mFirstLoad; + Ogre::SceneManager* mSceneMgr; Ogre::RenderWindow* mWindow; diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 4417f7b9c..7e1adcf8b 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -35,6 +35,9 @@ namespace MWGui // interactive MessageBox GM_InterMessageBox, + GM_Loading, + GM_LoadingWallpaper, + GM_QuickKeysMenu }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index b29980b21..a4dd54e78 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -248,6 +248,8 @@ void WindowManager::updateVisible() mSpellWindow->setVisible(false); mQuickKeysMenu->setVisible(false); + mHud->setVisible(true); + // Mouse is visible whenever we're not in game mode MyGUI::PointerManager::getInstance().setVisible(isGuiMode()); @@ -340,6 +342,13 @@ void WindowManager::updateVisible() case GM_Journal: mJournal->setVisible(true); break; + case GM_LoadingWallpaper: + mHud->setVisible(false); + MyGUI::PointerManager::getInstance().setVisible(false); + break; + case GM_Loading: + MyGUI::PointerManager::getInstance().setVisible(false); + break; default: // Unsupported mode, switch back to game break; From 2f0b47fc38314073388030c678ac829a80cea0d6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Sep 2012 00:21:58 +0200 Subject: [PATCH 581/688] test --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/loadingscreen.cpp | 34 ++++++++++++++------------ apps/openmw/mwgui/loadingscreen.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++++ apps/openmw/mwgui/windowmanagerimp.hpp | 1 + apps/openmw/mwrender/sky.cpp | 3 --- apps/openmw/mwrender/sky.hpp | 1 - apps/openmw/mwworld/scene.cpp | 5 +++- 8 files changed, 30 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index bd8b7eb18..58fe180e9 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -218,6 +218,7 @@ namespace MWBase virtual void executeInConsole (const std::string& path) = 0; virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0; + virtual void loadingDone() = 0; }; } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index c25fad6bc..a56075d4d 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -80,11 +80,7 @@ namespace MWGui mTotalRefsLoading = total; } - if (mTotalCellsLoading == 0) - { - loadingOff(); - return; - } + assert (mTotalCellsLoading != 0); float refProgress; if (mTotalRefsLoading <= 1) @@ -101,11 +97,6 @@ namespace MWGui float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading); assert(progress <= 1 && progress >= 0); - if (progress >= 1) - { - loadingOff(); - return; - } mLoadingText->setCaption(stage + "... "); mProgressBar->setProgressPosition (static_cast(progress * 1000)); @@ -138,13 +129,14 @@ namespace MWGui if (!hasCompositor) { - mWindow->getViewport(0)->setClearEveryFrame(false); + //mWindow->getViewport(0)->setClearEveryFrame(false); } else { if (!mFirstLoad) - mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); - mRectangle->setVisible(true); + { + //mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); + } for (unsigned int i = 0; igetNumCompositors(); ++i) { @@ -152,6 +144,9 @@ namespace MWGui } } + mRectangle->setVisible(hasCompositor || mFirstLoad); + std::cout << "rect vis? " << mRectangle->getVisible () << " first load? " << mFirstLoad << std::endl; + mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("Splash/Splash_Bonelord.tga"); mWindow->update(); if (!hasCompositor) @@ -162,17 +157,24 @@ namespace MWGui { Ogre::CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0), chain->getCompositor(i)->getCompositor()->getName(), true); } - mRectangle->setVisible(false); } + mRectangle->setVisible(false); + // resume 3d rendering mSceneMgr->clearSpecialCaseRenderQueues(); mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); } } + void LoadingScreen::loadingDone() + { + loadingOff(); + } + void LoadingScreen::loadingOn() { + std::cout << "loading on " <setVisible(false); + //mFirstLoad = false; mWindowManager.removeGuiMode(GM_Loading); mWindowManager.removeGuiMode(GM_LoadingWallpaper); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 5cee37a51..28d970040 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -15,6 +15,7 @@ namespace MWGui virtual ~LoadingScreen(); void setLoadingProgress (const std::string& stage, int depth, int current, int total); + void loadingDone(); void onResChange(int w, int h); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a4dd54e78..99f476574 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -918,3 +918,8 @@ void WindowManager::setLoadingProgress (const std::string& stage, int depth, int { mLoadingScreen->setLoadingProgress (stage, depth, current, total); } + +void WindowManager::loadingDone () +{ + mLoadingScreen->loadingDone (); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 5327bb868..e16b03e43 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -197,6 +197,7 @@ namespace MWGui virtual void executeInConsole (const std::string& path); virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total); + virtual void loadingDone(); private: OEngine::GUI::MyGUIManager *mGuiManager; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index eba605d0c..60ecd4303 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -11,9 +11,6 @@ #include #include #include -#include -#include -#include #include diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 09d56dbd9..ee1360853 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -24,7 +24,6 @@ namespace Ogre class Entity; class BillboardSet; class TextureUnitState; - class Overlay; } namespace MWRender diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c3843b831..8a4a09358 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -290,7 +290,6 @@ namespace MWWorld mCurrentCell = *iter; - // adjust player playerCellChange (mCurrentCell, position, adjustPlayerPos); @@ -300,6 +299,8 @@ namespace MWWorld mRendering.switchToExterior(); mCellChanged = true; + + MWBase::Environment::get().getWindowManager ()->loadingDone (); } //We need the ogre renderer and a scene node. @@ -369,6 +370,8 @@ namespace MWWorld MWBase::Environment::get().getWorld()->adjustSky(); mCellChanged = true; + + MWBase::Environment::get().getWindowManager ()->loadingDone (); } void Scene::changeToExteriorCell (const ESM::Position& position) From a1a773373007ff4f63d63cf92df07165c53f25dc Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Wed, 12 Sep 2012 18:30:32 -0400 Subject: [PATCH 582/688] Set scale implemented --- apps/openmw/mwworld/physicssystem.cpp | 9 +++++++-- libs/openengine/bullet/physic.cpp | 27 ++++++++++++++++++++++----- libs/openengine/bullet/physic.hpp | 6 +++--- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 4285eeb18..243ec61ba 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -218,9 +218,8 @@ namespace MWWorld std::vector< std::pair > response; for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) { - btVector3 newPos = it->second->getPosition(); - Ogre::Vector3 coord(newPos.x(), newPos.y(), newPos.z()); + Ogre::Vector3 coord = it->second->getPosition(); if(it->first == "player"){ coord = playerphysics->ps.origin ; @@ -332,6 +331,12 @@ namespace MWWorld Ogre::Vector3 vec = node->getPosition(); addObject(handle, handleToMesh[handle], quat, scale, vec); } + + if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) + { + float scale = node->getScale().x; + act->setScale(scale); + } } bool PhysicsSystem::toggleCollisionMode() diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 8fa7681f5..2824da4ca 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -29,7 +29,7 @@ namespace Physic PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale): mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0), mBody(0), collisionMode(false), mBoxRotation(0,0,0,0) { - mBody = mEngine->createAndAdjustRigidBody(mesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, &mBoxRotationInverse); + mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, &mBoxRotationInverse); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map } @@ -74,19 +74,20 @@ namespace Physic //internalGhostObject->getWorldTransform().setRotation( quat ); } - btVector3 PhysicActor::getPosition(void) + Ogre::Vector3 PhysicActor::getPosition(void) { btVector3 vec = mBody->getWorldTransform().getOrigin(); Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(), mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ()); Ogre::Vector3 transrot = rotation * mBoxScaledTranslation; - btVector3 visualPosition = vec - btVector3(transrot.x, transrot.y, transrot.z); + Ogre::Vector3 visualPosition = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()) - transrot; return visualPosition; } - btQuaternion PhysicActor::getRotation(void) + Ogre::Quaternion PhysicActor::getRotation(void) { - return mBody->getWorldTransform().getRotation() * mBoxRotationInverse; + btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse; + return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); } void PhysicActor::setPosition(const btVector3& pos) @@ -95,6 +96,22 @@ namespace Physic //externalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); } + void PhysicActor::setScale(float scale){ + std::cout << "Trying to set scale to " << scale << "\n"; + Ogre::Vector3 position = getPosition(); + Ogre::Quaternion rotation = getRotation(); + //We only need to change the scaled box translation, box rotations remain the same. + mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX(); + mBoxScaledTranslation *= scale; + if(mBody){ + mEngine->dynamicsWorld->removeRigidBody(mBody); + delete mBody; + } + //Create the newly scaled rigid body + mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation); + mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 1ff706146..2568cc36f 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -77,13 +77,13 @@ namespace Physic bool getCollisionMode(); - btVector3 getPosition(void); + Ogre::Vector3 getPosition(void); - btQuaternion getRotation(void); + Ogre::Quaternion getRotation(void); void setPosition(const btVector3& pos); - + void setScale(float scale); std::string mName; From 4f3eb3c34a1aae95d48c2aee0596d6778e29e60c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Sep 2012 00:54:32 +0200 Subject: [PATCH 583/688] character preview WIP --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwgui/inventorywindow.cpp | 21 +++++---- apps/openmw/mwgui/inventorywindow.hpp | 1 + apps/openmw/mwrender/characterpreview.cpp | 52 ++++++++++++++++++++++ apps/openmw/mwrender/characterpreview.hpp | 29 ++++++++++++ apps/openmw/mwrender/npcanimation.cpp | 9 ++-- apps/openmw/mwrender/npcanimation.hpp | 4 +- apps/openmw/mwrender/renderconst.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 11 ++++- apps/openmw/mwrender/renderingmanager.hpp | 5 +++ apps/openmw/mwrender/shadows.cpp | 2 +- apps/openmw/mwrender/water.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 5 +++ apps/openmw/mwworld/worldimp.hpp | 2 + files/mygui/openmw_inventory_window.layout | 8 +++- 16 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 apps/openmw/mwrender/characterpreview.cpp create mode 100644 apps/openmw/mwrender/characterpreview.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index cffbc60c7..047cea760 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows - compositors + compositors characterpreview ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 19405fb7a..5ecd1b1b9 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -271,6 +271,8 @@ namespace MWBase virtual void togglePlayerLooking(bool enable) = 0; virtual void renderPlayer() = 0; + + virtual void updateCharacterPreview(int sizeX, int sizeY) = 0; }; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 6dc19944f..cf23ecd8f 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -48,6 +48,7 @@ namespace MWGui static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); getWidget(mAvatar, "Avatar"); + getWidget(mAvatarImage, "AvatarImage"); getWidget(mEncumbranceBar, "EncumbranceBar"); getWidget(mEncumbranceText, "EncumbranceBarT"); getWidget(mFilterAll, "AllButton"); @@ -100,6 +101,13 @@ namespace MWGui _sender->getSize().width - 12 - (_sender->getSize().height-44) * aspect - 15, _sender->getSize().height-44 ); drawItems(); + + MyGUI::IntSize size = mAvatarImage->getSize(); + + std::cout << "dims " << size.width << " " << size.height << std::endl; + MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); + + mAvatarImage->setImageCoord (MyGUI::IntCoord(0,0,size.width, size.height)); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -171,14 +179,7 @@ namespace MWGui drawItems(); - // update selected weapon icon - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator weaponSlot = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (weaponSlot == invStore.end()) - mWindowManager.unsetSelectedWeapon(); - else - mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability - + notifyContentChanged(); } } @@ -265,6 +266,10 @@ namespace MWGui mWindowManager.unsetSelectedWeapon(); else mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability + + MyGUI::IntSize size = mAvatarImage->getSize(); + mAvatarImage->setImageCoord (MyGUI::IntCoord(0,0,size.width, size.height)); + MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index fbdb79977..4a385dd71 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -24,6 +24,7 @@ namespace MWGui protected: MyGUI::Widget* mAvatar; + MyGUI::ImageBox* mAvatarImage; MyGUI::TextBox* mArmorRating; MyGUI::ProgressBar* mEncumbranceBar; MyGUI::TextBox* mEncumbranceText; diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp new file mode 100644 index 000000000..3d3f0a58d --- /dev/null +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -0,0 +1,52 @@ +#include "characterpreview.hpp" + + +#include +#include + + +#include "renderconst.hpp" + +namespace MWRender +{ + + CharacterPreview::CharacterPreview(Ogre::SceneManager *sceneMgr, Ogre::SceneNode *node) + : mSceneMgr(sceneMgr) + { + mCamera = mSceneMgr->createCamera ("CharacterPreviewCamera"); + mCamera->setAspectRatio (0.5); + + mNode = node->createChildSceneNode (); + mNode->attachObject (mCamera); + + mNode->setPosition(0, 185, 70); + mNode->roll(Ogre::Degree(180)); + + mNode->pitch(Ogre::Degree(90)); + + mTexture = Ogre::TextureManager::getSingleton().createManual("CharacterPreview", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, 512, 1024, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); + + mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + mViewport = mRenderTarget->addViewport(mCamera); + mViewport->setOverlaysEnabled(false); + mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); + mViewport->setShadowsEnabled(false); + mViewport->setMaterialScheme("local_map"); + mViewport->setVisibilityMask (RV_Player); + mRenderTarget->setActive(true); + mRenderTarget->setAutoUpdated (false); + } + + void CharacterPreview::update(int sizeX, int sizeY) + { + bool wasVisible = mNode->getParentSceneNode()->getAttachedObject(0)->getVisible (); + mNode->getParentSceneNode()->setVisible(true, false); + //mViewport->setDimensions (0, 0, float(sizeX) / float(512), float(sizeY) / float(1024)); + + mRenderTarget->update(); + + mNode->getParentSceneNode()->setVisible(wasVisible, false); + } + +} diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp new file mode 100644 index 000000000..e15d6c359 --- /dev/null +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -0,0 +1,29 @@ +#ifndef MWRENDER_CHARACTERPREVIEW_H +#define MWRENDER_CHARACTERPREVIEW_H + +#include + +namespace MWRender +{ + + class CharacterPreview + { + public: + CharacterPreview(Ogre::SceneManager* sceneMgr, Ogre::SceneNode* node); + + void update(int sizeX, int sizeY); + + private: + Ogre::TexturePtr mTexture; + Ogre::RenderTarget* mRenderTarget; + Ogre::Viewport* mViewport; + + Ogre::Camera* mCamera; + + Ogre::SceneManager* mSceneMgr; + Ogre::SceneNode* mNode; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 4f98aebc4..392a79c96 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -45,7 +45,7 @@ NpcAnimation::~NpcAnimation() } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv) +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv, bool player) : Animation(_rend), mStateID(-1), mInv(_inv), timeToChange(0), robe(mInv.end()), helmet(mInv.end()), shirt(mInv.end()), cuirass(mInv.end()), greaves(mInv.end()), @@ -53,6 +53,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere boots(mInv.end()), leftglove(mInv.end()), rightglove(mInv.end()), skirtiter(mInv.end()), pants(mInv.end()) + , mIsPlayer(player) { MWWorld::LiveCellRef *ref = ptr.get(); @@ -94,7 +95,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere { Ogre::Entity *base = mEntityList.mEntities[i]; - base->setVisibilityFlags(RV_Actors); + base->setVisibilityFlags(mIsPlayer ? RV_Player : RV_Actors); bool transparent = false; for(unsigned int j=0;j < base->getNumSubEntities();++j) { @@ -357,13 +358,13 @@ NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, con mInsert, mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) - parts[i]->setVisibilityFlags(RV_Actors); + parts[i]->setVisibilityFlags(mIsPlayer ? RV_Player : RV_Actors); return entities; } void NpcAnimation::runAnimation(float timepassed) { - if(timeToChange > .2) + //if(timeToChange > .2) { timeToChange = 0; updateParts(); diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index d4b2a5b9e..945dd210e 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -18,6 +18,8 @@ private: int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty int mPartPriorities[27]; + bool mIsPlayer; + //Bounded Parts NifOgre::EntityList lclavicle; NifOgre::EntityList rclavicle; @@ -66,7 +68,7 @@ private: MWWorld::ContainerStoreIterator skirtiter; public: - NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); + NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv, bool player=false); virtual ~NpcAnimation(); NifOgre::EntityList insertBoundedPart(const std::string &mesh, const std::string &bonename); virtual void runAnimation(float timepassed); diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 9f57833bb..194ca9da6 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -41,7 +41,7 @@ enum VisibilityFlags // Water RV_Water = 8, - // Actors (player, npcs, creatures) + // Actors (npcs, creatures) RV_Actors = 16, // Misc objects (containers, dynamic objects) @@ -54,6 +54,8 @@ enum VisibilityFlags RV_OcclusionQuery = 256, + RV_Player = 512, + RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water /// \todo markers (normally hidden) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 70d7de552..acb587d03 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -34,6 +34,7 @@ #include "water.hpp" #include "compositors.hpp" #include "npcanimation.hpp" +#include "characterpreview.hpp" using namespace MWRender; using namespace Ogre; @@ -159,6 +160,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mDebugging = new Debugging(mMwRoot, engine); mLocalMap = new MWRender::LocalMap(&mRendering, this); + mCharacterPreview = new CharacterPreview(mRendering.getScene (), playerNode); + setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } @@ -175,6 +178,7 @@ RenderingManager::~RenderingManager () delete mOcclusionQuery; delete mCompositors; delete mWater; + delete mCharacterPreview; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -860,7 +864,7 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) new MWRender::NpcAnimation( ptr, mRendering, - MWWorld::Class::get(ptr).getInventoryStore(ptr) + MWWorld::Class::get(ptr).getInventoryStore(ptr), true ); mPlayer->setAnimation(anim); } @@ -882,4 +886,9 @@ bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, boo return mLocalMap->isPositionExplored(nX, nY, x, y, interior); } +void RenderingManager::updateCharacterPreview (int sizeX, int sizeY) +{ + mCharacterPreview->update(sizeX, sizeY); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index e5be238bd..fe79c9400 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -43,6 +43,7 @@ namespace MWRender class LocalMap; class Water; class Compositors; + class CharacterPreview; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -191,6 +192,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored + void updateCharacterPreview(int sizeX, int sizeY); + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); @@ -213,6 +216,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList TerrainManager* mTerrainManager; + CharacterPreview* mCharacterPreview; + MWRender::Water *mWater; OEngine::Render::OgreRenderer &mRendering; diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 3d9f13243..fc28f1cb9 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -111,7 +111,7 @@ void Shadows::recreate() sh::Factory::getInstance ().setSharedParameter ("shadowFar_fadeStart", sh::makeProperty(shadowFar_fadeStart)); // Set visibility mask for the shadow render textures - int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows") + int visibilityMask = (RV_Actors + RV_Player) * Settings::Manager::getBool("actor shadows", "Shadows") + (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows") + RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows"); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 7100540fc..6a3eeb509 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -332,7 +332,7 @@ void Water::applyVisibilityMask() mVisibilityFlags = RV_Terrain * Settings::Manager::getBool("reflect terrain", "Water") + RV_Statics * Settings::Manager::getBool("reflect statics", "Water") + RV_StaticsSmall * Settings::Manager::getBool("reflect small statics", "Water") - + RV_Actors * Settings::Manager::getBool("reflect actors", "Water") + + (RV_Actors + RV_Player) * Settings::Manager::getBool("reflect actors", "Water") + RV_Misc * Settings::Manager::getBool("reflect misc", "Water") + RV_Sky; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 869ce94d1..f88f5c320 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1222,4 +1222,9 @@ namespace MWWorld { mRendering->renderPlayer(mPlayer->getPlayer()); } + + void World::updateCharacterPreview (int sizeX, int sizeY) + { + mRendering->updateCharacterPreview(sizeX, sizeY); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6279343d4..5e8bc697e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -307,6 +307,8 @@ namespace MWWorld } virtual void renderPlayer(); + + virtual void updateCharacterPreview(int sizeX, int sizeY); }; } diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 1ffabe3b6..b9e14fffa 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -12,8 +12,12 @@ - - + + + + + + From f2ab4c929daa35d7401a5347e06bd83f4fb7b210 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 09:26:17 +0200 Subject: [PATCH 584/688] Issue #356: added hardcoded magic effect flags *grumble* --- components/esm/loadmgef.cpp | 29 +++++++++++++++++++++++++++++ components/esm/loadmgef.hpp | 1 + 2 files changed, 30 insertions(+) diff --git a/components/esm/loadmgef.cpp b/components/esm/loadmgef.cpp index 2eda67b61..9aa6b26e3 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -1,5 +1,30 @@ #include "loadmgef.hpp" +namespace +{ + const int NumberOfHardcodedFlags = 143; + const int HardcodedFlags[NumberOfHardcodedFlags] = { + 0x11c8, 0x11c0, 0x11c8, 0x11e0, 0x11e0, 0x11e0, 0x11e0, 0x11d0, + 0x11c0, 0x11c0, 0x11e0, 0x11c0, 0x11184, 0x11184, 0x1f0, 0x1f0, + 0x1f0, 0x11d2, 0x11f0, 0x11d0, 0x11d0, 0x11d1, 0x1d2, 0x1f0, + 0x1d0, 0x1d0, 0x1d1, 0x1f0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, + 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x11d0, 0x1d0, 0x1d0, 0x11c8, + 0x31c0, 0x11c0, 0x11c0, 0x11c0, 0x1180, 0x11d8, 0x11d8, 0x11d0, + 0x11d0, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, 0x11180, + 0x11180, 0x11c4, 0x111b8, 0x1040, 0x104c, 0x104c, 0x104c, 0x104c, + 0x1040, 0x1040, 0x1040, 0x11c0, 0x11c0, 0x1cc, 0x1cc, 0x1cc, + 0x1cc, 0x1cc, 0x1c2, 0x1c0, 0x1c0, 0x1c0, 0x1c1, 0x11c2, + 0x11c0, 0x11c0, 0x11c0, 0x11c1, 0x11c0, 0x21192, 0x20190, 0x20190, + 0x20190, 0x21191, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x11c0, + 0x11c0, 0x11c0, 0x11c0, 0x11c0, 0x1c0, 0x11190, 0x9048, 0x9048, + 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, + 0x9048, 0x9048, 0x9048, 0x9048, 0x9048, 0x11c0, 0x1180, 0x1180, + 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x5048, 0x1188, 0x5048, + 0x5048, 0x5048, 0x5048, 0x5048, 0x1048, 0x104c, 0x1048, 0x40, + 0x11c8, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048, 0x1048 + }; +} + namespace ESM { @@ -8,6 +33,10 @@ void MagicEffect::load(ESMReader &esm) esm.getHNT(index, "INDX"); esm.getHNT(data, "MEDT", 36); + + if (index>=0 && index Date: Thu, 13 Sep 2012 09:30:47 +0200 Subject: [PATCH 585/688] Issue #356: basic support for ingredients in ActiveSpells (still using the wrong formula) --- apps/openmw/mwmechanics/activespells.cpp | 49 ++++++++++++++++++------ apps/openmw/mwmechanics/activespells.hpp | 2 +- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 94b98d3be..5361a52c8 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include #include @@ -46,13 +48,13 @@ namespace MWMechanics for (TIterator iter (begin()); iter!=end(); ++iter) { - const ESM::EffectList& effects = getEffectList (iter->first); + std::pair effects = getEffectList (iter->first); const MWWorld::TimeStamp& start = iter->second.first; float magnitude = iter->second.second; - for (std::vector::const_iterator iter (effects.list.begin()); - iter!=effects.list.end(); ++iter) + for (std::vector::const_iterator iter (effects.first.list.begin()); + iter!=effects.first.list.end(); ++iter) { if (iter->duration) { @@ -73,15 +75,38 @@ namespace MWMechanics } } - const ESM::EffectList& ActiveSpells::getEffectList (const std::string& id) const + std::pair ActiveSpells::getEffectList (const std::string& id) const { if (const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().spells.search (id)) - return spell->effects; + return std::make_pair (spell->effects, false); if (const ESM::Potion *potion = MWBase::Environment::get().getWorld()->getStore().potions.search (id)) - return potion->effects; + return std::make_pair (potion->effects, false); + + if (const ESM::Ingredient *ingredient = + MWBase::Environment::get().getWorld()->getStore().ingreds.search (id)) + { + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().magicEffects.find ( + ingredient->data.effectID[0]); + + ESM::ENAMstruct effect; + effect.effectID = ingredient->data.effectID[0]; + effect.skill = ingredient->data.skills[0]; + effect.attribute = ingredient->data.attributes[0]; + effect.range = 0; + effect.area = 0; + effect.duration = magicEffect->data.flags & ESM::MagicEffect::NoDuration ? 0 : 1; + effect.magnMin = 0; + effect.magnMax = 0; + + std::pair result; + + result.first.list.push_back (effect); + result.second = true; + } throw std::runtime_error ("ID " + id + " can not produce lasting effects"); } @@ -92,12 +117,12 @@ namespace MWMechanics bool ActiveSpells::addSpell (const std::string& id) { - const ESM::EffectList& effects = getEffectList (id); + std::pair effects = getEffectList (id); bool found = false; - for (std::vector::const_iterator iter (effects.list.begin()); - iter!=effects.list.end(); ++iter) + for (std::vector::const_iterator iter (effects.first.list.begin()); + iter!=effects.first.list.end(); ++iter) { if (iter->duration) { @@ -155,12 +180,12 @@ namespace MWMechanics double ActiveSpells::timeToExpire (const TIterator& iterator) const { - const ESM::EffectList& effects = getEffectList (iterator->first); + std::pair effects = getEffectList (iterator->first); int duration = 0; - for (std::vector::const_iterator iter (effects.list.begin()); - iter!=effects.list.end(); ++iter) + for (std::vector::const_iterator iter (effects.first.list.begin()); + iter!=effects.first.list.end(); ++iter) { if (iter->duration>duration) duration = iter->duration; diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 2226cea84..6a1205bfb 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -37,7 +37,7 @@ namespace MWMechanics void update() const; - const ESM::EffectList& getEffectList (const std::string& id) const; + std::pair getEffectList (const std::string& id) const; public: From 677158c477da1fa08ad9353430eb9b1511c24b5e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 10:41:55 +0200 Subject: [PATCH 586/688] added typesafe access functions for GMST values --- components/esm/loadgmst.cpp | 35 +++++++++++++++++++++++++++++++++++ components/esm/loadgmst.hpp | 9 +++++++++ 2 files changed, 44 insertions(+) diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index 677642e31..4bd464da6 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -1,8 +1,15 @@ #include "loadgmst.hpp" +#include + +#include "defs.hpp" + namespace ESM { +/// \todo Review GMST "fixing". Probably remove completely or at least make it optional. Its definitely not +/// working properly in its current state and I doubt it can be fixed without breaking other stuff. + // Some handy macros #define cI(s,x) { if(id == (s)) return (i == (x)); } #define cF(s,x) { if(id == (s)) return (f == (x)); } @@ -169,4 +176,32 @@ void GameSetting::load(ESMReader &esm) dirty = true; } +int GameSetting::getInt() const +{ + switch (type) + { + case VT_Float: return static_cast (f); + case VT_Int: return i; + default: throw std::runtime_error ("GMST " + id + " is not of a numeric type"); + } +} + +int GameSetting::getFloat() const +{ + switch (type) + { + case VT_Float: return f; + case VT_Int: return i; + default: throw std::runtime_error ("GMST " + id + " is not of a numeric type"); + } +} + +std::string GameSetting::getString() const +{ + if (type==VT_String) + return str; + + throw std::runtime_error ("GMST " + id + " is not a string"); +} + } diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index 01fbc3067..f63028731 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -83,6 +83,15 @@ struct GameSetting bool isDirtyBloodmoon(); void load(ESMReader &esm); + + int getInt() const; + ///< Throws an exception if GMST is not of type int or float. + + int getFloat() const; + ///< Throws an exception if GMST is not of type int or float. + + std::string getString() const; + ///< Throwns an exception if GMST is not of type string. }; } #endif From 9dcf8939e96edd7e1b988b8d5d65af9357b256ae Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 10:45:32 +0200 Subject: [PATCH 587/688] Issue #356: added function for fatigue term calculation --- apps/openmw/mwmechanics/creaturestats.cpp | 20 ++++++++++++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 3 +++ 2 files changed, 23 insertions(+) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 09603bff2..49e5bb390 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -1,5 +1,12 @@ #include "creaturestats.hpp" +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + namespace MWMechanics { CreatureStats::CreatureStats() @@ -55,4 +62,17 @@ namespace MWMechanics { return mAiSequence; } + + float CreatureStats::getFatigueTerm() const + { + int max = getFatigue().getModified(); + int current = getFatigue().getCurrent(); + + float normalised = max==0 ? 1 : std::max (0.0f, static_cast (current)/max); + + const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + return store.gameSettings.find ("fFatigueBase")->getFloat() + - store.gameSettings.find ("fFatigueMult")->getFloat() * (1-normalised); + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index d19972e7b..bb6ec9451 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -106,6 +106,9 @@ namespace MWMechanics const AiSequence& getAiSequence() const; AiSequence& getAiSequence(); + + float getFatigueTerm() const; + ///< Return effective fatigue }; // Inline const getters From 2934987f78a556e0451bc8498f597af37169b51a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 10:52:34 +0200 Subject: [PATCH 588/688] Issue #356: Some CreatureStats cleanup --- apps/openmw/mwmechanics/creaturestats.cpp | 172 +++++++++++++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 178 ---------------------- 2 files changed, 172 insertions(+), 178 deletions(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 49e5bb390..0d8dab0f5 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -75,4 +75,176 @@ namespace MWMechanics return store.gameSettings.find ("fFatigueBase")->getFloat() - store.gameSettings.find ("fFatigueMult")->getFloat() * (1-normalised); } + + const Stat &CreatureStats::getAttribute(int index) const + { + if (index < 0 || index > 7) { + throw std::runtime_error("attribute index is out of range"); + } + return mAttributes[index]; + } + + const DynamicStat &CreatureStats::getHealth() const + { + return mDynamic[0]; + } + + const DynamicStat &CreatureStats::getMagicka() const + { + return mDynamic[1]; + } + + const DynamicStat &CreatureStats::getFatigue() const + { + return mDynamic[2]; + } + + const Spells &CreatureStats::getSpells() const + { + return mSpells; + } + + const ActiveSpells &CreatureStats::getActiveSpells() const + { + return mActiveSpells; + } + + const MagicEffects &CreatureStats::getMagicEffects() const + { + return mMagicEffects; + } + + int CreatureStats::getLevel() const + { + return mLevel; + } + + int CreatureStats::getHello() const + { + return mHello; + } + + int CreatureStats::getFight() const + { + return mFight; + } + + int CreatureStats::getFlee() const + { + return mFlee; + } + + int CreatureStats::getAlarm() const + { + return mAlarm; + } + + Stat &CreatureStats::getAttribute(int index) + { + if (index < 0 || index > 7) { + throw std::runtime_error("attribute index is out of range"); + } + return mAttributes[index]; + } + + DynamicStat &CreatureStats::getHealth() + { + return mDynamic[0]; + } + + DynamicStat &CreatureStats::getMagicka() + { + return mDynamic[1]; + } + + DynamicStat &CreatureStats::getFatigue() + { + return mDynamic[2]; + } + + DynamicStat &CreatureStats::getDynamic(int index) + { + if (index < 0 || index > 2) { + throw std::runtime_error("dynamic stat index is out of range"); + } + return mDynamic[index]; + } + + Spells &CreatureStats::getSpells() + { + return mSpells; + } + + void CreatureStats::setSpells(const Spells &spells) + { + mSpells = spells; + } + + ActiveSpells &CreatureStats::getActiveSpells() + { + return mActiveSpells; + } + + MagicEffects &CreatureStats::getMagicEffects() + { + return mMagicEffects; + } + + void CreatureStats::setAttribute(int index, const Stat &value) + { + if (index < 0 || index > 7) { + throw std::runtime_error("attribute index is out of range"); + } + mAttributes[index] = value; + } + + void CreatureStats::setHealth(const DynamicStat &value) + { + mDynamic[0] = value; + } + + void CreatureStats::setMagicka(const DynamicStat &value) + { + mDynamic[1] = value; + } + + void CreatureStats::setFatigue(const DynamicStat &value) + { + mDynamic[2] = value; + } + + void CreatureStats::setLevel(int level) + { + mLevel = level; + } + + void CreatureStats::setActiveSpells(const ActiveSpells &active) + { + mActiveSpells = active; + } + + void CreatureStats::setMagicEffects(const MagicEffects &effects) + { + mMagicEffects = effects; + } + + void CreatureStats::setHello(int value) + { + mHello = value; + } + + void CreatureStats::setFight(int value) + { + mFight = value; + } + + void CreatureStats::setFlee(int value) + { + mFlee = value; + } + + void CreatureStats::setAlarm(int value) + { + mAlarm = value; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index bb6ec9451..d8d0f957a 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -110,184 +110,6 @@ namespace MWMechanics float getFatigueTerm() const; ///< Return effective fatigue }; - - // Inline const getters - - inline const Stat & - CreatureStats::getAttribute(int index) const { - if (index < 0 || index > 7) { - throw std::runtime_error("attribute index is out of range"); - } - return mAttributes[index]; - } - - inline const DynamicStat & - CreatureStats::getHealth() const { - return mDynamic[0]; - } - - inline const DynamicStat & - CreatureStats::getMagicka() const { - return mDynamic[1]; - } - - inline const DynamicStat & - CreatureStats::getFatigue() const { - return mDynamic[2]; - } - - inline const Spells & - CreatureStats::getSpells() const { - return mSpells; - } - - inline const ActiveSpells & - CreatureStats::getActiveSpells() const { - return mActiveSpells; - } - - inline const MagicEffects & - CreatureStats::getMagicEffects() const { - return mMagicEffects; - } - - inline int - CreatureStats::getLevel() const { - return mLevel; - } - - inline int - CreatureStats::getHello() const { - return mHello; - } - - inline int - CreatureStats::getFight() const { - return mFight; - } - - inline int - CreatureStats::getFlee() const { - return mFlee; - } - - inline int - CreatureStats::getAlarm() const { - return mAlarm; - } - - // Inline non-const getters - - inline Stat & - CreatureStats::getAttribute(int index) { - if (index < 0 || index > 7) { - throw std::runtime_error("attribute index is out of range"); - } - return mAttributes[index]; - } - - inline DynamicStat & - CreatureStats::getHealth() { - return mDynamic[0]; - } - - inline DynamicStat & - CreatureStats::getMagicka() { - return mDynamic[1]; - } - - inline DynamicStat & - CreatureStats::getFatigue() { - return mDynamic[2]; - } - - inline DynamicStat & - CreatureStats::getDynamic(int index) { - if (index < 0 || index > 2) { - throw std::runtime_error("dynamic stat index is out of range"); - } - return mDynamic[index]; - } - - inline Spells & - CreatureStats::getSpells() { - return mSpells; - } - - inline void - CreatureStats::setSpells(const Spells &spells) { - mSpells = spells; - } - - inline ActiveSpells & - CreatureStats::getActiveSpells() { - return mActiveSpells; - } - - inline MagicEffects & - CreatureStats::getMagicEffects() { - return mMagicEffects; - } - - // Inline setters - - inline void - CreatureStats::setAttribute(int index, const Stat &value) { - if (index < 0 || index > 7) { - throw std::runtime_error("attribute index is out of range"); - } - mAttributes[index] = value; - } - - inline void - CreatureStats::setHealth(const DynamicStat &value) { - mDynamic[0] = value; - } - - inline void - CreatureStats::setMagicka(const DynamicStat &value) { - mDynamic[1] = value; - } - - inline void - CreatureStats::setFatigue(const DynamicStat &value) { - mDynamic[2] = value; - } - - inline void - CreatureStats::setLevel(int level) { - mLevel = level; - } - - inline void - CreatureStats::setActiveSpells(const ActiveSpells &active) { - mActiveSpells = active; - } - - inline void - CreatureStats::setMagicEffects(const MagicEffects &effects) { - mMagicEffects = effects; - } - - inline void - CreatureStats::setHello(int value) { - mHello = value; - } - - inline void - CreatureStats::setFight(int value) { - mFight = value; - } - - inline void - CreatureStats::setFlee(int value) { - mFlee = value; - } - - inline void - CreatureStats::setAlarm(int value) { - mAlarm = value; - } } #endif From 0af0dc0d5de485872b04b74764820654448126af Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 11:13:05 +0200 Subject: [PATCH 589/688] Issue #356: More CreatureStats cleanup --- apps/openmw/mwmechanics/creaturestats.cpp | 46 +---------------------- apps/openmw/mwmechanics/creaturestats.hpp | 5 --- 2 files changed, 1 insertion(+), 50 deletions(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 0d8dab0f5..91a9225fe 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -8,51 +8,7 @@ #include "../mwbase/world.hpp" namespace MWMechanics -{ - CreatureStats::CreatureStats() - {} - - // Can't use all benefits of members initialization because of - // lack of copy constructors - CreatureStats::CreatureStats(const CreatureStats &orig) - : mLevel(orig.mLevel), mHello(orig.mHello), mFight(orig.mFight), - mFlee(orig.mFlee), mAlarm(orig.mAlarm) - { - for (int i = 0; i < 8; ++i) { - mAttributes[i] = orig.mAttributes[i]; - } - for (int i = 0; i < 3; ++i) { - mDynamic[i] = orig.mDynamic[i]; - } - mSpells = orig.mSpells; - mActiveSpells = orig.mActiveSpells; - mMagicEffects = orig.mMagicEffects; - } - - CreatureStats::~CreatureStats() - {} - - const CreatureStats & - CreatureStats::operator=(const CreatureStats &orig) - { - for (int i = 0; i < 8; ++i) { - mAttributes[i] = orig.mAttributes[i]; - } - for (int i = 0; i < 3; ++i) { - mDynamic[i] = orig.mDynamic[i]; - } - mLevel = orig.mLevel; - mSpells = orig.mSpells; - mActiveSpells = orig.mActiveSpells; - mMagicEffects = orig.mMagicEffects; - mHello = orig.mHello; - mFight = orig.mFight; - mFlee = orig.mFlee; - mAlarm = orig.mAlarm; - - return *this; - } - +{ const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index d8d0f957a..a6fb6779a 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -31,11 +31,6 @@ namespace MWMechanics AiSequence mAiSequence; public: - CreatureStats(); - CreatureStats(const CreatureStats &); - virtual ~CreatureStats(); - - const CreatureStats & operator=(const CreatureStats &); const Stat & getAttribute(int index) const; From 9de7b3bf90d5a5c6691226276faaea9c4b71d51a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 11:30:59 +0200 Subject: [PATCH 590/688] Issue #356: Properly support ingredients effect in ActiveSpells (hopefully) --- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwmechanics/activespells.cpp | 101 +++++++++++++++++------ apps/openmw/mwmechanics/activespells.hpp | 9 +- 3 files changed, 87 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8aab9da56..063f39d2a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -365,7 +365,7 @@ namespace MWClass /// \todo consider instant effects - return stats.getActiveSpells().addSpell (id); + return stats.getActiveSpells().addSpell (id, actor); } void Npc::skillUsageSucceeded (const MWWorld::Ptr& ptr, int skill, int usageType) const diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 5361a52c8..b8273093c 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -7,12 +7,18 @@ #include #include #include +#include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/class.hpp" + +#include "creaturestats.hpp" +#include "npcstats.hpp" + namespace MWMechanics { void ActiveSpells::update() const @@ -43,36 +49,66 @@ namespace MWMechanics } if (rebuild) + rebuildEffects(); + } + + void ActiveSpells::rebuildEffects() const + { + MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); + + mEffects = MagicEffects(); + + for (TIterator iter (begin()); iter!=end(); ++iter) { - mEffects = MagicEffects(); + std::pair effects = getEffectList (iter->first); - for (TIterator iter (begin()); iter!=end(); ++iter) + const MWWorld::TimeStamp& start = iter->second.first; + float magnitude = iter->second.second; + + for (std::vector::const_iterator iter (effects.first.list.begin()); + iter!=effects.first.list.end(); ++iter) { - std::pair effects = getEffectList (iter->first); - - const MWWorld::TimeStamp& start = iter->second.first; - float magnitude = iter->second.second; - - for (std::vector::const_iterator iter (effects.first.list.begin()); - iter!=effects.first.list.end(); ++iter) + if (iter->duration) { - if (iter->duration) - { - MWWorld::TimeStamp end = start; - end += static_cast (iter->duration)* - MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); + int duration = iter->duration; + + if (effects.second) + duration *= magnitude; + + MWWorld::TimeStamp end = start; + end += static_cast (duration)* + MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - if (end>now) + if (end>now) + { + EffectParam param; + + if (effects.second) { - EffectParam param; - param.mMagnitude = static_cast ( - (iter->magnMax-iter->magnMin+1)*magnitude + iter->magnMin); - mEffects.add (*iter, param); + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().magicEffects.find ( + iter->effectID); + + if (iter->duration==0) + { + param.mMagnitude = + static_cast (magnitude / (0.1 * magicEffect->data.baseCost)); + } + else + { + param.mMagnitude = + static_cast (0.05*magnitude / (0.1 * magicEffect->data.baseCost)); + } } + else + param.mMagnitude = static_cast ( + (iter->magnMax-iter->magnMin)*magnitude + iter->magnMin); + + mEffects.add (*iter, param); } } } - } + } } std::pair ActiveSpells::getEffectList (const std::string& id) const @@ -99,8 +135,8 @@ namespace MWMechanics effect.range = 0; effect.area = 0; effect.duration = magicEffect->data.flags & ESM::MagicEffect::NoDuration ? 0 : 1; - effect.magnMin = 0; - effect.magnMax = 0; + effect.magnMin = 1; + effect.magnMax = 1; std::pair result; @@ -115,7 +151,7 @@ namespace MWMechanics : mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} - bool ActiveSpells::addSpell (const std::string& id) + bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor) { std::pair effects = getEffectList (id); @@ -138,6 +174,22 @@ namespace MWMechanics float random = static_cast (std::rand()) / RAND_MAX; + if (effects.second) + { + // ingredient -> special treatment required. + const CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); + const NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor); + + float x = + (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + 0.2 * creatureStats.getAttribute (1).getModified() + + 0.1 * creatureStats.getAttribute (7).getModified()) + * creatureStats.getFatigueTerm(); + random *= 100; + random = random / std::min (x, 100.0f); + random *= 0.25 * x; + } + if (iter==mSpells.end()) mSpells.insert (std::make_pair (id, std::make_pair (MWBase::Environment::get().getWorld()->getTimeStamp(), random))); @@ -191,6 +243,9 @@ namespace MWMechanics duration = iter->duration; } + if (effects.second) + duration *= iterator->second.second; + double scaledDuration = duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 6a1205bfb..e7a239854 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -15,6 +15,11 @@ namespace ESM struct EffectList; } +namespace MWWorld +{ + class Ptr; +} + namespace MWMechanics { /// \brief Lasting spell effects @@ -36,6 +41,8 @@ namespace MWMechanics mutable MWWorld::TimeStamp mLastUpdate; void update() const; + + void rebuildEffects() const; std::pair getEffectList (const std::string& id) const; @@ -43,7 +50,7 @@ namespace MWMechanics ActiveSpells(); - bool addSpell (const std::string& id); + bool addSpell (const std::string& id, const MWWorld::Ptr& actor); ///< Overwrites an existing spell with the same ID. If the spell does not have any /// non-instant effects, it is ignored. /// From 7ad80e306b3292d14c0055895e7bef8093908f01 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Sep 2012 12:33:09 +0200 Subject: [PATCH 591/688] fixed the disappearing --- apps/openmw/engine.cpp | 4 ++ apps/openmw/mwgui/loadingscreen.cpp | 57 +++++++++++++++++------------ apps/openmw/mwgui/loadingscreen.hpp | 3 ++ 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 82bd3b69b..db6edf397 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -162,6 +162,10 @@ void OMW::Engine::loadBSA() dataDirectory = iter->string(); std::cout << "Data dir " << dataDirectory << std::endl; Bsa::addDir(dataDirectory, mFSStrict); + + // Workaround: Mygui does not find textures in non-BSA subfolders, _unless_ they are explicitely added like this + // For splash screens, this is OK to do, but eventually we will need an investigation why this is necessary + Bsa::addDir(dataDirectory + "/Splash", mFSStrict); } } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index a56075d4d..e41f2eec9 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -22,6 +22,7 @@ namespace MWGui , WindowBase("openmw_loading_screen.layout", parWindowManager) , mLoadingOn(false) , mLastRenderTime(0.f) + , mLastWallpaperChangeTime(0.f) , mFirstLoad(true) { getWidget(mLoadingText, "LoadingText"); @@ -107,6 +108,11 @@ namespace MWGui { mLastRenderTime = mTimer.getMilliseconds (); + if (mFirstLoad && mTimer.getMilliseconds () > mLastWallpaperChangeTime + 3000*1) + { + mLastWallpaperChangeTime = mTimer.getMilliseconds (); + changeWallpaper(); + } // Turn off rendering except the GUI mSceneMgr->clearSpecialCaseRenderQueues(); @@ -129,13 +135,14 @@ namespace MWGui if (!hasCompositor) { - //mWindow->getViewport(0)->setClearEveryFrame(false); + mWindow->getViewport(0)->setClearEveryFrame(false); } else { if (!mFirstLoad) { - //mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); + mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(chain->getCompositor ("gbufferFinalizer")->getTextureInstance ("no_mrt_output", 0)->getName()); + mRectangle->setVisible(true); } for (unsigned int i = 0; igetNumCompositors(); ++i) @@ -144,9 +151,6 @@ namespace MWGui } } - mRectangle->setVisible(hasCompositor || mFirstLoad); - std::cout << "rect vis? " << mRectangle->getVisible () << " first load? " << mFirstLoad << std::endl; - mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("Splash/Splash_Bonelord.tga"); mWindow->update(); if (!hasCompositor) @@ -174,44 +178,49 @@ namespace MWGui void LoadingScreen::loadingOn() { - std::cout << "loading on " < splash; - splash.push_back ("Splash/Splash_Bonelord.tga"); - splash.push_back ("Splash/Splash_ClannDaddy.tga"); - splash.push_back ("Splash/Splash_ClannFear.tga"); - splash.push_back ("Splash/Splash_Daedroth.tga"); - splash.push_back ("Splash/Splash_Hunger.tga"); - splash.push_back ("Splash/Splash_KwamaWarrior.tga"); - splash.push_back ("Splash/Splash_Netch.tga"); - splash.push_back ("Splash/Splash_NixHound.tga"); - splash.push_back ("Splash/Splash_Siltstriker.tga"); - splash.push_back ("Splash/Splash_Skeleton.tga"); - splash.push_back ("Splash/Splash_SphereCenturion.tga"); - - mBackgroundMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName(splash[rand() % splash.size()]); - mRectangle->setVisible(true); + changeWallpaper(); mWindowManager.pushGuiMode(GM_LoadingWallpaper); } else + { + mBackgroundImage->setImageTexture(""); mWindowManager.pushGuiMode(GM_Loading); + } } void LoadingScreen::loadingOff() { - std::cout << "loading off " << std::endl; setVisible(false); mLoadingOn = false; - //mFirstLoad = false; + mFirstLoad = false; mWindowManager.removeGuiMode(GM_Loading); mWindowManager.removeGuiMode(GM_LoadingWallpaper); } + + void LoadingScreen::changeWallpaper () + { + /// \todo use a directory listing here + std::vector splash; + splash.push_back ("Splash_Bonelord.tga"); + splash.push_back ("Splash_ClannDaddy.tga"); + splash.push_back ("Splash_Clannfear.tga"); + splash.push_back ("Splash_Daedroth.tga"); + splash.push_back ("Splash_Hunger.tga"); + splash.push_back ("Splash_KwamaWarrior.tga"); + splash.push_back ("Splash_Netch.tga"); + splash.push_back ("Splash_NixHound.tga"); + splash.push_back ("Splash_Siltstriker.tga"); + splash.push_back ("Splash_Skeleton.tga"); + splash.push_back ("Splash_SphereCenturion.tga"); + + mBackgroundImage->setImageTexture (splash[rand() % splash.size()]); + } } diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 28d970040..a012793ca 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -25,6 +25,7 @@ namespace MWGui Ogre::SceneManager* mSceneMgr; Ogre::RenderWindow* mWindow; + unsigned long mLastWallpaperChangeTime; unsigned long mLastRenderTime; Ogre::Timer mTimer; @@ -46,6 +47,8 @@ namespace MWGui void loadingOn(); void loadingOff(); + + void changeWallpaper(); }; } From 2ccd7a480dbe5fa629316599e78ff302badd9cb0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 13:01:59 +0200 Subject: [PATCH 592/688] Issue #356: added a missing return --- apps/openmw/mwmechanics/activespells.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index b8273093c..67e5bad3d 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -142,6 +142,8 @@ namespace MWMechanics result.first.list.push_back (effect); result.second = true; + + return result; } throw std::runtime_error ("ID " + id + " can not produce lasting effects"); From fe68a252d5ba4c7ea0f933f3a719a63fc2eae2d4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 13:02:26 +0200 Subject: [PATCH 593/688] Issue #356: Implemented ingredient eating --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/ingredient.cpp | 17 +++++++++++ apps/openmw/mwclass/ingredient.hpp | 7 +++++ apps/openmw/mwworld/actioneat.cpp | 49 ++++++++++++++++++++++++++++++ apps/openmw/mwworld/actioneat.hpp | 19 ++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 apps/openmw/mwworld/actioneat.cpp create mode 100644 apps/openmw/mwworld/actioneat.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4cf3bd7fd..68dd9b8a8 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -51,7 +51,7 @@ add_openmw_dir (mwworld refdata worldimp physicssystem scene globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellfunctors cells localscripts customdata weather inventorystore ptr actionopen actionread - actionequip timestamp actionalchemy cellstore actionapply + actionequip timestamp actionalchemy cellstore actionapply actioneat ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index f0354de76..4d88be1eb 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -11,6 +11,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/actioneat.hpp" #include "../mwgui/tooltips.hpp" @@ -19,6 +20,14 @@ namespace MWClass { + std::string Ingredient::getId (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->base->mId; + } + void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); @@ -84,6 +93,14 @@ namespace MWClass return ref->base->data.value; } + + boost::shared_ptr Ingredient::use (const MWWorld::Ptr& ptr) const + { + boost::shared_ptr action (new MWWorld::ActionEat (ptr)); + + return action; + } + void Ingredient::registerSelf() { boost::shared_ptr instance (new Ingredient); diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 1365c4a71..0afd202fb 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + virtual std::string getId (const MWWorld::Ptr& ptr) const; + ///< Return ID of \a ptr + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering @@ -37,6 +40,10 @@ namespace MWClass virtual int getValue (const MWWorld::Ptr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) + const; + ///< Generate action for using via inventory menu + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwworld/actioneat.cpp b/apps/openmw/mwworld/actioneat.cpp new file mode 100644 index 000000000..abd1ac4b9 --- /dev/null +++ b/apps/openmw/mwworld/actioneat.cpp @@ -0,0 +1,49 @@ + +#include "actioneat.hpp" + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" + +#include "class.hpp" + +namespace MWWorld +{ + void ActionEat::executeImp (const Ptr& actor) + { + // remove used item + getTarget().getRefData().setCount (getTarget().getRefData().getCount()-1); + + // check for success + const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get (actor).getNpcStats (actor); + + float x = + (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + + 0.2 * creatureStats.getAttribute (1).getModified() + + 0.1 * creatureStats.getAttribute (7).getModified()) + * creatureStats.getFatigueTerm(); + + if (x>=100*static_cast (std::rand()) / RAND_MAX) + { + // apply to actor + std::string id = Class::get (getTarget()).getId (getTarget()); + + Class::get (actor).apply (actor, id, actor); + // we ignore the result here. Skill increases no matter if the ingredient did something or not. + + // increase skill + Class::get (actor).skillUsageSucceeded (actor, ESM::Skill::Alchemy, 1); + } + } + + ActionEat::ActionEat (const MWWorld::Ptr& object) : Action (false, object) {} +} diff --git a/apps/openmw/mwworld/actioneat.hpp b/apps/openmw/mwworld/actioneat.hpp new file mode 100644 index 000000000..ce5330db7 --- /dev/null +++ b/apps/openmw/mwworld/actioneat.hpp @@ -0,0 +1,19 @@ +#ifndef GAME_MWWORLD_ACTIONEAT_H +#define GAME_MWWORLD_ACTIONEAT_H + +#include "action.hpp" +#include "ptr.hpp" + +namespace MWWorld +{ + class ActionEat : public Action + { + virtual void executeImp (const Ptr& actor); + + public: + + ActionEat (const MWWorld::Ptr& object); + }; +} + +#endif From b5ddc8d4fb3af8d050ee852bee63a0380fed947f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Sep 2012 13:10:02 +0200 Subject: [PATCH 594/688] Fix the marker collision; also, nodes marked with NCO are now correctly ignored for collision. --- components/nifbullet/bullet_nif_loader.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index b70404635..0fb758ea8 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -164,10 +164,12 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // the flags we currently use, at least. flags |= node->flags; - // Marker objects: just skip the entire node + // Marker objects: no collision /// \todo don't do this in the editor if (node->name.find("marker") != std::string::npos) - return; + { + flags |= 0x800; + } // Check for extra data Nif::Extra *e = node; @@ -183,12 +185,10 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // affecting the entire subtree of this node Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; - if (sd->string == "NCO" && !raycastingOnly) + if (sd->string == "NCO") { // No collision. Use an internal flag setting to mark this. - // We ignor this node! flags |= 0x800; - return; } else if (sd->string == "MRK" && !raycastingOnly) // Marker objects. These are only visible in the @@ -234,7 +234,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, } else if (node->recType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) { - cShape->collide = true; + cShape->collide = !(flags&0x800); handleNiTriShape(dynamic_cast(node), flags,node->trafo.rotation,node->trafo.pos,node->trafo.scale,raycastingOnly); } else if(node->recType == Nif::RC_RootCollisionNode) From 5706caa141044553809a8646e29da06b07bd28fa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Sep 2012 13:30:57 +0200 Subject: [PATCH 595/688] Issue #356: added ingredient eating sound effect --- apps/openmw/mwclass/ingredient.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 4d88be1eb..bdeb0e82b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -98,6 +98,8 @@ namespace MWClass { boost::shared_ptr action (new MWWorld::ActionEat (ptr)); + action->setSound ("Swallow"); + return action; } From 5155339cb65619ec69be8151f021e4de8054cc17 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Sep 2012 14:56:50 +0200 Subject: [PATCH 596/688] character preview working --- apps/openmw/mwgui/inventorywindow.cpp | 12 ++++++------ apps/openmw/mwrender/characterpreview.cpp | 2 +- files/mygui/openmw_inventory_window.layout | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index cf23ecd8f..9ccf5cfe1 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -102,12 +102,10 @@ namespace MWGui _sender->getSize().height-44 ); drawItems(); - MyGUI::IntSize size = mAvatarImage->getSize(); + MyGUI::IntSize size = mAvatar->getSize(); - std::cout << "dims " << size.width << " " << size.height << std::endl; MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); - - mAvatarImage->setImageCoord (MyGUI::IntCoord(0,0,size.width, size.height)); + mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -267,9 +265,11 @@ namespace MWGui else mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability - MyGUI::IntSize size = mAvatarImage->getSize(); - mAvatarImage->setImageCoord (MyGUI::IntCoord(0,0,size.width, size.height)); + MyGUI::IntSize size = mAvatar->getSize(); + MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); + + mAvatarImage->setSize(MyGUI::IntSize(512, 1024)); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 3d3f0a58d..fd25aab08 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -42,7 +42,7 @@ namespace MWRender { bool wasVisible = mNode->getParentSceneNode()->getAttachedObject(0)->getVisible (); mNode->getParentSceneNode()->setVisible(true, false); - //mViewport->setDimensions (0, 0, float(sizeX) / float(512), float(sizeY) / float(1024)); + mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); mRenderTarget->update(); diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index b9e14fffa..31bf58c34 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -15,9 +15,9 @@ - - - + + + From eef750e6b05b9c8ea8059274d50d650679f0edd5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 13 Sep 2012 19:03:31 +0200 Subject: [PATCH 597/688] race selection preview --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwgui/inventorywindow.cpp | 2 + apps/openmw/mwgui/race.cpp | 15 +++-- apps/openmw/mwgui/race.hpp | 4 +- apps/openmw/mwrender/actors.cpp | 4 +- apps/openmw/mwrender/characterpreview.cpp | 72 +++++++++++++++++----- apps/openmw/mwrender/characterpreview.hpp | 33 ++++++++-- apps/openmw/mwrender/npcanimation.cpp | 16 +++-- apps/openmw/mwrender/npcanimation.hpp | 7 ++- apps/openmw/mwrender/player.cpp | 5 ++ apps/openmw/mwrender/player.hpp | 1 + apps/openmw/mwrender/renderconst.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 26 ++++++-- apps/openmw/mwrender/renderingmanager.hpp | 9 ++- apps/openmw/mwrender/shadows.cpp | 2 +- apps/openmw/mwrender/water.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 5 ++ apps/openmw/mwworld/worldimp.hpp | 1 + files/mygui/openmw_chargen_race.layout | 4 +- files/mygui/openmw_inventory_window.layout | 1 - libs/openengine/ogre/renderer.cpp | 4 +- 21 files changed, 167 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 5ecd1b1b9..f56313ba3 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -273,6 +273,7 @@ namespace MWBase virtual void renderPlayer() = 0; virtual void updateCharacterPreview(int sizeX, int sizeY) = 0; + virtual void updateRaceSelectionPreview(float angle) = 0; }; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 9ccf5cfe1..9df6d893d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -106,6 +106,7 @@ namespace MWGui MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); + mAvatarImage->setImageTexture("CharacterPreview"); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -270,6 +271,7 @@ namespace MWGui MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); mAvatarImage->setSize(MyGUI::IntSize(512, 1024)); + mAvatarImage->setImageTexture("CharacterPreview"); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 370b30733..e1dc4af1a 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -25,15 +25,17 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) , mHairIndex(0) , mFaceCount(10) , mHairCount(14) + , mCurrentAngle(0) { // Centre dialog center(); - // These are just demo values, you should replace these with - // real calls from outside the class later. - setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); - getWidget(mAppearanceBox, "AppearanceBox"); + getWidget(mPreviewImage, "PreviewImage"); + + MWBase::Environment::get().getWorld ()->updateRaceSelectionPreview (0); + + mPreviewImage->setImageTexture ("CharacterHeadPreview"); getWidget(mHeadRotate, "HeadRotate"); mHeadRotate->setScrollRange(50); @@ -149,7 +151,10 @@ void RaceDialog::onBackClicked(MyGUI::Widget* _sender) void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) { - // TODO: Rotate head + float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; + float diff = angle - mCurrentAngle; + MWBase::Environment::get().getWorld ()->updateRaceSelectionPreview (diff); + mCurrentAngle += diff; } void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 4aded28c3..c1517eb76 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -73,7 +73,7 @@ namespace MWGui void updateSkills(); void updateSpellPowers(); - MyGUI::CanvasPtr mAppearanceBox; + MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; MyGUI::ScrollBar* mHeadRotate; @@ -87,6 +87,8 @@ namespace MWGui int mFaceCount, mHairCount; std::string mCurrentRaceId; + + float mCurrentAngle; }; } #endif diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 415d17241..15c89d7f5 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -3,6 +3,8 @@ #include #include +#include "renderconst.hpp" + using namespace Ogre; using namespace MWRender; @@ -23,7 +25,7 @@ void Actors::setMwRoot(Ogre::SceneNode* root){ void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv){ insertBegin(ptr, true, true); - NpcAnimation* anim = new MWRender::NpcAnimation(ptr, mRend, inv); + NpcAnimation* anim = new MWRender::NpcAnimation(ptr, ptr.getRefData ().getBaseNode (), mRend, inv, RV_Actors); mAllActors[ptr] = anim; } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index fd25aab08..21442782f 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -6,26 +6,31 @@ #include "renderconst.hpp" +#include "npcanimation.hpp" namespace MWRender { - CharacterPreview::CharacterPreview(Ogre::SceneManager *sceneMgr, Ogre::SceneNode *node) + CharacterPreview::CharacterPreview(Ogre::SceneManager *sceneMgr, Ogre::SceneNode *node, int sizeX, int sizeY, const std::string& name, + Ogre::Vector3 position, Ogre::Vector3 lookAt) : mSceneMgr(sceneMgr) + , mSizeX(sizeX) + , mSizeY(sizeY) { - mCamera = mSceneMgr->createCamera ("CharacterPreviewCamera"); - mCamera->setAspectRatio (0.5); + mCamera = mSceneMgr->createCamera (name); + mCamera->setAspectRatio (float(sizeX) / float(sizeY)); - mNode = node->createChildSceneNode (); - mNode->attachObject (mCamera); + mNode = node; + mNode->setVisible (false); - mNode->setPosition(0, 185, 70); - mNode->roll(Ogre::Degree(180)); + mCamera->setPosition(position); + mCamera->lookAt(lookAt); - mNode->pitch(Ogre::Degree(90)); + mCamera->setNearClipDistance (0.01); + mCamera->setFarClipDistance (1000); - mTexture = Ogre::TextureManager::getSingleton().createManual("CharacterPreview", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, 512, 1024, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); + mTexture = Ogre::TextureManager::getSingleton().createManual(name, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, sizeX, sizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); mRenderTarget = mTexture->getBuffer()->getRenderTarget(); mViewport = mRenderTarget->addViewport(mCamera); @@ -33,20 +38,55 @@ namespace MWRender mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); mViewport->setShadowsEnabled(false); mViewport->setMaterialScheme("local_map"); - mViewport->setVisibilityMask (RV_Player); + mViewport->setVisibilityMask (RV_PlayerPreview); mRenderTarget->setActive(true); mRenderTarget->setAutoUpdated (false); } - void CharacterPreview::update(int sizeX, int sizeY) + // -------------------------------------------------------------------------------------------------- + + + InventoryPreview::InventoryPreview(Ogre::SceneManager *sceneMgr, Ogre::SceneNode *node) + : CharacterPreview(sceneMgr, node, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) + , mAnimation(NULL) { - bool wasVisible = mNode->getParentSceneNode()->getAttachedObject(0)->getVisible (); - mNode->getParentSceneNode()->setVisible(true, false); + + } + + void InventoryPreview::update(int sizeX, int sizeY) + { + if (mAnimation) + mAnimation->forceUpdate (); + mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); - mRenderTarget->update(); + mNode->setOrientation (Ogre::Quaternion::IDENTITY); - mNode->getParentSceneNode()->setVisible(wasVisible, false); + mNode->setVisible (true); + mRenderTarget->update(); + mNode->setVisible (false); + } + + void InventoryPreview::setNpcAnimation (NpcAnimation *anim) + { + mAnimation = anim; + } + + // -------------------------------------------------------------------------------------------------- + + RaceSelectionPreview::RaceSelectionPreview(Ogre::SceneManager *sceneMgr, Ogre::SceneNode *node) + : CharacterPreview(sceneMgr, node, 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 120, -35), Ogre::Vector3(0,125,0)) + { + + } + + void RaceSelectionPreview::update(float angle) + { + mNode->roll(Ogre::Radian(angle), Ogre::SceneNode::TS_LOCAL); + + mNode->setVisible (true); + mRenderTarget->update(); + mNode->setVisible (false); } } diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index e15d6c359..ba48ea17d 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -6,14 +6,15 @@ namespace MWRender { + class NpcAnimation; + class CharacterPreview { public: - CharacterPreview(Ogre::SceneManager* sceneMgr, Ogre::SceneNode* node); + CharacterPreview(Ogre::SceneManager* sceneMgr, Ogre::SceneNode* node, int sizeX, int sizeY, const std::string& name, + Ogre::Vector3 position, Ogre::Vector3 lookAt); - void update(int sizeX, int sizeY); - - private: + protected: Ogre::TexturePtr mTexture; Ogre::RenderTarget* mRenderTarget; Ogre::Viewport* mViewport; @@ -22,6 +23,30 @@ namespace MWRender Ogre::SceneManager* mSceneMgr; Ogre::SceneNode* mNode; + + int mSizeX; + int mSizeY; + }; + + class InventoryPreview : public CharacterPreview + { + public: + InventoryPreview(Ogre::SceneManager* sceneMgr, Ogre::SceneNode* node); + + void update(int sizeX, int sizeY); + + void setNpcAnimation (NpcAnimation* anim); + + private: + NpcAnimation* mAnimation; + }; + + class RaceSelectionPreview : public CharacterPreview + { + public: + RaceSelectionPreview(Ogre::SceneManager* sceneMgr, Ogre::SceneNode* node); + + void update(float angle); }; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 392a79c96..00b877ba0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -45,15 +45,14 @@ NpcAnimation::~NpcAnimation() } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv, bool player) - : Animation(_rend), mStateID(-1), mInv(_inv), timeToChange(0), +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv, int visibilityFlags) + : Animation(_rend), mStateID(-1), mInv(_inv), timeToChange(0), mVisibilityFlags(visibilityFlags), robe(mInv.end()), helmet(mInv.end()), shirt(mInv.end()), cuirass(mInv.end()), greaves(mInv.end()), leftpauldron(mInv.end()), rightpauldron(mInv.end()), boots(mInv.end()), leftglove(mInv.end()), rightglove(mInv.end()), skirtiter(mInv.end()), pants(mInv.end()) - , mIsPlayer(player) { MWWorld::LiveCellRef *ref = ptr.get(); @@ -85,7 +84,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; */ - mInsert = ptr.getRefData().getBaseNode(); + mInsert = node; assert(mInsert); std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); @@ -95,7 +94,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere { Ogre::Entity *base = mEntityList.mEntities[i]; - base->setVisibilityFlags(mIsPlayer ? RV_Player : RV_Actors); + base->setVisibilityFlags(mVisibilityFlags); bool transparent = false; for(unsigned int j=0;j < base->getNumSubEntities();++j) { @@ -358,7 +357,7 @@ NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, con mInsert, mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) - parts[i]->setVisibilityFlags(mIsPlayer ? RV_Player : RV_Actors); + parts[i]->setVisibilityFlags(mVisibilityFlags); return entities; } @@ -582,4 +581,9 @@ void NpcAnimation::addPartGroup(int group, int priority, std::vector& parts); + + void forceUpdate(); }; } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3ccc08022..297cb8d7a 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -35,6 +35,11 @@ namespace MWRender mPreviewCam.yaw = 0.f; mPreviewCam.offset = 400.f; } + + Player::~Player() + { + delete mAnimation; + } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 8dd313b7f..b4d8983e4 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -63,6 +63,7 @@ namespace MWRender public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); + ~Player(); /// Set where the player is looking at. Uses Morrowind (euler) angles /// \param rot Rotation angles in radians diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 194ca9da6..e6ecb5150 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -54,7 +54,7 @@ enum VisibilityFlags RV_OcclusionQuery = 256, - RV_Player = 512, + RV_PlayerPreview = 512, RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index acb587d03..d2a51f5f1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -160,8 +160,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mDebugging = new Debugging(mMwRoot, engine); mLocalMap = new MWRender::LocalMap(&mRendering, this); - mCharacterPreview = new CharacterPreview(mRendering.getScene (), playerNode); - setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } @@ -178,7 +176,9 @@ RenderingManager::~RenderingManager () delete mOcclusionQuery; delete mCompositors; delete mWater; - delete mCharacterPreview; + delete mInventoryPreview; + delete mRaceSelectionPreview; + delete mPreviewAnimation; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -862,11 +862,20 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) { MWRender::NpcAnimation *anim = new MWRender::NpcAnimation( - ptr, + ptr, ptr.getRefData ().getBaseNode (), mRendering, - MWWorld::Class::get(ptr).getInventoryStore(ptr), true + MWWorld::Class::get(ptr).getInventoryStore(ptr), RV_Actors ); mPlayer->setAnimation(anim); + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + + Ogre::SceneNode* previewNode = mMwRoot->createChildSceneNode (); + mPreviewAnimation = new NpcAnimation(ptr, previewNode, mRendering, invStore, RV_PlayerPreview); + + mInventoryPreview = new InventoryPreview(mRendering.getScene (), previewNode); + mInventoryPreview->setNpcAnimation (mPreviewAnimation); + mRaceSelectionPreview = new RaceSelectionPreview(mRendering.getScene (), previewNode); } void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) @@ -888,7 +897,12 @@ bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, boo void RenderingManager::updateCharacterPreview (int sizeX, int sizeY) { - mCharacterPreview->update(sizeX, sizeY); + mInventoryPreview->update(sizeX, sizeY); +} + +void RenderingManager::updateRaceSelectionPreview (float angle) +{ + mRaceSelectionPreview->update(angle); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index fe79c9400..cbad67472 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -43,7 +43,8 @@ namespace MWRender class LocalMap; class Water; class Compositors; - class CharacterPreview; + class InventoryPreview; + class RaceSelectionPreview; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -193,6 +194,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList ///< see MWRender::LocalMap::isPositionExplored void updateCharacterPreview(int sizeX, int sizeY); + void updateRaceSelectionPreview(float angle); protected: virtual void windowResized(Ogre::RenderWindow* rw); @@ -216,7 +218,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList TerrainManager* mTerrainManager; - CharacterPreview* mCharacterPreview; + InventoryPreview* mInventoryPreview; + RaceSelectionPreview* mRaceSelectionPreview; MWRender::Water *mWater; @@ -247,6 +250,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Shadows* mShadows; MWRender::Compositors* mCompositors; + + MWRender::NpcAnimation* mPreviewAnimation; }; } diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index fc28f1cb9..3d9f13243 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -111,7 +111,7 @@ void Shadows::recreate() sh::Factory::getInstance ().setSharedParameter ("shadowFar_fadeStart", sh::makeProperty(shadowFar_fadeStart)); // Set visibility mask for the shadow render textures - int visibilityMask = (RV_Actors + RV_Player) * Settings::Manager::getBool("actor shadows", "Shadows") + int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows") + (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows") + RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows"); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 6a3eeb509..7100540fc 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -332,7 +332,7 @@ void Water::applyVisibilityMask() mVisibilityFlags = RV_Terrain * Settings::Manager::getBool("reflect terrain", "Water") + RV_Statics * Settings::Manager::getBool("reflect statics", "Water") + RV_StaticsSmall * Settings::Manager::getBool("reflect small statics", "Water") - + (RV_Actors + RV_Player) * Settings::Manager::getBool("reflect actors", "Water") + + RV_Actors * Settings::Manager::getBool("reflect actors", "Water") + RV_Misc * Settings::Manager::getBool("reflect misc", "Water") + RV_Sky; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f88f5c320..005b94ffa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1227,4 +1227,9 @@ namespace MWWorld { mRendering->updateCharacterPreview(sizeX, sizeY); } + + void World::updateRaceSelectionPreview (float angle) + { + mRendering->updateRaceSelectionPreview(angle); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5e8bc697e..20da6f729 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -309,6 +309,7 @@ namespace MWWorld virtual void renderPlayer(); virtual void updateCharacterPreview(int sizeX, int sizeY); + virtual void updateRaceSelectionPreview(float angle); }; } diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 3d32ffd9a..6891f5d93 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -8,7 +8,9 @@ - + + + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 31bf58c34..4f295212b 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -13,7 +13,6 @@ - diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index e342f4c5f..2764410ad 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -188,7 +188,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms); // create the semi-transparent black background texture used by the GUI. - // has to be created in code with TU_DYNAMIC_WRITE_ONLY_DISCARDABLE param + // has to be created in code with TU_DYNAMIC_WRITE_ONLY param // so that it can be modified at runtime. Ogre::TextureManager::getSingleton().createManual( "transparent.png", @@ -197,7 +197,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& 1, 1, 0, Ogre::PF_A8R8G8B8, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + Ogre::TU_DYNAMIC_WRITE_ONLY); } void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip) From 6ac2a12296f92a3e55b3d2c1b6771aed5ab72270 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Sep 2012 14:34:18 +0200 Subject: [PATCH 598/688] item selection working on the preview --- CMakeLists.txt | 1 + apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwgui/inventorywindow.cpp | 30 +++++ apps/openmw/mwgui/inventorywindow.hpp | 6 + apps/openmw/mwgui/tooltips.cpp | 13 ++- apps/openmw/mwrender/characterpreview.cpp | 16 +++ apps/openmw/mwrender/characterpreview.hpp | 15 +++ apps/openmw/mwrender/npcanimation.cpp | 59 +++++----- apps/openmw/mwrender/npcanimation.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 6 + apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 15 +++ apps/openmw/mwworld/worldimp.hpp | 1 + extern/shiny | 2 +- files/CMakeLists.txt | 4 +- files/materials/selection.mat | 8 ++ files/materials/selection.shader | 24 ++++ files/materials/selection.shaderset | 15 +++ files/mygui/openmw_chargen_race.layout | 2 +- files/mygui/openmw_inventory_window.layout | 1 + libs/openengine/ogre/selectionbuffer.cpp | 123 +++++++++++++++++++++ libs/openengine/ogre/selectionbuffer.hpp | 54 +++++++++ 22 files changed, 367 insertions(+), 34 deletions(-) create mode 100644 files/materials/selection.mat create mode 100644 files/materials/selection.shader create mode 100644 files/materials/selection.shaderset create mode 100644 libs/openengine/ogre/selectionbuffer.cpp create mode 100644 libs/openengine/ogre/selectionbuffer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b5b0884e4..e01b3d26b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/atlas.cpp + ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ) set(OENGINE_GUI ${LIBDIR}/openengine/gui/manager.cpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f56313ba3..0865f6383 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -274,6 +274,7 @@ namespace MWBase virtual void updateCharacterPreview(int sizeX, int sizeY) = 0; virtual void updateRaceSelectionPreview(float angle) = 0; + virtual MWWorld::Ptr getCharacterPreviewItemSelected(int posX, int posY) = 0; }; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 9df6d893d..7e46aeb03 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -44,6 +44,8 @@ namespace MWGui : ContainerBase(dragAndDrop) , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) , mTrading(false) + , mAvatarClickedPosX(0) + , mAvatarClickedPosY(0) { static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); @@ -107,6 +109,7 @@ namespace MWGui MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); mAvatarImage->setImageTexture("CharacterPreview"); + mAvatarImage->setImageTexture("SelectionBuffer"); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -180,6 +183,28 @@ namespace MWGui notifyContentChanged(); } + else + { + MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left); + MyGUI::IntPoint relPos = mousePos - mAvatar->getAbsolutePosition (); + int realX = int(float(relPos.left) / float(mAvatar->getSize().width) * 512.f ); + int realY = int(float(relPos.top) / float(mAvatar->getSize().height) * 1024.f ); + MWWorld::Ptr itemSelected = MWBase::Environment::get().getWorld ()->getCharacterPreviewItemSelected (realX, realY); + if (itemSelected.isEmpty ()) + return; + + + for (unsigned int i=0; i < mContainerWidget->getChildCount (); ++i) + { + MyGUI::Widget* w = mContainerWidget->getChildAt (i); + + if (*w->getUserData() == itemSelected) + { + onSelectedItem(w); + return; + } + } + } } std::vector InventoryWindow::getEquippedItems() @@ -336,4 +361,9 @@ namespace MWGui text->setCaption(getCountString(count)); mDragAndDrop->mDraggedFrom = this; } + + MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () + { + return mAvatar->getAbsoluteCoord (); + } } diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 4a385dd71..288f32704 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -22,6 +22,8 @@ namespace MWGui int getPlayerGold(); + MyGUI::IntCoord getAvatarScreenCoord(); + protected: MyGUI::Widget* mAvatar; MyGUI::ImageBox* mAvatarImage; @@ -38,6 +40,10 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; + int mAvatarClickedPosX; + int mAvatarClickedPosY; + + bool mTrading; void onWindowResize(MyGUI::Window* _sender); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 7aee26bcc..3306ea5c2 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -14,6 +14,7 @@ #include "map_window.hpp" #include "widgets.hpp" +#include "inventorywindow.hpp" using namespace MWGui; using namespace MyGUI; @@ -162,12 +163,22 @@ void ToolTips::onFrame(float frameDuration) return; } - if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); tooltipSize = getToolTipViaPtr(false); } + else if (type == "AvatarItemSelection") + { + MyGUI::IntCoord avatarPos = mWindowManager->getInventoryWindow ()->getAvatarScreenCoord (); + MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); + int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); + int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); + MWWorld::Ptr item = MWBase::Environment::get().getWorld ()->getCharacterPreviewItemSelected (realX, realY); + + mFocusObject = item; + tooltipSize = getToolTipViaPtr(false); + } else if (type == "Spell") { ToolTipInfo info; diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 21442782f..4a450fad9 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "renderconst.hpp" #include "npcanimation.hpp" @@ -50,7 +52,12 @@ namespace MWRender : CharacterPreview(sceneMgr, node, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) , mAnimation(NULL) { + mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview); + } + InventoryPreview::~InventoryPreview() + { + delete mSelectionBuffer; } void InventoryPreview::update(int sizeX, int sizeY) @@ -63,7 +70,10 @@ namespace MWRender mNode->setOrientation (Ogre::Quaternion::IDENTITY); mNode->setVisible (true); + mRenderTarget->update(); + mSelectionBuffer->update(); + mNode->setVisible (false); } @@ -72,6 +82,12 @@ namespace MWRender mAnimation = anim; } + int InventoryPreview::getSlotSelected (int posX, int posY) + { + std::cout << posX << " " << posY << std::endl; + return mSelectionBuffer->getSelected (posX, posY); + } + // -------------------------------------------------------------------------------------------------- RaceSelectionPreview::RaceSelectionPreview(Ogre::SceneManager *sceneMgr, Ogre::SceneNode *node) diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index ba48ea17d..fd406ceb5 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -2,6 +2,16 @@ #define MWRENDER_CHARACTERPREVIEW_H #include +#include + + +namespace OEngine +{ +namespace Render +{ +class SelectionBuffer; +} +} namespace MWRender { @@ -32,13 +42,18 @@ namespace MWRender { public: InventoryPreview(Ogre::SceneManager* sceneMgr, Ogre::SceneNode* node); + virtual ~InventoryPreview(); void update(int sizeX, int sizeY); + int getSlotSelected(int posX, int posY); + void setNpcAnimation (NpcAnimation* anim); private: NpcAnimation* mAnimation; + + OEngine::Render::SelectionBuffer* mSelectionBuffer; }; class RaceSelectionPreview : public CharacterPreview diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 00b877ba0..c039b8d44 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -94,6 +94,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, OEngi { Ogre::Entity *base = mEntityList.mEntities[i]; + base->getUserObjectBindings ().setUserAny (Ogre::Any(-1)); + base->setVisibilityFlags(mVisibilityFlags); bool transparent = false; for(unsigned int j=0;j < base->getNumSubEntities();++j) @@ -351,13 +353,16 @@ void NpcAnimation::updateParts() } } -NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) +NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename) { NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, mInsert, mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) + { parts[i]->setVisibilityFlags(mVisibilityFlags); + parts[i]->getUserObjectBindings ().setUserAny (Ogre::Any(group)); + } return entities; } @@ -480,83 +485,83 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, switch(type) { case ESM::PRT_Head: //0 - head = insertBoundedPart(mesh, "Head"); + head = insertBoundedPart(mesh, group, "Head"); break; case ESM::PRT_Hair: //1 - hair = insertBoundedPart(mesh, "Head"); + hair = insertBoundedPart(mesh, group, "Head"); break; case ESM::PRT_Neck: //2 - neck = insertBoundedPart(mesh, "Neck"); + neck = insertBoundedPart(mesh, group, "Neck"); break; case ESM::PRT_Cuirass: //3 - chest = insertBoundedPart(mesh, "Chest"); + chest = insertBoundedPart(mesh, group, "Chest"); break; case ESM::PRT_Groin: //4 - groin = insertBoundedPart(mesh, "Groin"); + groin = insertBoundedPart(mesh, group, "Groin"); break; case ESM::PRT_Skirt: //5 - skirt = insertBoundedPart(mesh, "Groin"); + skirt = insertBoundedPart(mesh, group, "Groin"); break; case ESM::PRT_RHand: //6 - rHand = insertBoundedPart(mesh, "Right Hand"); + rHand = insertBoundedPart(mesh, group, "Right Hand"); break; case ESM::PRT_LHand: //7 - lHand = insertBoundedPart(mesh, "Left Hand"); + lHand = insertBoundedPart(mesh, group, "Left Hand"); break; case ESM::PRT_RWrist: //8 - rWrist = insertBoundedPart(mesh, "Right Wrist"); + rWrist = insertBoundedPart(mesh, group, "Right Wrist"); break; case ESM::PRT_LWrist: //9 - lWrist = insertBoundedPart(mesh, "Left Wrist"); + lWrist = insertBoundedPart(mesh, group, "Left Wrist"); break; case ESM::PRT_Shield: //10 break; case ESM::PRT_RForearm: //11 - rForearm = insertBoundedPart(mesh, "Right Forearm"); + rForearm = insertBoundedPart(mesh, group, "Right Forearm"); break; case ESM::PRT_LForearm: //12 - lForearm = insertBoundedPart(mesh, "Left Forearm"); + lForearm = insertBoundedPart(mesh, group, "Left Forearm"); break; case ESM::PRT_RUpperarm: //13 - rupperArm = insertBoundedPart(mesh, "Right Upper Arm"); + rupperArm = insertBoundedPart(mesh, group, "Right Upper Arm"); break; case ESM::PRT_LUpperarm: //14 - lupperArm = insertBoundedPart(mesh, "Left Upper Arm"); + lupperArm = insertBoundedPart(mesh, group, "Left Upper Arm"); break; case ESM::PRT_RFoot: //15 - rfoot = insertBoundedPart(mesh, "Right Foot"); + rfoot = insertBoundedPart(mesh, group, "Right Foot"); break; case ESM::PRT_LFoot: //16 - lfoot = insertBoundedPart(mesh, "Left Foot"); + lfoot = insertBoundedPart(mesh, group, "Left Foot"); break; case ESM::PRT_RAnkle: //17 - rAnkle = insertBoundedPart(mesh, "Right Ankle"); + rAnkle = insertBoundedPart(mesh, group, "Right Ankle"); break; case ESM::PRT_LAnkle: //18 - lAnkle = insertBoundedPart(mesh, "Left Ankle"); + lAnkle = insertBoundedPart(mesh, group, "Left Ankle"); break; case ESM::PRT_RKnee: //19 - rKnee = insertBoundedPart(mesh, "Right Knee"); + rKnee = insertBoundedPart(mesh, group, "Right Knee"); break; case ESM::PRT_LKnee: //20 - lKnee = insertBoundedPart(mesh, "Left Knee"); + lKnee = insertBoundedPart(mesh, group, "Left Knee"); break; case ESM::PRT_RLeg: //21 - rUpperLeg = insertBoundedPart(mesh, "Right Upper Leg"); + rUpperLeg = insertBoundedPart(mesh, group, "Right Upper Leg"); break; case ESM::PRT_LLeg: //22 - lUpperLeg = insertBoundedPart(mesh, "Left Upper Leg"); + lUpperLeg = insertBoundedPart(mesh, group, "Left Upper Leg"); break; case ESM::PRT_RPauldron: //23 - rclavicle = insertBoundedPart(mesh , "Right Clavicle"); + rclavicle = insertBoundedPart(mesh , group, "Right Clavicle"); break; case ESM::PRT_LPauldron: //24 - lclavicle = insertBoundedPart(mesh, "Left Clavicle"); + lclavicle = insertBoundedPart(mesh, group, "Left Clavicle"); break; case ESM::PRT_Weapon: //25 break; case ESM::PRT_Tail: //26 - tail = insertBoundedPart(mesh, "Tail"); + tail = insertBoundedPart(mesh, group, "Tail"); break; } return true; @@ -575,7 +580,7 @@ void NpcAnimation::addPartGroup(int group, int priority, std::vectorgetStore().bodyParts.search(part.male); if(bodypart) - addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); + addOrReplaceIndividualPart(part.part, group, priority,"meshes\\" + bodypart->model); else reserveIndividualPart(part.part, group, priority); } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index fe66d733a..699c6648d 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -18,8 +18,6 @@ private: int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty int mPartPriorities[27]; - bool mIsPlayer; - //Bounded Parts NifOgre::EntityList lclavicle; NifOgre::EntityList rclavicle; @@ -73,7 +71,7 @@ public: NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv, int visibilityFlags); virtual ~NpcAnimation(); - NifOgre::EntityList insertBoundedPart(const std::string &mesh, const std::string &bonename); + NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename); virtual void runAnimation(float timepassed); void updateParts(); void removeEntities(NifOgre::EntityList &entities); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index d2a51f5f1..a1ad77caf 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -905,4 +905,10 @@ void RenderingManager::updateRaceSelectionPreview (float angle) mRaceSelectionPreview->update(angle); } +int RenderingManager::getCharacterPreviewSlotSelected(int posX, int posY) +{ + std::cout << "SLOT IS " << mInventoryPreview->getSlotSelected(posX, posY) << std::endl; + return mInventoryPreview->getSlotSelected(posX, posY); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index cbad67472..42fcb1502 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -195,6 +195,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void updateCharacterPreview(int sizeX, int sizeY); void updateRaceSelectionPreview(float angle); + int getCharacterPreviewSlotSelected(int posX, int posY); protected: virtual void windowResized(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 005b94ffa..a38cdd5ff 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1232,4 +1232,19 @@ namespace MWWorld { mRendering->updateRaceSelectionPreview(angle); } + + MWWorld::Ptr World::getCharacterPreviewItemSelected(int posX, int posY) + { + int slot = mRendering->getCharacterPreviewSlotSelected(posX, posY); + + if (slot == -1) + return MWWorld::Ptr(); + + MWWorld::Ptr player = getPlayer().getPlayer (); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); + if (invStore.getSlot(slot) != invStore.end()) + return *invStore.getSlot (slot); + else + return MWWorld::Ptr(); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 20da6f729..cd7286861 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -310,6 +310,7 @@ namespace MWWorld virtual void updateCharacterPreview(int sizeX, int sizeY); virtual void updateRaceSelectionPreview(float angle); + virtual MWWorld::Ptr getCharacterPreviewItemSelected(int posX, int posY); }; } diff --git a/extern/shiny b/extern/shiny index 716841523..4750676ac 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 7168415233905de2864eec71ed4312cb8f83059b +Subproject commit 4750676ac46a7aaa86bca53dc68c5a1ba11f3bc1 diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index 3e99f5745..e8426afb7 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -40,7 +40,9 @@ set(MATERIAL_FILES water.mat water.shader water.shaderset - + selection.mat + selection.shader + selection.shaderset ) copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water "${OpenMW_BINARY_DIR}/resources/water/" "${WATER_FILES}") diff --git a/files/materials/selection.mat b/files/materials/selection.mat new file mode 100644 index 000000000..a76dd7179 --- /dev/null +++ b/files/materials/selection.mat @@ -0,0 +1,8 @@ +material SelectionColour +{ + pass + { + vertex_program selection_vertex + fragment_program selection_fragment + } +} diff --git a/files/materials/selection.shader b/files/materials/selection.shader new file mode 100644 index 000000000..095a31259 --- /dev/null +++ b/files/materials/selection.shader @@ -0,0 +1,24 @@ +#include "core.h" + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + } + +#else + + SH_BEGIN_PROGRAM + shUniform(float4, colour) @shAutoConstant(colour, custom, 1) + + SH_START_PROGRAM + { + shOutputColour(0) = colour; + //shOutputColour(0) = float4(1,0,0,1); + } + +#endif diff --git a/files/materials/selection.shaderset b/files/materials/selection.shaderset new file mode 100644 index 000000000..c90826282 --- /dev/null +++ b/files/materials/selection.shaderset @@ -0,0 +1,15 @@ +shader_set selection_vertex +{ + source selection.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set selection_fragment +{ + source selection.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 6891f5d93..b69073899 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -14,7 +14,7 @@ - + diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 4f295212b..86dee558f 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -12,6 +12,7 @@ + diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp new file mode 100644 index 000000000..99489f008 --- /dev/null +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -0,0 +1,123 @@ +#include "selectionbuffer.hpp" + +#include +#include +#include +#include + +#include + +namespace OEngine +{ +namespace Render +{ + + SelectionBuffer::SelectionBuffer(Ogre::Camera *camera, int sizeX, int sizeY, int visibilityFlags) + { + mTexture = Ogre::TextureManager::getSingleton().createManual("SelectionBuffer", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, sizeX, sizeY, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET); + + mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + Ogre::Viewport* vp = mRenderTarget->addViewport(camera); + vp->setOverlaysEnabled(false); + vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); + vp->setShadowsEnabled(false); + vp->setMaterialScheme("selectionbuffer"); + vp->setVisibilityMask (visibilityFlags); + mRenderTarget->setActive(true); + mRenderTarget->setAutoUpdated (false); + + mCurrentColour = Ogre::ColourValue(0.3, 0.3, 0.3); + } + + SelectionBuffer::~SelectionBuffer() + { + Ogre::TextureManager::getSingleton ().remove("SelectionBuffer"); + } + + void SelectionBuffer::update () + { + Ogre::MaterialManager::getSingleton ().addListener (this); + + mRenderTarget->update(); + + Ogre::MaterialManager::getSingleton ().removeListener (this); + + mTexture->convertToImage(mBuffer); + } + + int SelectionBuffer::getSelected(int xPos, int yPos) + { + Ogre::ColourValue clr = mBuffer.getColourAt (xPos, yPos, 0); + clr.a = 1; + if (mColourMap.find(clr) != mColourMap.end()) + return mColourMap[clr]; + else + return -1; // nothing selected + } + + Ogre::Technique* SelectionBuffer::handleSchemeNotFound ( + unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial, + unsigned short lodIndex, const Ogre::Renderable *rend) + { + if (schemeName == "selectionbuffer") + { + sh::Factory::getInstance ()._ensureMaterial ("SelectionColour", "Default"); + + Ogre::MaterialPtr m = Ogre::MaterialManager::getSingleton ().getByName("SelectionColour"); + + + if(typeid(*rend) == typeid(Ogre::SubEntity)) + { + const Ogre::SubEntity *subEntity = static_cast(rend); + int id = subEntity->getParent ()->getUserObjectBindings().getUserAny().get(); + std::cout << "found ID:" << id << " entity name is " << subEntity->getParent()->getName() << std::endl; + bool found = false; + Ogre::ColourValue colour; + for (std::map::iterator it = mColourMap.begin(); it != mColourMap.end(); ++it) + { + if (it->second == id) + { + found = true; + colour = it->first; + } + } + + + if (!found) + { + getNextColour(); + const_cast(subEntity)->setCustomParameter(1, Ogre::Vector4(mCurrentColour.r, mCurrentColour.g, mCurrentColour.b, 1.0)); + mColourMap[mCurrentColour] = id; + } + else + { + const_cast(subEntity)->setCustomParameter(1, Ogre::Vector4(colour.r, colour.g, colour.b, 1.0)); + } + + assert(m->getTechnique(1)); + return m->getTechnique(1); + } + else + throw std::runtime_error("selectionbuffer only works with entities"); + } + return NULL; + } + + void SelectionBuffer::getNextColour () + { + Ogre::ARGB color = (float(rand()) / float(RAND_MAX)) * std::numeric_limits::max(); + + if (mCurrentColour.getAsARGB () == color) + { + getNextColour(); + return; + } + + mCurrentColour.setAsARGB(color); + mCurrentColour.a = 1; + } + + +} +} diff --git a/libs/openengine/ogre/selectionbuffer.hpp b/libs/openengine/ogre/selectionbuffer.hpp new file mode 100644 index 000000000..c487b24b0 --- /dev/null +++ b/libs/openengine/ogre/selectionbuffer.hpp @@ -0,0 +1,54 @@ +#ifndef OENGINE_SELECTIONBUFFER_H +#define OENGINE_SELECTIONBUFFER_H + + +#include +#include +#include + +namespace OEngine +{ +namespace Render +{ + + struct cmp_ColourValue + { + bool operator()(const Ogre::ColourValue &a, const Ogre::ColourValue &b) const + { + return a.getAsBGRA() < b.getAsBGRA(); + } + }; + + class SelectionBuffer : public Ogre::MaterialManager::Listener + { + public: + SelectionBuffer(Ogre::Camera* camera, int sizeX, int sizeY, int visibilityFlags); + virtual ~SelectionBuffer(); + + int getSelected(int xPos, int yPos); + ///< @return ID of the selected object + + void update(); + + virtual Ogre::Technique* handleSchemeNotFound ( + unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial, + unsigned short lodIndex, const Ogre::Renderable *rend); + + + private: + Ogre::TexturePtr mTexture; + Ogre::RenderTexture* mRenderTarget; + + Ogre::Image mBuffer; + + std::map mColourMap; + + Ogre::ColourValue mCurrentColour; + + void getNextColour(); + }; + +} +} + +#endif From 1f7e044fadd45b0658f08595d113b56d59f4d5f8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Sep 2012 14:41:19 +0200 Subject: [PATCH 599/688] removed some debug stuff --- apps/openmw/mwgui/inventorywindow.cpp | 2 -- apps/openmw/mwrender/characterpreview.cpp | 1 - apps/openmw/mwrender/renderingmanager.cpp | 1 - libs/openengine/ogre/selectionbuffer.cpp | 1 - 4 files changed, 5 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7e46aeb03..f5091eea6 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -109,7 +109,6 @@ namespace MWGui MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); mAvatarImage->setImageTexture("CharacterPreview"); - mAvatarImage->setImageTexture("SelectionBuffer"); } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -294,7 +293,6 @@ namespace MWGui MyGUI::IntSize size = mAvatar->getSize(); MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); - mAvatarImage->setSize(MyGUI::IntSize(512, 1024)); mAvatarImage->setImageTexture("CharacterPreview"); } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 4a450fad9..1be78a0f7 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -84,7 +84,6 @@ namespace MWRender int InventoryPreview::getSlotSelected (int posX, int posY) { - std::cout << posX << " " << posY << std::endl; return mSelectionBuffer->getSelected (posX, posY); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a1ad77caf..4b77da5e1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -907,7 +907,6 @@ void RenderingManager::updateRaceSelectionPreview (float angle) int RenderingManager::getCharacterPreviewSlotSelected(int posX, int posY) { - std::cout << "SLOT IS " << mInventoryPreview->getSlotSelected(posX, posY) << std::endl; return mInventoryPreview->getSlotSelected(posX, posY); } diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 99489f008..7b9e6f984 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -71,7 +71,6 @@ namespace Render { const Ogre::SubEntity *subEntity = static_cast(rend); int id = subEntity->getParent ()->getUserObjectBindings().getUserAny().get(); - std::cout << "found ID:" << id << " entity name is " << subEntity->getParent()->getName() << std::endl; bool found = false; Ogre::ColourValue colour; for (std::map::iterator it = mColourMap.begin(); it != mColourMap.end(); ++it) From 565aad378333f9a07152694ad2c462365f850d46 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Sep 2012 14:49:56 +0200 Subject: [PATCH 600/688] changed a method for ogre 1.8.0 --- libs/openengine/ogre/selectionbuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 7b9e6f984..3ecb99f3b 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -70,7 +70,7 @@ namespace Render if(typeid(*rend) == typeid(Ogre::SubEntity)) { const Ogre::SubEntity *subEntity = static_cast(rend); - int id = subEntity->getParent ()->getUserObjectBindings().getUserAny().get(); + int id = Ogre::any_cast(subEntity->getParent ()->getUserObjectBindings().getUserAny()); bool found = false; Ogre::ColourValue colour; for (std::map::iterator it = mColourMap.begin(); it != mColourMap.end(); ++it) From 44c644dd7b3d7c7a65b6d5386adec4937a7de62e Mon Sep 17 00:00:00 2001 From: Brother Brick Date: Fri, 14 Sep 2012 15:30:48 +0200 Subject: [PATCH 601/688] make it statically compilable --- libs/openengine/ogre/renderer.cpp | 2 +- libs/openengine/ogre/renderer.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index e342f4c5f..daf82b9da 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -78,7 +78,7 @@ void OgreRenderer::start() #endif } -bool OgreRenderer::loadPlugins() const +bool OgreRenderer::loadPlugins() { #ifdef ENABLE_PLUGIN_GL mGLPlugin = new Ogre::GLPlugin(); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 9b7003368..00e094b4d 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -151,7 +151,7 @@ namespace OEngine /// Start the main rendering loop void start(); - bool loadPlugins() const; + bool loadPlugins() ; void update(float dt); From a308d2e38a35db50d173408750086915fd6e2884 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Sep 2012 15:33:24 +0200 Subject: [PATCH 602/688] fixed exception, fixed armor label obscuring the items --- apps/openmw/mwgui/tooltips.cpp | 3 ++- files/mygui/openmw_inventory_window.layout | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 3306ea5c2..039df2269 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -177,7 +177,8 @@ void ToolTips::onFrame(float frameDuration) MWWorld::Ptr item = MWBase::Environment::get().getWorld ()->getCharacterPreviewItemSelected (realX, realY); mFocusObject = item; - tooltipSize = getToolTipViaPtr(false); + if (!mFocusObject.isEmpty ()) + tooltipSize = getToolTipViaPtr(false); } else if (type == "Spell") { diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 86dee558f..3a60916f7 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -18,6 +18,7 @@ + From 7c59b83419809721f03a107d74be13434cf12597 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Sep 2012 17:10:10 +0200 Subject: [PATCH 603/688] performance optimization --- apps/openmw/mwgui/inventorywindow.cpp | 16 ++++++++-------- apps/openmw/mwgui/inventorywindow.hpp | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index f5091eea6..dffd14d17 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -44,8 +44,8 @@ namespace MWGui : ContainerBase(dragAndDrop) , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) , mTrading(false) - , mAvatarClickedPosX(0) - , mAvatarClickedPosY(0) + , mLastXSize(0) + , mLastYSize(0) { static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); @@ -102,13 +102,13 @@ namespace MWGui mRightPane->getPosition().top, _sender->getSize().width - 12 - (_sender->getSize().height-44) * aspect - 15, _sender->getSize().height-44 ); - drawItems(); - MyGUI::IntSize size = mAvatar->getSize(); - - MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); - mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); - mAvatarImage->setImageTexture("CharacterPreview"); + if (mMainWidget->getSize().width != mLastXSize || mMainWidget->getSize().height != mLastYSize) + { + drawItems(); + mLastXSize = mMainWidget->getSize().width; + mLastYSize = mMainWidget->getSize().height; + } } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 288f32704..f17431597 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -40,8 +40,8 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; - int mAvatarClickedPosX; - int mAvatarClickedPosY; + int mLastXSize; + int mLastYSize; bool mTrading; From 35f7216f2f52b4b21e89ecdf974f731ca18add84 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Sep 2012 17:25:32 +0200 Subject: [PATCH 604/688] fixed some things --- apps/openmw/mwgui/inventorywindow.cpp | 3 ++- apps/openmw/mwrender/npcanimation.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index dffd14d17..66a8efe5d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -92,6 +92,7 @@ namespace MWGui mBoughtItems.clear(); onWindowResize(static_cast(mMainWidget)); + drawItems(); } void InventoryWindow::onWindowResize(MyGUI::Window* _sender) @@ -293,7 +294,7 @@ namespace MWGui MyGUI::IntSize size = mAvatar->getSize(); MWBase::Environment::get().getWorld()->updateCharacterPreview (size.width, size.height); - mAvatarImage->setSize(MyGUI::IntSize(512, 1024)); + mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); mAvatarImage->setImageTexture("CharacterPreview"); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c039b8d44..c386193c8 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -368,7 +368,7 @@ NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int void NpcAnimation::runAnimation(float timepassed) { - //if(timeToChange > .2) + if(timeToChange > .2) { timeToChange = 0; updateParts(); From b76a28f69ce5357b60501b1fd2f6aca6b9c8469a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Sep 2012 19:44:00 +0200 Subject: [PATCH 605/688] beginnings of the gui --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/levelupdialog.cpp | 63 ++++++++ apps/openmw/mwgui/levelupdialog.hpp | 30 ++++ apps/openmw/mwgui/mode.hpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 3 + apps/openmw/mwgui/windowmanagerimp.hpp | 2 + files/mygui/CMakeLists.txt | 5 +- .../{openmw.font.xml => openmw_font.xml} | 0 files/mygui/openmw_levelup_dialog.layout | 146 ++++++++++++++++++ ...{openmw.pointer.xml => openmw_pointer.xml} | 0 10 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 apps/openmw/mwgui/levelupdialog.cpp create mode 100644 apps/openmw/mwgui/levelupdialog.hpp rename files/mygui/{openmw.font.xml => openmw_font.xml} (100%) create mode 100644 files/mygui/openmw_levelup_dialog.layout rename files/mygui/{openmw.pointer.xml => openmw_pointer.xml} (100%) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b9ea8041d..1870a93f8 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -29,7 +29,7 @@ add_openmw_dir (mwgui map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu - itemselection spellbuyingwindow loadingscreen + itemselection spellbuyingwindow loadingscreen levelupdialog ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp new file mode 100644 index 000000000..9f57a2bdd --- /dev/null +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -0,0 +1,63 @@ +#include "levelupdialog.hpp" + +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + +namespace MWGui +{ + + LevelupDialog::LevelupDialog(MWBase::WindowManager &parWindowManager) + : WindowBase("openmw_levelup_dialog.layout", parWindowManager) + { + getWidget(mOkButton, "OkButton"); + getWidget(mClassImage, "ClassImage"); + getWidget(mLevelText, "LevelText"); + + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onOkButtonClicked); + + for (int i=1; i<9; ++i) + { + MyGUI::TextBox* t; + getWidget(t, "AttribVal" + boost::lexical_cast(i)); + + MyGUI::Button* b; + getWidget(b, "Attrib" + boost::lexical_cast(i)); + b->setUserData (i-1); + b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); + + mAttributeValues.push_back(t); + + getWidget(t, "AttribMultiplier" + boost::lexical_cast(i)); + + t->setCaption("x2"); + mAttributeMultipliers.push_back(t); + } + + center(); + + open(); + } + + void LevelupDialog::open() + { + center(); + + mClassImage->setImageTexture ("textures\\levelup\\acrobat.dds"); + + /// \todo replace this with INI-imported texts + int level = 2; + mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); + } + + void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) + { + MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}", std::vector()); + } + + void LevelupDialog::onAttributeClicked (MyGUI::Widget *sender) + { + int index = *sender->getUserData(); + } +} diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp new file mode 100644 index 000000000..181868e9e --- /dev/null +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -0,0 +1,30 @@ +#ifndef MWGUI_LEVELUPDIALOG_H +#define MWGUI_LEVELUPDIALOG_H + +#include "window_base.hpp" + +namespace MWGui +{ + + class LevelupDialog : public WindowBase + { + public: + LevelupDialog(MWBase::WindowManager& parWindowManager); + + virtual void open(); + + private: + MyGUI::Button* mOkButton; + MyGUI::ImageBox* mClassImage; + MyGUI::TextBox* mLevelText; + + std::vector mAttributeValues; + std::vector mAttributeMultipliers; + + void onOkButtonClicked (MyGUI::Widget* sender); + void onAttributeClicked (MyGUI::Widget* sender); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 7e1adcf8b..eb2c52b26 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -22,6 +22,8 @@ namespace MWGui GM_Rest, GM_SpellBuying, + GM_Levelup, + // Startup character creation dialogs GM_Name, GM_Race, diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 99f476574..670f6122e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -44,6 +44,7 @@ #include "spellwindow.hpp" #include "quickkeysmenu.hpp" #include "loadingscreen.hpp" +#include "levelupdialog.hpp" using namespace MWGui; @@ -147,6 +148,7 @@ WindowManager::WindowManager( mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); mQuickKeysMenu = new QuickKeysMenu(*this); + mLevelupDialog = new LevelupDialog(*this); mLoadingScreen = new LoadingScreen(mOgre->getScene (), mOgre->getWindow (), *this); mLoadingScreen->onResChange (w,h); @@ -200,6 +202,7 @@ WindowManager::~WindowManager() delete mAlchemyWindow; delete mSpellWindow; delete mLoadingScreen; + delete mLevelupDialog; cleanupGarbage(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index e16b03e43..09d2f0c7d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -62,6 +62,7 @@ namespace MWGui class AlchemyWindow; class QuickKeysMenu; class LoadingScreen; + class LevelupDialog; class WindowManager : public MWBase::WindowManager { @@ -224,6 +225,7 @@ namespace MWGui SpellWindow* mSpellWindow; QuickKeysMenu* mQuickKeysMenu; LoadingScreen* mLoadingScreen; + LevelupDialog* mLevelupDialog; CharacterCreation* mCharGen; diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index fdf447e69..55230fa76 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -35,7 +35,7 @@ set(MYGUI_FILES openmw_dialogue_window.layout openmw_dialogue_window_skin.xml openmw_edit.skin.xml - openmw.font.xml + openmw_font.xml openmw_hud_box.skin.xml openmw_hud_energybar.skin.xml openmw_hud.layout @@ -51,7 +51,7 @@ set(MYGUI_FILES openmw_map_window.layout openmw_map_window_skin.xml openmw_messagebox.layout - openmw.pointer.xml + openmw_pointer.xml openmw_progress.skin.xml openmw_resources.xml openmw_scroll.layout @@ -72,6 +72,7 @@ set(MYGUI_FILES openmw_magicselection_dialog.layout openmw_spell_buying_window.layout openmw_loading_screen.layout + openmw_levelup_dialog.layout smallbars.png VeraMono.ttf markers.png diff --git a/files/mygui/openmw.font.xml b/files/mygui/openmw_font.xml similarity index 100% rename from files/mygui/openmw.font.xml rename to files/mygui/openmw_font.xml diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout new file mode 100644 index 000000000..6525a1e41 --- /dev/null +++ b/files/mygui/openmw_levelup_dialog.layout @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw.pointer.xml b/files/mygui/openmw_pointer.xml similarity index 100% rename from files/mygui/openmw.pointer.xml rename to files/mygui/openmw_pointer.xml From b7c9285b24b1d9bd205ee36a5bf6676fd10d35be Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 14 Sep 2012 21:12:16 +0200 Subject: [PATCH 606/688] added support for out of world rendering --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 7 +++++++ apps/openmw/mwrender/externalrendering.hpp | 23 ++++++++++++++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 6 ++++++ apps/openmw/mwrender/renderingmanager.hpp | 7 +++++-- apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 7 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 apps/openmw/mwrender/externalrendering.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b9ea8041d..79d7f7af9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows - compositors + compositors externalrendering ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 19405fb7a..a83d6906f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -35,6 +35,11 @@ namespace ESMS struct ESMStore; } +namespace MWRender +{ + class ExternalRendering; +} + namespace MWWorld { class CellStore; @@ -271,6 +276,8 @@ namespace MWBase virtual void togglePlayerLooking(bool enable) = 0; virtual void renderPlayer() = 0; + + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; }; } diff --git a/apps/openmw/mwrender/externalrendering.hpp b/apps/openmw/mwrender/externalrendering.hpp new file mode 100644 index 000000000..33c9afd87 --- /dev/null +++ b/apps/openmw/mwrender/externalrendering.hpp @@ -0,0 +1,23 @@ +#ifndef GAME_RENDERING_EXTERNALRENDERING_H +#define GAME_RENDERING_EXTERNALRENDERING_H + +namespace Ogre +{ + class SceneManager; +} + +namespace MWRender +{ + /// \brief Base class for out of world rendering + class ExternalRendering + { + public: + + virtual void setup (Ogre::SceneManager *sceneManager) = 0; + + virtual ~ExternalRendering() {} + }; +} + +#endif + diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 70d7de552..da49442f6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -34,6 +34,7 @@ #include "water.hpp" #include "compositors.hpp" #include "npcanimation.hpp" +#include "externalrendering.hpp" using namespace MWRender; using namespace Ogre; @@ -882,4 +883,9 @@ bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, boo return mLocalMap->isPositionExplored(nX, nY, x, y, interior); } +void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rendering) +{ + rendering.setup (mRendering.getScene()); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index e5be238bd..e994e4909 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -43,6 +43,7 @@ namespace MWRender class LocalMap; class Water; class Compositors; + class ExternalRendering; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -185,12 +186,14 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList static bool waterShaderSupported(); - virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); ///< see MWRender::LocalMap::getInteriorMapPosition - virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored + void setupExternalRendering (MWRender::ExternalRendering& rendering); + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 869ce94d1..27c354aef 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1222,4 +1222,9 @@ namespace MWWorld { mRendering->renderPlayer(mPlayer->getPlayer()); } + + void World::setupExternalRendering (MWRender::ExternalRendering& rendering) + { + mRendering->setupExternalRendering (rendering); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6279343d4..96e6ae3f8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -307,6 +307,8 @@ namespace MWWorld } virtual void renderPlayer(); + + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); }; } From 3e794a6087bfb708c151170dab8a0e508c66affa Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 01:02:07 +0200 Subject: [PATCH 607/688] remove useless member and removed a warning --- apps/openmw/mwrender/renderingmanager.cpp | 5 ++--- apps/openmw/mwrender/renderingmanager.hpp | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index fea08f108..308a01688 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -95,7 +95,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const if (filter == "anisotropic") tfo = TFO_ANISOTROPIC; else if (filter == "trilinear") tfo = TFO_TRILINEAR; else if (filter == "bilinear") tfo = TFO_BILINEAR; - else if (filter == "none") tfo = TFO_NONE; + else /*if (filter == "none")*/ tfo = TFO_NONE; MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); @@ -176,7 +176,6 @@ RenderingManager::~RenderingManager () delete mOcclusionQuery; delete mCompositors; delete mWater; - delete mPreviewAnimation; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -725,7 +724,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec if (filter == "anisotropic") tfo = TFO_ANISOTROPIC; else if (filter == "trilinear") tfo = TFO_TRILINEAR; else if (filter == "bilinear") tfo = TFO_BILINEAR; - else if (filter == "none") tfo = TFO_NONE; + else /*if (filter == "none")*/ tfo = TFO_NONE; MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b7944c9b9..e994e4909 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -245,8 +245,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Shadows* mShadows; MWRender::Compositors* mCompositors; - - MWRender::NpcAnimation* mPreviewAnimation; }; } From 76b494100e367ecbe5ae80d5cf67f710fd235f6d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 17:12:42 +0200 Subject: [PATCH 608/688] finished? --- apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwgui/charactercreation.cpp | 8 +- apps/openmw/mwgui/charactercreation.hpp | 14 +-- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/hud.hpp | 2 +- apps/openmw/mwgui/levelupdialog.cpp | 147 +++++++++++++++++++++- apps/openmw/mwgui/levelupdialog.hpp | 9 ++ apps/openmw/mwgui/review.cpp | 6 +- apps/openmw/mwgui/review.hpp | 6 +- apps/openmw/mwgui/stats_window.cpp | 16 ++- apps/openmw/mwgui/stats_window.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 7 +- apps/openmw/mwgui/windowmanagerimp.hpp | 4 +- apps/openmw/mwinput/inputmanagerimp.cpp | 9 ++ apps/openmw/mwinput/inputmanagerimp.hpp | 1 + apps/openmw/mwmechanics/actors.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 35 ++++-- apps/openmw/mwmechanics/creaturestats.hpp | 29 +++-- apps/openmw/mwmechanics/npcstats.cpp | 70 ++++++++++- apps/openmw/mwmechanics/npcstats.hpp | 11 ++ apps/openmw/mwscript/docs/vmformat.txt | 6 +- apps/openmw/mwscript/statsextensions.cpp | 50 ++++++++ files/mygui/openmw_levelup_dialog.layout | 10 +- 23 files changed, 381 insertions(+), 67 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 58fe180e9..6e4c10625 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -118,7 +118,7 @@ namespace MWBase /// Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::Stat& value) = 0; virtual void setValue (int parSkill, const MWMechanics::Stat& value) = 0; - virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 7d63f6922..0005b78ad 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -151,7 +151,7 @@ void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat } } -void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) +void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { if (mReviewDialog) { @@ -294,17 +294,17 @@ void CharacterCreation::spawnDialog(const char id) } } -void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) +void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) { mPlayerHealth = value; } -void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) +void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) { mPlayerMagicka = value; } -void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) +void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) { mPlayerFatigue = value; } diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index d65763d0c..28ced2e70 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -35,14 +35,14 @@ namespace MWGui //Show a dialog void spawnDialog(const char id); - void setPlayerHealth (const MWMechanics::DynamicStat& value); + void setPlayerHealth (const MWMechanics::DynamicStat& value); - void setPlayerMagicka (const MWMechanics::DynamicStat& value); + void setPlayerMagicka (const MWMechanics::DynamicStat& value); - void setPlayerFatigue (const MWMechanics::DynamicStat& value); + void setPlayerFatigue (const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); void configureSkills (const SkillList& major, const SkillList& minor); @@ -65,9 +65,9 @@ namespace MWGui std::string mPlayerRaceId; std::string mPlayerBirthSignId; ESM::Class mPlayerClass; - MWMechanics::DynamicStat mPlayerHealth; - MWMechanics::DynamicStat mPlayerMagicka; - MWMechanics::DynamicStat mPlayerFatigue; + MWMechanics::DynamicStat mPlayerHealth; + MWMechanics::DynamicStat mPlayerMagicka; + MWMechanics::DynamicStat mPlayerFatigue; //Class generation vars unsigned mGenerateClassStep; // Keeps track of current step in Generate Class dialog diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index b14833553..72dc0c7fb 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -161,7 +161,7 @@ void HUD::setEffect(const char *img) mEffect1->setImageTexture(img); } -void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) +void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) { static const char *ids[] = { diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index de4228e87..013ad59f0 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -14,7 +14,7 @@ namespace MWGui public: HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop); void setEffect(const char *img); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setFPS(float fps); void setTriangleCount(unsigned int count); void setBatchCount(unsigned int count); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 9f57a2bdd..610c22bf7 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -4,6 +4,17 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/stat.hpp" + +#include +#include namespace MWGui { @@ -26,6 +37,7 @@ namespace MWGui getWidget(b, "Attrib" + boost::lexical_cast(i)); b->setUserData (i-1); b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); + mAttributes.push_back(b); mAttributeValues.push_back(t); @@ -35,29 +47,152 @@ namespace MWGui mAttributeMultipliers.push_back(t); } - center(); + int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; + for (int i=0; i<3; ++i) + { + MyGUI::ImageBox* image = mMainWidget->createWidget("ImageBox", MyGUI::IntCoord(curX,250,16,16), MyGUI::Align::Default); + image->setImageTexture ("icons\\tx_goldicon.dds"); + curX += 24+2; + mCoins.push_back(image); + } - open(); + center(); + } + + void LevelupDialog::setAttributeValues() + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + + for (int i=0; i<8; ++i) + { + int val = creatureStats.getAttribute (i).getBase (); + if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end()) + { + val += pcStats.getLevelupAttributeMultiplier (i); + } + + if (val >= 100) + val = 100; + + mAttributeValues[i]->setCaption(boost::lexical_cast(val)); + } + } + + + void LevelupDialog::resetCoins () + { + int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; + for (int i=0; i<3; ++i) + { + MyGUI::ImageBox* image = mCoins[i]; + image->setCoord(MyGUI::IntCoord(curX,250,16,16)); + curX += 24+2; + } + } + + void LevelupDialog::assignCoins () + { + resetCoins(); + for (unsigned int i=0; igetCaption() == "" ? 0 : 30; + + MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(24+xdiff,-4); + image->setPosition(pos); + } + + setAttributeValues(); } void LevelupDialog::open() { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + center(); - mClassImage->setImageTexture ("textures\\levelup\\acrobat.dds"); + mSpentAttributes.clear(); + resetCoins(); + + setAttributeValues(); + + // set class image + const ESM::Class& playerClass = MWBase::Environment::get().getWorld ()->getPlayer ().getClass (); + // retrieve the ID to this class + std::string classId; + std::map list = MWBase::Environment::get().getWorld()->getStore ().classes.list; + for (std::map::iterator it = list.begin(); it != list.end(); ++it) + { + if (playerClass.name == it->second.name) + classId = it->first; + } + mClassImage->setImageTexture ("textures\\levelup\\" + classId + ".dds"); /// \todo replace this with INI-imported texts - int level = 2; + int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); + + for (int i=0; i<8; ++i) + { + MyGUI::TextBox* text = mAttributeMultipliers[i]; + int mult = pcStats.getLevelupAttributeMultiplier (i); + text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast(mult)); + } } void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) { - MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}", std::vector()); + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + + if (mSpentAttributes.size() < 3) + MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}", std::vector()); + else + { + // increase attributes + for (int i=0; i<3; ++i) + { + MWMechanics::Stat& attribute = creatureStats.getAttribute(mSpentAttributes[i]); + attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); + + if (attribute.getBase() >= 100) + attribute.setBase(100); + } + + // "When you gain a level, in addition to increasing three primary attributes, your Health + // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, + // the Health increase is calculated from the increased Endurance" + creatureStats.increaseLevelHealthBonus (creatureStats.getAttribute(ESM::Attribute::Endurance).getBase() * 0.1f); + + creatureStats.setLevel (creatureStats.getLevel()+1); + pcStats.levelUp (); + + mWindowManager.removeGuiMode (GM_Rest); + } + } void LevelupDialog::onAttributeClicked (MyGUI::Widget *sender) { - int index = *sender->getUserData(); + int attribute = *sender->getUserData(); + + std::vector::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute); + if (found != mSpentAttributes.end()) + mSpentAttributes.erase (found); + else + { + if (mSpentAttributes.size() == 3) + mSpentAttributes[2] = attribute; + else + mSpentAttributes.push_back(attribute); + } + assignCoins(); } } diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp index 181868e9e..f5b24530d 100644 --- a/apps/openmw/mwgui/levelupdialog.hpp +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -18,11 +18,20 @@ namespace MWGui MyGUI::ImageBox* mClassImage; MyGUI::TextBox* mLevelText; + std::vector mAttributes; std::vector mAttributeValues; std::vector mAttributeMultipliers; + std::vector mCoins; + + std::vector mSpentAttributes; void onOkButtonClicked (MyGUI::Widget* sender); void onAttributeClicked (MyGUI::Widget* sender); + + void assignCoins(); + void resetCoins(); + + void setAttributeValues(); }; } diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 76b211b18..411fead08 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -134,21 +134,21 @@ void ReviewDialog::setBirthSign(const std::string& signId) } } -void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) +void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) { mHealth->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); } -void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) +void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) { mMagicka->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); } -void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) +void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) { mFatigue->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 058e3cff3..2b0740234 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -35,9 +35,9 @@ namespace MWGui void setClass(const ESM::Class& class_); void setBirthSign (const std::string &signId); - void setHealth(const MWMechanics::DynamicStat& value); - void setMagicka(const MWMechanics::DynamicStat& value); - void setFatigue(const MWMechanics::DynamicStat& value); + void setHealth(const MWMechanics::DynamicStat& value); + void setMagicka(const MWMechanics::DynamicStat& value); + void setFatigue(const MWMechanics::DynamicStat& value); void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index ffbfd7d78..88bc3dc90 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -95,6 +95,7 @@ void StatsWindow::setBar(const std::string& name, const std::string& tname, int getWidget(pt, name); pt->setProgressRange(max); pt->setProgressPosition(val); + std::cout << "set bar " << name << val << " " << max << std::endl; std::stringstream out; out << val << "/" << max; @@ -137,7 +138,7 @@ void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& } } -void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) +void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { static const char *ids[] = { @@ -150,7 +151,7 @@ void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicSta if (ids[i]==id) { std::string id (ids[i]); - setBar (id, id + "T", value.getCurrent(), value.getModified()); + setBar (id, id + "T", static_cast(value.getCurrent()), static_cast(value.getModified())); // health, magicka, fatigue tooltip MyGUI::Widget* w; @@ -236,12 +237,21 @@ void StatsWindow::configureSkills (const std::vector& major, const std::vec void StatsWindow::onFrame () { - if (mMainWidget->getVisible()) + if (!mMainWidget->getVisible()) return; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + // level progress + MyGUI::Widget* levelWidget; + for (int i=0; i<2; ++i) + { + getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); + levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); + levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); + } + setFactions(PCstats.getFactionRanks()); setBirthSign(MWBase::Environment::get().getWorld()->getPlayer().getBirthsign()); diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index 73c4706dd..75f8c568b 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -32,7 +32,7 @@ namespace MWGui /// Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 670f6122e..465f2c239 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -250,6 +250,7 @@ void WindowManager::updateVisible() mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); mQuickKeysMenu->setVisible(false); + mLevelupDialog->setVisible(false); mHud->setVisible(true); @@ -301,6 +302,9 @@ void WindowManager::updateVisible() case GM_Alchemy: mAlchemyWindow->setVisible(true); break; + case GM_Rest: + mLevelupDialog->setVisible(true); + break; case GM_Name: case GM_Race: case GM_Class: @@ -398,8 +402,9 @@ void WindowManager::setValue (int parSkill, const MWMechanics::Stat& valu mPlayerSkillValues[parSkill] = value; } -void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) +void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { + std::cout << " set value " << id << value.getModified () << std::endl; mStatsWindow->setValue (id, value); mHud->setValue (id, value); mCharGen->setValue(id, value); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 09d2f0c7d..131029ed4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -118,7 +118,7 @@ namespace MWGui ///< Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::Stat& value); virtual void setValue (int parSkill, const MWMechanics::Stat& value); - virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); @@ -243,7 +243,7 @@ namespace MWGui std::map > mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; std::map > mPlayerSkillValues; - MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; + MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; MyGUI::Gui *mGui; // Gui diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b198785d2..724dccd8e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -190,6 +190,9 @@ namespace MWInput case A_ToggleWeapon: toggleWeapon (); break; + case A_Rest: + rest(); + break; case A_ToggleSpell: toggleSpell (); break; @@ -543,6 +546,12 @@ namespace MWInput } } + void InputManager::rest() + { + if (!mWindows.isGuiMode ()) + mWindows.pushGuiMode (MWGui::GM_Rest); + } + void InputManager::screenshot() { mEngine.screenshot(); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 7d03f1d5b..5e6169f68 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -168,6 +168,7 @@ namespace MWInput void toggleWalking(); void toggleAutoMove(); void exitNow(); + void rest(); void quickKey (int index); void showQuickKeysMenu(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index bd037df9f..b4cc40fec 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -68,7 +68,7 @@ namespace MWMechanics creatureStats.getMagicEffects().get (EffectKey (84)).mMagnitude * 0.1 + 0.5; creatureStats.getHealth().setBase( - static_cast (0.5 * (strength + endurance))); + static_cast (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ()); creatureStats.getMagicka().setBase( static_cast (intelligence + magickaFactor * intelligence)); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 91a9225fe..fc0523141 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -9,6 +9,21 @@ namespace MWMechanics { + CreatureStats::CreatureStats() + : mLevelHealthBonus(0.f) + { + } + + void CreatureStats::increaseLevelHealthBonus (float value) + { + mLevelHealthBonus += value; + } + + float CreatureStats::getLevelHealthBonus () const + { + return mLevelHealthBonus; + } + const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; @@ -40,17 +55,17 @@ namespace MWMechanics return mAttributes[index]; } - const DynamicStat &CreatureStats::getHealth() const + const DynamicStat &CreatureStats::getHealth() const { return mDynamic[0]; } - const DynamicStat &CreatureStats::getMagicka() const + const DynamicStat &CreatureStats::getMagicka() const { return mDynamic[1]; } - const DynamicStat &CreatureStats::getFatigue() const + const DynamicStat &CreatureStats::getFatigue() const { return mDynamic[2]; } @@ -103,22 +118,22 @@ namespace MWMechanics return mAttributes[index]; } - DynamicStat &CreatureStats::getHealth() + DynamicStat &CreatureStats::getHealth() { return mDynamic[0]; } - DynamicStat &CreatureStats::getMagicka() + DynamicStat &CreatureStats::getMagicka() { return mDynamic[1]; } - DynamicStat &CreatureStats::getFatigue() + DynamicStat &CreatureStats::getFatigue() { return mDynamic[2]; } - DynamicStat &CreatureStats::getDynamic(int index) + DynamicStat &CreatureStats::getDynamic(int index) { if (index < 0 || index > 2) { throw std::runtime_error("dynamic stat index is out of range"); @@ -154,17 +169,17 @@ namespace MWMechanics mAttributes[index] = value; } - void CreatureStats::setHealth(const DynamicStat &value) + void CreatureStats::setHealth(const DynamicStat &value) { mDynamic[0] = value; } - void CreatureStats::setMagicka(const DynamicStat &value) + void CreatureStats::setMagicka(const DynamicStat &value) { mDynamic[1] = value; } - void CreatureStats::setFatigue(const DynamicStat &value) + void CreatureStats::setFatigue(const DynamicStat &value) { mDynamic[2] = value; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index a6fb6779a..7a1e46f56 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -19,7 +19,7 @@ namespace MWMechanics class CreatureStats { Stat mAttributes[8]; - DynamicStat mDynamic[3]; // health, magicka, fatigue + DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; Spells mSpells; ActiveSpells mActiveSpells; @@ -30,15 +30,18 @@ namespace MWMechanics int mAlarm; AiSequence mAiSequence; + float mLevelHealthBonus; + public: + CreatureStats(); const Stat & getAttribute(int index) const; - const DynamicStat & getHealth() const; + const DynamicStat & getHealth() const; - const DynamicStat & getMagicka() const; + const DynamicStat & getMagicka() const; - const DynamicStat & getFatigue() const; + const DynamicStat & getFatigue() const; const Spells & getSpells() const; @@ -59,13 +62,13 @@ namespace MWMechanics Stat & getAttribute(int index); - DynamicStat & getHealth(); + DynamicStat & getHealth(); - DynamicStat & getMagicka(); + DynamicStat & getMagicka(); - DynamicStat & getFatigue(); + DynamicStat & getFatigue(); - DynamicStat & getDynamic(int index); + DynamicStat & getDynamic(int index); Spells & getSpells(); @@ -76,11 +79,11 @@ namespace MWMechanics void setAttribute(int index, const Stat &value); - void setHealth(const DynamicStat &value); + void setHealth(const DynamicStat &value); - void setMagicka(const DynamicStat &value); + void setMagicka(const DynamicStat &value); - void setFatigue(const DynamicStat &value); + void setFatigue(const DynamicStat &value); void setSpells(const Spells &spells); @@ -104,6 +107,10 @@ namespace MWMechanics float getFatigueTerm() const; ///< Return effective fatigue + + // small hack to allow the fact that Health permanently increases by 10% of endurance on each level up + void increaseLevelHealthBonus(float value); + float getLevelHealthBonus() const; }; } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index d2908e26e..fc99c44a8 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -12,10 +12,16 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" MWMechanics::NpcStats::NpcStats() : mMovementFlags (0), mDrawState (DrawState_Nothing) -{} +, mLevelProgress(0) +{ + mSkillIncreases.resize (ESM::Attribute::Length); + for (int i=0; i (base)!=level) + { + // skill leveled up base = level+1; + // if this is a major or minor skill of the class, increase level progress + //bool levelProgress = false; + bool levelProgress = true; + for (int i=0; i<2; ++i) + for (int j=0; j<5; ++j) + { + int skill = class_.data.skills[j][i]; + if (skill == skillIndex) + levelProgress = true; + } + + if (!levelProgress) + std::cout <<"This is not a level skilL" << std::endl; + + mLevelProgress += levelProgress; + + // check the attribute this skill belongs to + const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().skills.find(skillIndex); + ++mSkillIncreases[skill->data.attribute]; + + if (mLevelProgress >= 10) + { + // levelup is possible now + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", std::vector()); + } + } + getSkill (skillIndex).setBase (base); } + +int MWMechanics::NpcStats::getLevelProgress () const +{ + return mLevelProgress; +} + +void MWMechanics::NpcStats::levelUp() +{ + mLevelProgress -= 10; + for (int i=0; i #include #include +#include #include "stat.hpp" #include "drawstate.hpp" @@ -45,6 +46,10 @@ namespace MWMechanics unsigned int mMovementFlags; Stat mSkill[27]; + int mLevelProgress; // 0-10 + + std::vector mSkillIncreases; // number of skill increases for each attribute + public: NpcStats(); @@ -73,6 +78,12 @@ namespace MWMechanics void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1); ///< Increase skill by usage. + + int getLevelProgress() const; + + int getLevelupAttributeMultiplier(int attribute) const; + + void levelUp(); }; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 59b9bca35..8052c6a08 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -183,4 +183,8 @@ op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference op 0x2000174: ToggleVanityMode op 0x2000175-0x200018B: Get controls disabled -opcodes 0x200018C-0x3ffffff unused +op 0x200018C: GetLevel +op 0x200018D: GetLevel, explicit reference +op 0x200018E: SetLevel +op 0x200018F: SetLevel, explicit reference +opcodes 0x200018F-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 5113c9dbf..f49d684a5 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -32,6 +32,42 @@ namespace MWScript { namespace Stats { + template + class OpGetLevel : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = + MWWorld::Class::get (ptr) + .getCreatureStats (ptr) + .getLevel(); + + runtime.push (value); + } + }; + + template + class OpSetLevel : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = runtime[0].mInteger; + runtime.pop(); + + MWWorld::Class::get (ptr) + .getCreatureStats (ptr) + .setLevel(value); + } + }; + template class OpGetAttribute : public Interpreter::Opcode0 { @@ -592,6 +628,11 @@ namespace MWScript const int opcodeModDisposition = 0x200014d; const int opcodeModDispositionExplicit = 0x200014e; + const int opcodeGetLevel = 0x200018c; + const int opcodeGetLevelExplicit = 0x200018d; + const int opcodeSetLevel = 0x200018e; + const int opcodeSetLevelExplicit = 0x200018f; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -674,6 +715,9 @@ namespace MWScript extensions.registerInstruction("moddisposition","l",opcodeModDisposition, opcodeModDispositionExplicit); extensions.registerFunction("getpcrank",'l',"/S",opcodeGetPCRank,opcodeGetPCRankExplicit); + + extensions.registerInstruction("setlevel", "l", opcodeSetLevel, opcodeSetLevelExplicit); + extensions.registerFunction("getlevel", 'l', "", opcodeGetLevel, opcodeGetLevelExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -745,6 +789,12 @@ namespace MWScript interpreter.installSegment5(opcodeModDispositionExplicit,new OpModDisposition); interpreter.installSegment3(opcodeGetPCRank,new OpGetPCRank); interpreter.installSegment3(opcodeGetPCRankExplicit,new OpGetPCRank); + + interpreter.installSegment5 (opcodeGetLevel, new OpGetLevel); + interpreter.installSegment5 (opcodeGetLevelExplicit, new OpGetLevel); + interpreter.installSegment5 (opcodeSetLevel, new OpSetLevel); + interpreter.installSegment5 (opcodeSetLevelExplicit, new OpSetLevel); + } } } diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout index 6525a1e41..86e65e99a 100644 --- a/files/mygui/openmw_levelup_dialog.layout +++ b/files/mygui/openmw_levelup_dialog.layout @@ -4,7 +4,7 @@ - + @@ -31,7 +31,6 @@ - @@ -45,7 +44,6 @@ - @@ -59,7 +57,6 @@ - @@ -73,7 +70,6 @@ - @@ -88,7 +84,6 @@ - @@ -102,7 +97,6 @@ - @@ -116,7 +110,6 @@ - @@ -130,7 +123,6 @@ - From b34b894d6a743bbc423321f3ba72ce19f520a3eb Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 17:20:47 +0200 Subject: [PATCH 609/688] removed some debug stuff --- apps/openmw/mwgui/levelupdialog.cpp | 1 - apps/openmw/mwgui/stats_window.cpp | 1 - apps/openmw/mwgui/windowmanagerimp.cpp | 1 - apps/openmw/mwmechanics/npcstats.cpp | 11 ++--------- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 610c22bf7..3616283d9 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -43,7 +43,6 @@ namespace MWGui getWidget(t, "AttribMultiplier" + boost::lexical_cast(i)); - t->setCaption("x2"); mAttributeMultipliers.push_back(t); } diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 88bc3dc90..701c39b0d 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -95,7 +95,6 @@ void StatsWindow::setBar(const std::string& name, const std::string& tname, int getWidget(pt, name); pt->setProgressRange(max); pt->setProgressPosition(val); - std::cout << "set bar " << name << val << " " << max << std::endl; std::stringstream out; out << val << "/" << max; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 465f2c239..8f7c0d17a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -404,7 +404,6 @@ void WindowManager::setValue (int parSkill, const MWMechanics::Stat& valu void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { - std::cout << " set value " << id << value.getModified () << std::endl; mStatsWindow->setValue (id, value); mHud->setValue (id, value); mCharGen->setValue(id, value); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index fc99c44a8..2980aed59 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -128,10 +128,7 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla throw std::runtime_error ("invalid skill specialisation factor"); } - //return 1.0 / (level +1) * (1.0 / (skillFactor)) * typeFactor * specialisationFactor; - - ///FIXME: TEST FOR FASTER LEVELLING - return 1.0 / (level +1) * (1.0 / (skillFactor/100)) * typeFactor * specialisationFactor; + return 1.0 / (level +1) * (1.0 / (skillFactor)) * typeFactor * specialisationFactor; } void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType) @@ -148,8 +145,7 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, base = level+1; // if this is a major or minor skill of the class, increase level progress - //bool levelProgress = false; - bool levelProgress = true; + bool levelProgress = false; for (int i=0; i<2; ++i) for (int j=0; j<5; ++j) { @@ -158,9 +154,6 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, levelProgress = true; } - if (!levelProgress) - std::cout <<"This is not a level skilL" << std::endl; - mLevelProgress += levelProgress; // check the attribute this skill belongs to From 234f8fa5d57b41f40347250cbebead2f6f5b8253 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 18:35:59 +0200 Subject: [PATCH 610/688] added the missing sound & notification for skill increase --- apps/openmw/mwmechanics/npcstats.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 2980aed59..2c8cc06de 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include #include @@ -13,6 +15,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() : mMovementFlags (0), mDrawState (DrawState_Nothing) @@ -160,6 +163,16 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().skills.find(skillIndex); ++mSkillIncreases[skill->data.attribute]; + // Play sound & skill progress notification + /// \todo check if character is the player, if levelling is ever implemented for NPCs + MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); + + std::stringstream message; + message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) + % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") + % base; + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector()); + if (mLevelProgress >= 10) { // levelup is possible now From f5237ff1a65e7b56b54750c826173ad4bfec5312 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 19:06:56 +0200 Subject: [PATCH 611/688] skill gain from books --- apps/openmw/mwmechanics/npcstats.cpp | 74 +++++++++++++++++----------- apps/openmw/mwmechanics/npcstats.hpp | 2 + apps/openmw/mwworld/actionread.cpp | 24 +++++++++ 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 2c8cc06de..0f5d2c95e 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -145,40 +145,54 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if (static_cast (base)!=level) { // skill leveled up + increaseSkill(skillIndex, class_, false); + } + else + getSkill (skillIndex).setBase (base); +} + +void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) +{ + float base = getSkill (skillIndex).getBase(); + + int level = static_cast (base); + + if (preserveProgress) + base += 1; + else base = level+1; - // if this is a major or minor skill of the class, increase level progress - bool levelProgress = false; - for (int i=0; i<2; ++i) - for (int j=0; j<5; ++j) - { - int skill = class_.data.skills[j][i]; - if (skill == skillIndex) - levelProgress = true; - } - - mLevelProgress += levelProgress; - - // check the attribute this skill belongs to - const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().skills.find(skillIndex); - ++mSkillIncreases[skill->data.attribute]; - - // Play sound & skill progress notification - /// \todo check if character is the player, if levelling is ever implemented for NPCs - MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); - - std::stringstream message; - message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) - % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") - % base; - MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector()); - - if (mLevelProgress >= 10) + // if this is a major or minor skill of the class, increase level progress + bool levelProgress = false; + for (int i=0; i<2; ++i) + for (int j=0; j<5; ++j) { - // levelup is possible now - MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", std::vector()); + int skill = class_.data.skills[j][i]; + if (skill == skillIndex) + levelProgress = true; } - } + + mLevelProgress += levelProgress; + + // check the attribute this skill belongs to + const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().skills.find(skillIndex); + ++mSkillIncreases[skill->data.attribute]; + + // Play sound & skill progress notification + /// \todo check if character is the player, if levelling is ever implemented for NPCs + MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); + + std::stringstream message; + message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) + % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") + % base; + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector()); + + if (mLevelProgress >= 10) + { + // levelup is possible now + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", std::vector()); + } getSkill (skillIndex).setBase (base); } diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index ea8f92609..7c3055783 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -79,6 +79,8 @@ namespace MWMechanics void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1); ///< Increase skill by usage. + void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress); + int getLevelProgress() const; int getLevelupAttributeMultiplier(int attribute) const; diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 7fb2b23e7..d049bf5b5 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -2,10 +2,18 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" #include "../mwgui/bookwindow.hpp" #include "../mwgui/scrollwindow.hpp" +#include + namespace MWWorld { ActionRead::ActionRead (const MWWorld::Ptr& object) : Action (false, object) @@ -26,5 +34,21 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Book); MWBase::Environment::get().getWindowManager()->getBookWindow()->open(getTarget()); } + + if (ref->base->data.skillID >= 0 && ref->base->data.skillID < ESM::Skill::Length) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); + MWWorld::LiveCellRef *playerRef = player.get(); + const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().classes.find ( + playerRef->base->cls); + + npcStats.increaseSkill (ref->base->data.skillID, *class_, true); + + // Remove skill from the book + /// \todo This will have to be changed later + const_cast(ref->base)->data.skillID = -1; + } + } } From 0dc242c60318896730243c6c292d27f92d272b11 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 19:10:48 +0200 Subject: [PATCH 612/688] don't increase skill beyond 100 --- apps/openmw/mwmechanics/npcstats.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 0f5d2c95e..f2b7df6cb 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -157,6 +157,9 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas int level = static_cast (base); + if (level == 100) + return; + if (preserveProgress) base += 1; else From cf358fa79d1e401b4025f6db39b9252192fe63a2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 19:20:13 +0200 Subject: [PATCH 613/688] >= instead of == --- apps/openmw/mwmechanics/npcstats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index f2b7df6cb..5e3896af2 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -157,7 +157,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas int level = static_cast (base); - if (level == 100) + if (level >= 100) return; if (preserveProgress) From 02bca98e9f45e0d13157d031f67593e069e55800 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 15 Sep 2012 13:23:49 -0400 Subject: [PATCH 614/688] Setrotate working --- apps/openmw/mwworld/physicssystem.cpp | 5 +---- libs/openengine/bullet/physic.cpp | 20 +++++++++----------- libs/openengine/bullet/physic.hpp | 11 ++++++++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 243ec61ba..5e58f61a3 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -257,10 +257,7 @@ namespace MWWorld const Ogre::Vector3& position, float scale, const Ogre::Quaternion& rotation) { //TODO:optimize this. Searching the std::map isn't very efficient i think. - std::cout << "NPC position" << position << "\n"; mEngine->addCharacter(handle, mesh, position, scale, rotation); - - } void PhysicsSystem::removeObject (const std::string& handle) @@ -309,7 +306,7 @@ namespace MWWorld if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { //Needs to be changed - act->setRotation(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w)); + act->setRotation(rotation); } if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) { diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 2824da4ca..f3cebfb40 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -29,7 +29,9 @@ namespace Physic PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale): mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0), mBody(0), collisionMode(false), mBoxRotation(0,0,0,0) { - mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, &mBoxRotationInverse); + mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); + Ogre::Quaternion inverse = mBoxRotation.Inverse(); + mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map } @@ -68,10 +70,11 @@ namespace Physic - void PhysicActor::setRotation(const btQuaternion& quat) + void PhysicActor::setRotation(const Ogre::Quaternion quat) { - //externalGhostObject->getWorldTransform().setRotation( quat ); - //internalGhostObject->getWorldTransform().setRotation( quat ); + if(!quat.equals(getRotation(), Ogre::Radian(0))){ + mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); + } } Ogre::Vector3 PhysicActor::getPosition(void) @@ -97,7 +100,6 @@ namespace Physic } void PhysicActor::setScale(float scale){ - std::cout << "Trying to set scale to " << scale << "\n"; Ogre::Vector3 position = getPosition(); Ogre::Quaternion rotation = getRotation(); //We only need to change the scaled box translation, box rotations remain the same. @@ -334,7 +336,7 @@ namespace Physic } RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3* scaledBoxTranslation, btQuaternion* boxRotation, btQuaternion* boxRotationInverse) + Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation) { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; @@ -357,11 +359,7 @@ namespace Physic if(scaledBoxTranslation != 0) *scaledBoxTranslation = shape->boxTranslation * scale; if(boxRotation != 0) - *boxRotation = btQuaternion(shape->boxRotation.x, shape->boxRotation.y, shape->boxRotation.z,shape->boxRotation.w); - if(boxRotationInverse != 0){ - Ogre::Quaternion inverse = shape->boxRotation.Inverse(); - *boxRotationInverse = btQuaternion(inverse.x,inverse.y,inverse.z,inverse.w); - } + *boxRotation = shape->boxRotation; adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 2568cc36f..7ea6a323d 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -67,7 +67,12 @@ namespace Physic */ void setWalkDirection(const btVector3& mvt); - void setRotation(const btQuaternion& quat); + /** + * This adjusts the rotation of a PhysicActor + * If we have any problems with this (getting stuck in pmove) we should change it + * from setting the visual orientation to setting the orientation of the rigid body directly. + */ + void setRotation(const Ogre::Quaternion quat); void setGravity(float gravity); @@ -92,7 +97,7 @@ namespace Physic OEngine::Physic::RigidBody* mBody; Ogre::Vector3 mBoxScaledTranslation; btQuaternion mBoxRotationInverse; - btQuaternion mBoxRotation; + Ogre::Quaternion mBoxRotation; bool collisionMode; std::string mMesh; PhysicEngine* mEngine; @@ -144,7 +149,7 @@ namespace Physic * After created, the body is set to the correct rotation, position, and scale */ RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3* scaledBoxTranslation = 0, btQuaternion* boxRotation = 0, btQuaternion* boxRotationInverse = 0); + Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0); /** * Adjusts a rigid body to the right position and rotation From 3e5ab069a64d1a4cf5c1eec5f4aa5d8dde0aaca7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 19:52:18 +0200 Subject: [PATCH 615/688] remove the const cast --- apps/openmw/mwworld/actionread.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index d049bf5b5..f21fd35ec 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -45,9 +45,8 @@ namespace MWWorld npcStats.increaseSkill (ref->base->data.skillID, *class_, true); - // Remove skill from the book - /// \todo This will have to be changed later - const_cast(ref->base)->data.skillID = -1; + /// \todo Remove skill from the book. Right now you can read as many times as you want + /// and the skill will still increase. } } From 9cf1cbc89b514b744f6e9d667bfba76bd45beef4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 20:03:53 +0200 Subject: [PATCH 616/688] remove book skill gain --- apps/openmw/mwworld/actionread.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index f21fd35ec..fe5e2d58f 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -35,6 +35,8 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->getBookWindow()->open(getTarget()); } + /* + // Skill gain from books if (ref->base->data.skillID >= 0 && ref->base->data.skillID < ESM::Skill::Length) { MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); @@ -48,6 +50,7 @@ namespace MWWorld /// \todo Remove skill from the book. Right now you can read as many times as you want /// and the skill will still increase. } + */ } } From 896428c129124d64f8479380881f1b1bd4568847 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Sep 2012 20:18:41 +0200 Subject: [PATCH 617/688] enableRestMenu & enableLevelupMenu --- apps/openmw/mwbase/windowmanager.hpp | 3 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwgui/windowmanagerimp.hpp | 5 +++++ apps/openmw/mwinput/inputmanagerimp.cpp | 7 +++++-- apps/openmw/mwscript/guiextensions.cpp | 16 +++++++++++----- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 6e4c10625..0fd78225a 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -219,6 +219,9 @@ namespace MWBase virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0; virtual void loadingDone() = 0; + + virtual void enableRest() = 0; + virtual bool getRestEnabled() = 0; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 8f7c0d17a..0241cc734 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -85,6 +85,7 @@ WindowManager::WindowManager( , mGarbageDialogs() , mShown(GW_ALL) , mAllowed(newGame ? GW_None : GW_ALL) + , mRestAllowed(newGame ? false : true) , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 131029ed4..a02aa17b5 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -200,6 +200,9 @@ namespace MWGui virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total); virtual void loadingDone(); + virtual void enableRest() { mRestAllowed = true; } + virtual bool getRestEnabled() { return mRestAllowed; } + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; @@ -260,6 +263,8 @@ namespace MWGui allow() and disableAll(). */ GuiWindow mAllowed; + // is the rest window allowed? + bool mRestAllowed; void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 724dccd8e..c3e131440 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -548,8 +548,11 @@ namespace MWInput void InputManager::rest() { - if (!mWindows.isGuiMode ()) - mWindows.pushGuiMode (MWGui::GM_Rest); + if (!mWindows.getRestEnabled () || mWindows.isGuiMode ()) + return; + + /// \todo check if resting is currently allowed (enemies nearby?) + mWindows.pushGuiMode (MWGui::GM_Rest); } void InputManager::screenshot() diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index d740e5feb..7f3b9777b 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -30,6 +30,16 @@ namespace MWScript } }; + class OpEnableRest : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::Environment::get().getWindowManager()->enableRest(); + } + }; + class OpShowDialogue : public Interpreter::Opcode0 { MWGui::GuiMode mDialogue; @@ -146,12 +156,8 @@ opcodeEnableStatsReviewMenu); interpreter.installSegment5 (opcodeEnableStatsMenu, new OpEnableWindow (MWGui::GW_Stats)); - /* Not done yet. Enabling rest mode is not really a gui - issue, it's a gameplay issue. - interpreter.installSegment5 (opcodeEnableRest, - new OpEnableDialogue (MWGui::GM_Rest)); - */ + new OpEnableRest ()); interpreter.installSegment5 (opcodeShowRestMenu, new OpShowDialogue (MWGui::GM_Rest)); From c161a3850fe2ef9806dbbfcc780c819d2de589b7 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Sat, 15 Sep 2012 15:36:22 -0400 Subject: [PATCH 618/688] Use correct type for iterator Fixes building on Windows with vs2010. --- libs/openengine/ogre/selectionbuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 3ecb99f3b..8dd12b653 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -73,7 +73,7 @@ namespace Render int id = Ogre::any_cast(subEntity->getParent ()->getUserObjectBindings().getUserAny()); bool found = false; Ogre::ColourValue colour; - for (std::map::iterator it = mColourMap.begin(); it != mColourMap.end(); ++it) + for (std::map::iterator it = mColourMap.begin(); it != mColourMap.end(); ++it) { if (it->second == id) { From f21faf197c0be14d82b25ccd425c1547a36ba7aa Mon Sep 17 00:00:00 2001 From: Edmondo Tommasina Date: Sat, 15 Sep 2012 22:19:44 +0200 Subject: [PATCH 619/688] =?UTF-8?q?selectionbuffer.cpp:=20fix=20=E2=80=98r?= =?UTF-8?q?untime=5Ferror=E2=80=99=20is=20not=20a=20member=20of=20?= =?UTF-8?q?=E2=80=98std=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/openengine/ogre/selectionbuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 3ecb99f3b..cb7f41a1c 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include From 256dff0a29c4cc9030384f49ee1c3e228a024095 Mon Sep 17 00:00:00 2001 From: Michael Mc Donnell Date: Sat, 15 Sep 2012 16:21:07 -0400 Subject: [PATCH 620/688] Disable warning 4800 for Visual Studio Visual Studio 2010 complains about constructs such as: int myInt = 1; bool myBool = myInt; Which are fine with most compilers. It would instead like: int myInt = 1; bool myBool = (myInt != 0); Warning 4800 is just an optimization warning and is therefore safe to disable. This patch disables warning 4800. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e01b3d26b..0a24998d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,6 +513,7 @@ if (WIN32) 4309 # Variable overflow, trying to store 128 in a signed char for example 4355 # Using 'this' in member initialization list 4701 # Potentially uninitialized local variable used + 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt ) foreach(d ${WARNINGS_DISABLE}) @@ -524,6 +525,7 @@ if (WIN32) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif (BUILD_LAUNCHER) set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif(MSVC) # Same for MinGW From f586f53a42e3ab3c4f10745c0ca2dc9b11298948 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 15 Sep 2012 16:45:50 -0400 Subject: [PATCH 621/688] positioning --- apps/openmw/mwworld/physicssystem.cpp | 7 +++++-- apps/openmw/mwworld/worldimp.cpp | 2 +- libs/openengine/bullet/physic.cpp | 5 ++--- libs/openengine/bullet/physic.hpp | 22 +++++++++++++++++++--- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 5e58f61a3..d0291b01a 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -282,8 +282,11 @@ namespace MWWorld tr.setOrigin(btVector3(position.x,position.y,position.z)); body->setWorldTransform(tr); } - else + else{ + //For objects that contain a box shape. + //Do any such objects exist? Perhaps animated objects? mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, position, node->getOrientation()); + } } if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { @@ -295,7 +298,7 @@ namespace MWWorld } else { - act->setPosition(btVector3(position.x,position.y,position.z)); + act->setPosition(position); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 55c2f4161..3ad58dc7d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -637,7 +637,7 @@ namespace MWWorld rot.x = Ogre::Degree(x).valueRadians(); rot.y = Ogre::Degree(y).valueRadians(); rot.z = Ogre::Degree(z).valueRadians(); - + if (mRendering->rotateObject(ptr, rot, adjust)) { float *objRot = ptr.getRefData().getPosition().rot; objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index f3cebfb40..9c387e140 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -93,10 +93,9 @@ namespace Physic return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); } - void PhysicActor::setPosition(const btVector3& pos) + void PhysicActor::setPosition(const Ogre::Vector3 pos) { - //internalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); - //externalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); + mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); } void PhysicActor::setScale(float scale){ diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 7ea6a323d..1598d3e87 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -51,7 +51,8 @@ namespace Physic }; /** - * A physic Actor use a modifed KinematicCharacterController taken in the bullet forum. + * A physic actor uses a rigid body based on box shapes. + * Pmove is used to move the physic actor around the dynamic world. */ class PhysicActor { @@ -82,15 +83,29 @@ namespace Physic bool getCollisionMode(); + /** + * This returns the visual position of the PhysicActor (used to position a scenenode). + * Note - this is different from the position of the contained mBody. + */ Ogre::Vector3 getPosition(void); + /** + * Returns the visual orientation of the PhysicActor + */ Ogre::Quaternion getRotation(void); - void setPosition(const btVector3& pos); + /** + * Sets the position of mBody from a visual position input. + * For most cases this should not be used. We should instead let pmove move the PhysicActor around for us + */ + void setPosition(const Ogre::Vector3 pos); + /** + * Sets the scale of the PhysicActor + */ void setScale(float scale); - std::string mName; + private: @@ -101,6 +116,7 @@ namespace Physic bool collisionMode; std::string mMesh; PhysicEngine* mEngine; + std::string mName; }; /** From f6384574dae1c31acb0dd7adc96e98838a589ac7 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 15 Sep 2012 18:17:42 -0400 Subject: [PATCH 622/688] Starting to introduce pmove --- libs/openengine/bullet/physic.cpp | 28 +++++++++++++++------------- libs/openengine/bullet/physic.hpp | 10 +++++++++- libs/openengine/bullet/trace.h | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 9c387e140..a9f483aa4 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "pmove.h" #include #include "CMotionState.h" #include "OgreRoot.h" @@ -33,6 +34,7 @@ namespace Physic Ogre::Quaternion inverse = mBoxRotation.Inverse(); mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map + pmove = new playerMove; } PhysicActor::~PhysicActor() @@ -41,6 +43,7 @@ namespace Physic mEngine->dynamicsWorld->removeRigidBody(mBody); delete mBody; } + delete pmove; } void PhysicActor::setGravity(float gravity) @@ -210,8 +213,8 @@ namespace Physic delete hf_it->second.mBody; } - RigidBodyContainer::iterator rb_it = RigidBodyMap.begin(); - for (; rb_it != RigidBodyMap.end(); ++rb_it) + RigidBodyContainer::iterator rb_it = ObjectMap.begin(); + for (; rb_it != ObjectMap.end(); ++rb_it) { if (rb_it->second != NULL) { @@ -380,22 +383,22 @@ namespace Physic } body->setActivationState(DISABLE_DEACTIVATION); if(addToMap){ - RigidBody* oldBody = RigidBodyMap[body->mName]; + RigidBody* oldBody = ObjectMap[body->mName]; if (oldBody != NULL) { dynamicsWorld->removeRigidBody(oldBody); delete oldBody; } - RigidBodyMap[body->mName] = body; + ObjectMap[body->mName] = body; } } } void PhysicEngine::removeRigidBody(std::string name) { - RigidBodyContainer::iterator it = RigidBodyMap.find(name); - if (it != RigidBodyMap.end() ) + RigidBodyContainer::iterator it = ObjectMap.find(name); + if (it != ObjectMap.end() ) { RigidBody* body = it->second; if(body != NULL) @@ -414,8 +417,8 @@ namespace Physic void PhysicEngine::deleteRigidBody(std::string name) { - RigidBodyContainer::iterator it = RigidBodyMap.find(name); - if (it != RigidBodyMap.end() ) + RigidBodyContainer::iterator it = ObjectMap.find(name); + if (it != ObjectMap.end() ) { RigidBody* body = it->second; //btScaledBvhTriangleMeshShape* scaled = dynamic_cast (body->getCollisionShape()); @@ -428,16 +431,16 @@ namespace Physic { delete scaled; }*/ - RigidBodyMap.erase(it); + ObjectMap.erase(it); } } RigidBody* PhysicEngine::getRigidBody(std::string name) { - RigidBodyContainer::iterator it = RigidBodyMap.find(name); - if (it != RigidBodyMap.end() ) + RigidBodyContainer::iterator it = ObjectMap.find(name); + if (it != ObjectMap.end() ) { - RigidBody* body = RigidBodyMap[name]; + RigidBody* body = ObjectMap[name]; return body; } else @@ -483,7 +486,6 @@ namespace Physic } PhysicActorMap.erase(it); } - //std::cout << "ok"; } PhysicActor* PhysicEngine::getCharacter(std::string name) diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 1598d3e87..f51675398 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -9,6 +9,8 @@ #include "BulletShapeLoader.h" #include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" + + class btRigidBody; class btBroadphaseInterface; class btDefaultCollisionConfiguration; @@ -16,6 +18,7 @@ class btSequentialImpulseConstraintSolver; class btCollisionDispatcher; class btDiscreteDynamicsWorld; class btHeightfieldTerrainShape; +struct playerMove; namespace BtOgre { @@ -27,6 +30,7 @@ namespace MWWorld class World; } + namespace OEngine { namespace Physic { @@ -109,6 +113,7 @@ namespace Physic private: + OEngine::Physic::RigidBody* mBody; Ogre::Vector3 mBoxScaledTranslation; btQuaternion mBoxRotationInverse; @@ -117,6 +122,8 @@ namespace Physic std::string mMesh; PhysicEngine* mEngine; std::string mName; + playerMove* pmove; + }; /** @@ -284,7 +291,7 @@ namespace Physic HeightFieldContainer mHeightFieldMap; typedef std::map RigidBodyContainer; - RigidBodyContainer RigidBodyMap; + RigidBodyContainer ObjectMap; typedef std::map PhysicActorContainer; PhysicActorContainer PhysicActorMap; @@ -293,6 +300,7 @@ namespace Physic BtOgre::DebugDrawer* mDebugDrawer; bool isDebugCreated; bool mDebugActive; + }; diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index bd554031a..1bfe0c717 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include "pmove.h" enum traceWorldType From 97326ffdbf62009c3e2e6ae6734676c402372430 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Sun, 16 Sep 2012 03:01:48 +0200 Subject: [PATCH 623/688] Fixed data-local directory handling in the launcher --- apps/launcher/datafilespage.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 64816fae5..69ed6defa 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -208,12 +208,10 @@ bool DataFilesPage::setupDataFiles() std::string local = variables["data-local"].as(); if (!local.empty()) { - mDataLocal.push_back(Files::PathContainer::value_type(local)); + mDataDirs.push_back(Files::PathContainer::value_type(local)); + mDataLocal.push_back(Files::PathContainer::value_type(local)); // For config writing } - if (mDataDirs.size()>1) - mDataDirs.resize (1); - mCfgMgr.processPaths(mDataDirs); while (mDataDirs.empty()) { From 4bcc34f4f963cfdd8319a74611c9d96cf07c2e73 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Sun, 16 Sep 2012 04:28:51 +0200 Subject: [PATCH 624/688] Re-enabled and fixed multiple data directory handling in the launcher --- apps/launcher/datafilespage.cpp | 154 ++++++++++++++++++-------------- apps/launcher/datafilespage.hpp | 1 + 2 files changed, 86 insertions(+), 69 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 69ed6defa..266a38101 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -189,71 +189,8 @@ void DataFilesPage::setupConfig() } -bool DataFilesPage::setupDataFiles() +void DataFilesPage::addDataFiles(Files::Collections &fileCollections, const QString &encoding) { - // We use the Configuration Manager to retrieve the configuration values - boost::program_options::variables_map variables; - boost::program_options::options_description desc; - - desc.add_options() - ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) - ("data-local", boost::program_options::value()->default_value("")) - ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) - ("encoding", boost::program_options::value()->default_value("win1252")); - - mCfgMgr.readConfiguration(variables, desc); - - // Put the paths in a boost::filesystem vector to use with Files::Collections - mDataDirs = Files::PathContainer(variables["data"].as()); - - std::string local = variables["data-local"].as(); - if (!local.empty()) { - mDataDirs.push_back(Files::PathContainer::value_type(local)); - mDataLocal.push_back(Files::PathContainer::value_type(local)); // For config writing - } - - mCfgMgr.processPaths(mDataDirs); - - while (mDataDirs.empty()) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error detecting Morrowind installation"); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Cancel); - msgBox.setText(tr("
Could not find the Data Files location

\ - The directory containing the data files was not found.

\ - Press \"Browse...\" to specify the location manually.
")); - - QAbstractButton *dirSelectButton = - msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); - - msgBox.exec(); - - if (msgBox.clickedButton() == dirSelectButton) { - - // Show a custom dir selection dialog which only accepts valid dirs - QString selectedDir = FileDialog::getExistingDirectory( - this, tr("Select Data Files Directory"), - QDir::currentPath(), - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - - // Add the user selected data directory - if (!selectedDir.isEmpty()) { - mDataDirs.push_back(Files::PathContainer::value_type(selectedDir.toStdString())); - mCfgMgr.processPaths(mDataDirs); - } else { - // Cancel from within the dir selection dialog - return false; - } - - } else { - // Cancel - return false; - } - } - - // Create a file collection for the data dirs - Files::Collections fileCollections(mDataDirs, !variables["fs-strict"].as()); - // First we add all the master files const Files::MultiDirCollection &esm = fileCollections.getCollection(".esm"); unsigned int i = 0; // Row number @@ -281,7 +218,7 @@ bool DataFilesPage::setupDataFiles() ESMReader fileReader; QStringList availableMasters; // Will contain all found masters - fileReader.setEncoding(variables["encoding"].as()); + fileReader.setEncoding(encoding.toStdString()); fileReader.open(iter->second.string()); // First we fill the availableMasters and the mMastersWidget @@ -352,6 +289,81 @@ bool DataFilesPage::setupDataFiles() } } + +} + + +bool DataFilesPage::setupDataFiles() +{ + // We use the Configuration Manager to retrieve the configuration values + boost::program_options::variables_map variables; + boost::program_options::options_description desc; + + desc.add_options() + ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) + ("data-local", boost::program_options::value()->default_value("")) + ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) + ("encoding", boost::program_options::value()->default_value("win1252")); + + mCfgMgr.readConfiguration(variables, desc); + + // Put the paths in a boost::filesystem vector to use with Files::Collections + mDataDirs = Files::PathContainer(variables["data"].as()); + + std::string local = variables["data-local"].as(); + if (!local.empty()) { + mDataLocal.push_back(Files::PathContainer::value_type(local)); + } + + mCfgMgr.processPaths(mDataDirs); + mCfgMgr.processPaths(mDataLocal); + + while (mDataDirs.empty()) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error detecting Morrowind installation"); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("
Could not find the Data Files location

\ + The directory containing the data files was not found.

\ + Press \"Browse...\" to specify the location manually.
")); + + QAbstractButton *dirSelectButton = + msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == dirSelectButton) { + + // Show a custom dir selection dialog which only accepts valid dirs + QString selectedDir = FileDialog::getExistingDirectory( + this, tr("Select Data Files Directory"), + QDir::currentPath(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + // Add the user selected data directory + if (!selectedDir.isEmpty()) { + mDataDirs.push_back(Files::PathContainer::value_type(selectedDir.toStdString())); + mCfgMgr.processPaths(mDataDirs); + } else { + // Cancel from within the dir selection dialog + return false; + } + + } else { + // Cancel + return false; + } + } + + // Add the plugins in the data directories + Files::Collections dataCollections(mDataDirs, !variables["fs-strict"].as()); + Files::Collections dataLocalCollections(mDataLocal, !variables["fs-strict"].as()); + + addDataFiles(dataCollections, QString::fromStdString(variables["encoding"].as())); + addDataFiles(dataLocalCollections, QString::fromStdString(variables["encoding"].as())); + + mDataFilesModel->sort(0); + readConfig(); return true; } @@ -1137,11 +1149,13 @@ void DataFilesPage::writeConfig(QString profile) path = QString::fromStdString(it->string()); path.remove(QChar('\"')); + QDir dir(path); + // Make sure the string is quoted when it contains spaces if (path.contains(" ")) { - gameConfig << "data=\"" << path << "\"" << endl; + gameConfig << "data=\"" << dir.absolutePath() << "\"" << endl; } else { - gameConfig << "data=" << path << endl; + gameConfig << "data=" << dir.absolutePath() << endl; } } @@ -1150,10 +1164,12 @@ void DataFilesPage::writeConfig(QString profile) path = QString::fromStdString(mDataLocal.front().string()); path.remove(QChar('\"')); + QDir dir(path); + if (path.contains(" ")) { - gameConfig << "data-local=\"" << path << "\"" << endl; + gameConfig << "data-local=\"" << dir.absolutePath() << "\"" << endl; } else { - gameConfig << "data-local=" << path << endl; + gameConfig << "data-local=" << dir.absolutePath() << endl; } } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 5078f6428..83b318677 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -89,6 +89,7 @@ private: const QStringList checkedPlugins(); const QStringList selectedMasters(); + void addDataFiles(Files::Collections &fileCollections, const QString &encoding); void addPlugins(const QModelIndex &index); void removePlugins(const QModelIndex &index); void uncheckPlugins(); From 76f2a8288498910dd1f271dd43f4317b2785adeb Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 15 Sep 2012 22:48:24 -0400 Subject: [PATCH 625/688] More pmove stuff; Turned off broadphase, we could not fall in exteriors --- apps/openmw/mwworld/physicssystem.cpp | 8 ++++---- libs/openengine/bullet/physic.cpp | 17 ++++++++++++++--- libs/openengine/bullet/physic.hpp | 4 +++- libs/openengine/bullet/pmove.cpp | 2 +- libs/openengine/bullet/pmove.h | 4 ++-- libs/openengine/bullet/trace.cpp | 3 ++- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index d0291b01a..27bff68c6 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -96,6 +96,10 @@ namespace MWWorld if(hasWater){ playerphysics->waterHeight = waterHeight; } + for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) + { + it->second->setCurrentWater(hasWater, waterHeight); + } } @@ -356,8 +360,6 @@ namespace MWWorld if(cmode) { act->enableCollisions(false); - act->setGravity(0.); - act->setVerticalVelocity(0); mFreeFly = true; return false; } @@ -365,8 +367,6 @@ namespace MWWorld { mFreeFly = false; act->enableCollisions(true); - act->setGravity(4.); - act->setVerticalVelocity(0); return true; } } diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index a9f483aa4..181aa62b5 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -46,19 +46,30 @@ namespace Physic delete pmove; } + void PhysicActor::setCurrentWater(bool hasWater, int waterHeight){ + pmove->hasWater = hasWater; + if(hasWater){ + pmove->waterHeight = waterHeight; + } + } + void PhysicActor::setGravity(float gravity) { - + pmove->ps.gravity = gravity; } void PhysicActor::enableCollisions(bool collision) { collisionMode = collision; + if(collisionMode) + pmove->ps.move_type=PM_NORMAL; + else + pmove->ps.move_type=PM_NOCLIP; } - void PhysicActor::setVerticalVelocity(float z) + void PhysicActor::setJumpVelocity(float velocity) { - + pmove->ps.jump_velocity = velocity; } bool PhysicActor::getCollisionMode() diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f51675398..481fa2ed8 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -65,6 +65,8 @@ namespace Physic ~PhysicActor(); + void setCurrentWater(bool hasWater, int waterHeight); + /** * This function set the walkDirection. This is not relative to the actor orientation. * I think it's also needed to take time into account. A typical call should look like this: @@ -81,7 +83,7 @@ namespace Physic void setGravity(float gravity); - void setVerticalVelocity(float z); + void setJumpVelocity(float velocity); void enableCollisions(bool collision); diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 3d2462ab5..f3e1a470a 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -704,7 +704,7 @@ static bool PM_CheckJump(void) //pm->ps->pm_flags |= PMF_JUMP_HELD; pm->ps.groundEntityNum = ENTITYNUM_NONE; - pm->ps.velocity.z = JUMP_VELOCITY; + pm->ps.velocity.z = pm->ps.jump_velocity; pm->ps.bSnap = false; //PM_AddEvent( EV_JUMP ); diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index aef028ba4..63d03f86a 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -42,7 +42,6 @@ static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); #define ENTITYNUM_NONE (MAX_GENTITIES - 1) #define ENTITYNUM_WORLD (MAX_GENTITIES - 2) #define MIN_WALK_NORMAL .7f // can't walk on very steep slopes -#define JUMP_VELOCITY (270) #define PS_PMOVEFRAMECOUNTBITS 6 #define MINS_Z -24 #define DEFAULT_VIEWHEIGHT 26 @@ -90,7 +89,7 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(800.0f), speed(480.0f), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + playerStruct() : gravity(800.0f), speed(480.0f), jump_velocity(270), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) { origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); @@ -122,6 +121,7 @@ struct playerMove int counter; float gravity; // default = 800 float speed; // default = 320 + float jump_velocity; //default = 270 int commandTime; // the time at which this command was issued (in milliseconds) diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 474d87ee9..03ed4ffb1 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -181,7 +181,8 @@ const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& if (!TestPointAgainstAabb2(aabbMin, aabbMax, *(const btVector3* const)&(start) ) ) { //We're solid - out->startSolid = true; + //THIS NEEDS TO BE TURNED OFF IF WE WANT FALLING IN EXTERIORS TO WORK CORRECTLY!!!!!!! + //out->startSolid = true; } } } From 192d6340985bd8b97e36dd241572fa972cfdce51 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 16 Sep 2012 13:19:38 -0400 Subject: [PATCH 626/688] Movement keys; View angles --- apps/openmw/mwworld/physicssystem.cpp | 4 ++-- libs/openengine/bullet/physic.cpp | 22 ++++++++++++++++++++-- libs/openengine/bullet/physic.hpp | 17 +++++++++++++---- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 27bff68c6..8de415980 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -176,11 +176,11 @@ namespace MWWorld //set the DebugRenderingMode. To disable it,set it to 0 //eng->setDebugRenderingMode(1); - //set the walkdirection to 0 (no movement) for every actor) + //set the movement keys to 0 (no movement) for every actor) for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) { OEngine::Physic::PhysicActor* act = it->second; - act->setWalkDirection(btVector3(0,0,0)); + act->setMovement(0,0,0); } playerMove::playercmd& pm_ref = playerphysics->cmd; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 181aa62b5..ca7709c2c 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -35,6 +35,7 @@ namespace Physic mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map pmove = new playerMove; + pmove->mEngine = mEngine; } PhysicActor::~PhysicActor() @@ -77,9 +78,18 @@ namespace Physic return collisionMode; } - void PhysicActor::setWalkDirection(const btVector3& mvt) + void PhysicActor::setMovement(signed char rightmove, signed char forwardmove, signed char upmove) { - + playerMove::playercmd& pm_ref = pmove->cmd; + pm_ref.rightmove = rightmove; + pm_ref.forwardmove = forwardmove; + pm_ref.upmove = upmove; + } + + void PhysicActor::setPmoveViewAngles(float pitch, float yaw, float roll){ + pmove->ps.viewangles.x = pitch; + pmove->ps.viewangles.y = yaw; + pmove->ps.viewangles.z = roll; } @@ -110,6 +120,8 @@ namespace Physic void PhysicActor::setPosition(const Ogre::Vector3 pos) { mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); + btVector3 vec = mBody->getWorldTransform().getOrigin(); + pmove->ps.origin = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()); } void PhysicActor::setScale(float scale){ @@ -127,6 +139,12 @@ namespace Physic mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map } + void PhysicActor::runPmove(){ + Pmove(pmove); + Ogre::Vector3 newpos = pmove->ps.origin; + mBody->getWorldTransform().setOrigin(btVector3(newpos.x, newpos.y, newpos.z)); + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 481fa2ed8..56b7f1fb7 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -68,11 +68,9 @@ namespace Physic void setCurrentWater(bool hasWater, int waterHeight); /** - * This function set the walkDirection. This is not relative to the actor orientation. - * I think it's also needed to take time into account. A typical call should look like this: - * setWalkDirection( mvt * orientation * dt) + * This function sets the movement keys for pmove */ - void setWalkDirection(const btVector3& mvt); + void setMovement(signed char rightmove, signed char forwardmove, signed char upmove); /** * This adjusts the rotation of a PhysicActor @@ -106,11 +104,22 @@ namespace Physic */ void setPosition(const Ogre::Vector3 pos); + /** + * Sets the view angles for pmove directly. + * Remember, add 90 for yaw. Set roll to 0. + */ + void setPmoveViewAngles(float pitch, float yaw, float roll); + /** * Sets the scale of the PhysicActor */ void setScale(float scale); + /** + * Runs pmove for this PhysicActor + */ + void runPmove(); + From 42d25c0af001c80ea7c17364e3aee07959725d62 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Sep 2012 20:56:49 +0200 Subject: [PATCH 627/688] fix resources, I renamed those in the last commit, but only in one place --- files/mygui/core.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/mygui/core.xml b/files/mygui/core.xml index e1fb1b5e2..ea1627875 100644 --- a/files/mygui/core.xml +++ b/files/mygui/core.xml @@ -4,8 +4,8 @@ - - + + From 0fa1dea6c1cdd9c5a02b185db253a39896513dd0 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 16 Sep 2012 17:29:16 -0400 Subject: [PATCH 628/688] Box size set; Set speed --- libs/openengine/bullet/physic.cpp | 13 +++++++++++++ libs/openengine/bullet/physic.hpp | 2 ++ libs/openengine/bullet/pmove.cpp | 20 ++++++++++---------- libs/openengine/bullet/pmove.h | 5 +++-- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index ca7709c2c..4ced2db90 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -36,6 +36,10 @@ namespace Physic mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map pmove = new playerMove; pmove->mEngine = mEngine; + btBoxShape* box = static_cast (mBody->getCollisionShape()); + btVector3 size = box->getHalfExtentsWithMargin(); + Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); + pmove->ps.halfExtents = halfExtents; } PhysicActor::~PhysicActor() @@ -58,6 +62,11 @@ namespace Physic { pmove->ps.gravity = gravity; } + + void PhysicActor::setSpeed(float speed) + { + pmove->ps.speed = speed; + } void PhysicActor::enableCollisions(bool collision) { @@ -137,6 +146,10 @@ namespace Physic //Create the newly scaled rigid body mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map + btBoxShape* box = static_cast (mBody->getCollisionShape()); + btVector3 size = box->getHalfExtentsWithMargin(); + Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); + pmove->ps.halfExtents = halfExtents; } void PhysicActor::runPmove(){ diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 56b7f1fb7..a69d80e53 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -81,6 +81,8 @@ namespace Physic void setGravity(float gravity); + void setSpeed(float speed); + void setJumpVelocity(float velocity); void enableCollisions(bool collision); diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index f3e1a470a..645abf205 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -231,7 +231,7 @@ bool PM_SlideMove( bool gravity ) // see if we can make it there //pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemaskg); //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&(end), *(const D3DXVECTOR3* const)&(pm->ps.velocity), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, end, halfExtents, Ogre::Math::DegreesToRadians (pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(&trace, pm->ps.origin, end, pm->ps.halfExtents, Ogre::Math::DegreesToRadians (pm->ps.viewangles.y), pm->isInterior, pm->mEngine); if (trace.allsolid) { @@ -449,7 +449,7 @@ int PM_StepSlideMove( bool gravity ) //pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); //tracefunc(&trace, start_o, down, , 0, pml.scene); //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, down, start_o, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(&trace, down, start_o, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); // up = vec3(0, 0, 1) //VectorSet(up, 0, 0, 1); @@ -479,7 +479,7 @@ int PM_StepSlideMove( bool gravity ) // test the player position if they were a stepheight higher //pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&up, D3DXVECTOR3(0.0f, STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, start_o, up, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(&trace, start_o, up, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); if ( trace.allsolid ) { //if ( pm->debugLevel ) @@ -510,7 +510,7 @@ int PM_StepSlideMove( bool gravity ) //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, down, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(&trace, pm->ps.origin, down, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); if ( !trace.allsolid ) //VectorCopy (trace.endpos, pm->ps->origin); pm->ps.origin = trace.endpos; @@ -902,7 +902,7 @@ static void PM_WalkMove( playerMove* const pmove ) if (pmove->hasWater ) { const float waterHeight = pmove->waterHeight; - const float waterSoundStepHeight = waterHeight + halfExtents.y; + const float waterSoundStepHeight = waterHeight + pm->ps.halfExtents.y; if (pmove->ps.origin.y < waterSoundStepHeight) step_underwater = true; } @@ -1182,7 +1182,7 @@ void PM_GroundTraceMissed() //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -64.0f, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(&trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); //It hit the ground below if ( trace.fraction < 1.0 && pm->ps.origin.z > trace.endpos.z) { @@ -1228,7 +1228,7 @@ static bool PM_CorrectAllSolid(traceResults* const trace) //pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); //tracefunc(trace, *(const D3DXVECTOR3* const)&point, *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, 0.0f, 0.0f), 0, pml.traceObj); - newtrace(trace, point, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(trace, point, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); if ( !trace->allsolid ) { @@ -1240,7 +1240,7 @@ static bool PM_CorrectAllSolid(traceResults* const trace) //pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); //tracefunc(trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); - newtrace(trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); pml.groundTrace = *trace; return true; } @@ -1341,7 +1341,7 @@ static void PM_CrashLand( void ) { const float waterHeight = pm->waterHeight; - const float waterHeightSplash = waterHeight + halfExtents.y; + const float waterHeightSplash = waterHeight + pm->ps.halfExtents.y; if (pm->ps.origin.z < waterHeightSplash) { splashSound = true; @@ -1416,7 +1416,7 @@ static void PM_GroundTrace( void ) //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, point, halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); + newtrace(&trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); pml.groundTrace = trace; // do something corrective if the trace starts in a solid... diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h index 63d03f86a..fa303184e 100644 --- a/libs/openengine/bullet/pmove.h +++ b/libs/openengine/bullet/pmove.h @@ -23,7 +23,7 @@ Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. extern SceneInstance* global_lastscene; #endif*/ -static const Ogre::Vector3 halfExtents(14.64f * 2, 14.24f * 2, 33.25f * 2); +static const Ogre::Vector3 halfExtentsDefault(14.64f * 2, 14.24f * 2, 33.25f * 2); #define MAX_CLIP_PLANES 5 #define OVERCLIP 1.001f @@ -89,7 +89,7 @@ struct playerMove { struct playerStruct { - playerStruct() : gravity(800.0f), speed(480.0f), jump_velocity(270), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1) + playerStruct() : gravity(800.0f), speed(480.0f), jump_velocity(270), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1), halfExtents(halfExtentsDefault) { origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); @@ -116,6 +116,7 @@ struct playerMove Ogre::Vector3 velocity; Ogre::Vector3 origin; + Ogre::Vector3 halfExtents; bool bSnap; bool snappingImplemented; int counter; From 05687c120fc5692bf04cbff89ab5230bd7ab78d7 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 16 Sep 2012 17:48:18 -0400 Subject: [PATCH 629/688] protection for null cast --- libs/openengine/bullet/physic.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 4ced2db90..b52c09eda 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -37,9 +37,11 @@ namespace Physic pmove = new playerMove; pmove->mEngine = mEngine; btBoxShape* box = static_cast (mBody->getCollisionShape()); - btVector3 size = box->getHalfExtentsWithMargin(); - Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); - pmove->ps.halfExtents = halfExtents; + if(box != NULL){ + btVector3 size = box->getHalfExtentsWithMargin(); + Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); + pmove->ps.halfExtents = halfExtents; + } } PhysicActor::~PhysicActor() @@ -147,9 +149,11 @@ namespace Physic mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map btBoxShape* box = static_cast (mBody->getCollisionShape()); - btVector3 size = box->getHalfExtentsWithMargin(); - Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); - pmove->ps.halfExtents = halfExtents; + if(box != NULL){ + btVector3 size = box->getHalfExtentsWithMargin(); + Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); + pmove->ps.halfExtents = halfExtents; + } } void PhysicActor::runPmove(){ From ff54508633b75518387958ce624c0dde854307a1 Mon Sep 17 00:00:00 2001 From: Brother Brick Date: Mon, 17 Sep 2012 11:57:29 +0200 Subject: [PATCH 630/688] during cleanup we now delete all plugins created --- libs/openengine/ogre/renderer.cpp | 29 ++++++++++++++++++++++++++++- libs/openengine/ogre/renderer.hpp | 4 +++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index daf82b9da..9251e5683 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -44,8 +44,10 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; - OGRE_DELETE mRoot; + delete mRoot; mRoot = NULL; + + unloadPlugins(); } void OgreRenderer::start() @@ -103,6 +105,31 @@ bool OgreRenderer::loadPlugins() return true; } +bool OgreRenderer::unloadPlugins() +{ + #ifdef ENABLE_PLUGIN_GL + delete mGLPlugin; + mGLPlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + delete mD3D9Plugin; + mD3D9Plugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_CgProgramManager + delete mCgPlugin; + mCgPlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + delete mOctreePlugin; + mOctreePlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + delete mParticleFXPlugin; + mParticleFXPlugin = NULL; + #endif + return true; +} + void OgreRenderer::update(float dt) { mFader->update(dt); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 00e094b4d..b71da9020 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -151,7 +151,9 @@ namespace OEngine /// Start the main rendering loop void start(); - bool loadPlugins() ; + bool loadPlugins(); + + bool unloadPlugins(); void update(float dt); From 032ff7c879e2b80423f21576f508edc8e8753776 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 17 Sep 2012 13:36:48 +0200 Subject: [PATCH 631/688] Clean-up. There is still a little bug. --- apps/openmw/mwbase/world.hpp | 3 ++ .../mwscript/transformationextensions.cpp | 36 ++++++++++--------- apps/openmw/mwworld/worldimp.cpp | 5 +++ apps/openmw/mwworld/worldimp.hpp | 3 ++ 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b0ce261f1..ef4e6a5b8 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -187,6 +187,9 @@ namespace MWBase virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; + virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; + ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. + virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const = 0; ///< Convert cell numbers to position. diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index c82108f8b..e40390a63 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -19,6 +19,7 @@ #include "../mwworld/player.hpp" #include "components\esm\loadcell.hpp" +#include "../mwworld/manualref.hpp" #include "OgreMath.h" namespace MWScript @@ -369,7 +370,7 @@ namespace MWScript pos.rot[0] = pos.rot[1] = 0; pos.rot[2] = zRot; MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); - MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,pos); + MWBase::Environment::get().getWorld()->safePlaceObject(ptr,*store,pos); } else { @@ -412,7 +413,7 @@ namespace MWScript pos.rot[0] = pos.rot[1] = 0; pos.rot[2] = zRot; MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); - MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,pos); + MWBase::Environment::get().getWorld()->safePlaceObject(ptr,*store,pos); } else { @@ -450,13 +451,14 @@ namespace MWScript ipos.pos[0] = pos.x; ipos.pos[1] = pos.y; ipos.pos[2] = pos.z; + ipos.rot[0] = 0; + ipos.rot[1] = 0; + ipos.rot[2] = 0; + MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); - int icount = ptr.getRefData().getCount(); - ptr.getRefData().setCount(count); - MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,ipos); - ptr.getRefData().setCount(icount); - //store->ge + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + ref.getPtr().getRefData().setCount(count); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); } }; @@ -491,13 +493,15 @@ namespace MWScript ipos.pos[0] = pos.x; ipos.pos[1] = pos.y; ipos.pos[2] = pos.z; + ipos.rot[0] = 0; + ipos.rot[1] = 0; + ipos.rot[2] = 0; + MWWorld::CellStore* store = me.getCell(); - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); - int icount = ptr.getRefData().getCount(); - ptr.getRefData().setCount(count); - MWBase::Environment::get().getWorld()->copyObjectToCell(ptr,*store,ipos); - ptr.getRefData().setCount(icount); - //store->ge + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + ref.getPtr().getRefData().setCount(count); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); + } }; @@ -566,8 +570,8 @@ namespace MWScript interpreter.installSegment5(opcodePlaceItemCell,new OpPlaceItemCell); interpreter.installSegment5(opcodePlaceItem,new OpPlaceItem); interpreter.installSegment5(opcodePlaceAtPc,new OpPlaceAtPc); - interpreter.installSegment5(opcodePlaceAtMe,new OpPlaceAtPc); - interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtPc); + interpreter.installSegment5(opcodePlaceAtMe,new OpPlaceAtMe); + interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d9c4fe321..fb475f361 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -656,6 +656,11 @@ namespace MWWorld } } + void World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) + { + copyObjectToCell(ptr,Cell,pos); + } + void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const { const int cellSize = 8192; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4031a180a..acf63b54a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -214,6 +214,9 @@ namespace MWWorld /// \param adjust indicates rotation should be set or adjusted virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); + virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos); + ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. + virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; ///< Convert cell numbers to position. From b96485284a64dcf005d6b43aeafff2d919f4783f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 17 Sep 2012 13:41:15 +0200 Subject: [PATCH 632/688] removed some unneeded return values --- libs/openengine/ogre/renderer.cpp | 6 ++---- libs/openengine/ogre/renderer.hpp | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 8cff0f9e0..65911df34 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -80,7 +80,7 @@ void OgreRenderer::start() #endif } -bool OgreRenderer::loadPlugins() +void OgreRenderer::loadPlugins() { #ifdef ENABLE_PLUGIN_GL mGLPlugin = new Ogre::GLPlugin(); @@ -102,10 +102,9 @@ bool OgreRenderer::loadPlugins() mParticleFXPlugin = new Ogre::ParticleFXPlugin(); mRoot->installPlugin(mParticleFXPlugin); #endif - return true; } -bool OgreRenderer::unloadPlugins() +void OgreRenderer::unloadPlugins() { #ifdef ENABLE_PLUGIN_GL delete mGLPlugin; @@ -127,7 +126,6 @@ bool OgreRenderer::unloadPlugins() delete mParticleFXPlugin; mParticleFXPlugin = NULL; #endif - return true; } void OgreRenderer::update(float dt) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index b71da9020..95bcac3b8 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -151,9 +151,9 @@ namespace OEngine /// Start the main rendering loop void start(); - bool loadPlugins(); + void loadPlugins(); - bool unloadPlugins(); + void unloadPlugins(); void update(float dt); From 3c1a9061de1641fa03e4a69c25b050ee89ae7f2a Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 17 Sep 2012 14:12:27 +0200 Subject: [PATCH 633/688] Fixed a bug of Ptr having a wrong orientation. But that's strange, seems CellRef isn't initialized properly --- apps/openmw/mwscript/transformationextensions.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index e40390a63..5b43453eb 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -369,8 +369,9 @@ namespace MWScript pos.pos[2] = z; pos.rot[0] = pos.rot[1] = 0; pos.rot[2] = zRot; - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); - MWBase::Environment::get().getWorld()->safePlaceObject(ptr,*store,pos); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + ref.getPtr().getCellRef().pos = pos; + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos); } else { @@ -412,8 +413,9 @@ namespace MWScript pos.pos[2] = z; pos.rot[0] = pos.rot[1] = 0; pos.rot[2] = zRot; - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr(itemID,false); - MWBase::Environment::get().getWorld()->safePlaceObject(ptr,*store,pos); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + ref.getPtr().getCellRef().pos = pos; + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos); } else { @@ -457,6 +459,7 @@ namespace MWScript MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + ref.getPtr().getCellRef().pos = ipos; ref.getPtr().getRefData().setCount(count); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); } @@ -499,6 +502,7 @@ namespace MWScript MWWorld::CellStore* store = me.getCell(); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + ref.getPtr().getCellRef().pos = ipos; ref.getPtr().getRefData().setCount(count); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); From f01aae138f1fc88f3358b4d31c2b024f52b60413 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 17 Sep 2012 17:23:26 -0400 Subject: [PATCH 634/688] Removing void --- libs/openengine/bullet/physic.cpp | 4 ++-- libs/openengine/bullet/physic.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index b52c09eda..74352b358 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -112,7 +112,7 @@ namespace Physic } } - Ogre::Vector3 PhysicActor::getPosition(void) + Ogre::Vector3 PhysicActor::getPosition() { btVector3 vec = mBody->getWorldTransform().getOrigin(); Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(), @@ -122,7 +122,7 @@ namespace Physic return visualPosition; } - Ogre::Quaternion PhysicActor::getRotation(void) + Ogre::Quaternion PhysicActor::getRotation() { btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse; return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index a69d80e53..e4e71706f 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -93,12 +93,12 @@ namespace Physic * This returns the visual position of the PhysicActor (used to position a scenenode). * Note - this is different from the position of the contained mBody. */ - Ogre::Vector3 getPosition(void); + Ogre::Vector3 getPosition(); /** * Returns the visual orientation of the PhysicActor */ - Ogre::Quaternion getRotation(void); + Ogre::Quaternion getRotation(); /** * Sets the position of mBody from a visual position input. From 408c5b8bd46214e685792aab4750d19688418776 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 18 Sep 2012 10:49:51 +0200 Subject: [PATCH 635/688] some cleanup --- .../mwscript/transformationextensions.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 4ce1c7cdf..cd979e2ec 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -1,6 +1,9 @@ #include +#include + #include +#include #include @@ -11,18 +14,13 @@ #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" #include "interpretercontext.hpp" #include "ref.hpp" #include "OgreSceneNode.h" - -#include "../mwworld/player.hpp" -#include -#include "../mwworld/manualref.hpp" - -#include "OgreMath.h" - namespace MWScript { namespace Transformation @@ -172,6 +170,8 @@ namespace MWScript { runtime.push(ptr.getRefData().getPosition().pos[2]); } + else + throw std::runtime_error ("invalid rotation axis: " + axis); } }; @@ -192,7 +192,6 @@ namespace MWScript float ax = ptr.getRefData().getPosition().pos[0]; float ay = ptr.getRefData().getPosition().pos[1]; float az = ptr.getRefData().getPosition().pos[2]; - if(axis == "x") { @@ -206,6 +205,8 @@ namespace MWScript { MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); } + else + throw std::runtime_error ("invalid axis: " + axis); } }; @@ -233,6 +234,8 @@ namespace MWScript { runtime.push(ptr.getCellRef().pos.pos[2]); } + else + throw std::runtime_error ("invalid axis: " + axis); } }; @@ -331,8 +334,6 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - //MWWorld::Ptr ptr = R()(runtime); - std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); @@ -402,10 +403,9 @@ namespace MWScript Interpreter::Type_Float zRot = runtime[0].mFloat; runtime.pop(); - MWWorld::CellStore* store = 0; int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); - store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); + MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getExterior(cx,cy); if(store) { ESM::Position pos; From ea8eab4f34f2d0b16050cd4ae33dd1bdd7bce82c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 18 Sep 2012 11:06:19 +0200 Subject: [PATCH 636/688] more cleanup --- apps/openmw/mwbase/world.hpp | 7 ++----- apps/openmw/mwscript/transformationextensions.cpp | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9d432c5b6..b841e5427 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -142,9 +142,6 @@ namespace MWBase virtual MWWorld::Ptr getPtrViaHandle (const std::string& handle) = 0; ///< Return a pointer to a liveCellRef with the given Ogre handle. - virtual void - copyObjectToCell(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell, const ESM::Position &pos) = 0; - /// \todo enable reference in the OGRE scene virtual void enable (const MWWorld::Ptr& ptr) = 0; @@ -208,7 +205,7 @@ namespace MWBase virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; - ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. + ///< place an object in a "safe" location (ie not in the void, etc). virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const = 0; @@ -224,7 +221,7 @@ namespace MWBase virtual bool toggleCollisionMode() = 0; ///< Toggle collision mode for player. If disabled player object should ignore /// collisions and gravity. - ///< \return Resulting mode + /// \return Resulting mode virtual bool toggleRenderMode (RenderMode mode) = 0; ///< Toggle a render mode. diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index cd979e2ec..570b8e081 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -19,7 +20,6 @@ #include "interpretercontext.hpp" #include "ref.hpp" -#include "OgreSceneNode.h" namespace MWScript { @@ -389,8 +389,6 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - //MWWorld::Ptr ptr = R()(runtime); - std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); From fcda3b6ca8a87c26d25aea387f6a68b51851c633 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 18 Sep 2012 11:09:54 +0200 Subject: [PATCH 637/688] fixed some script argument types --- apps/openmw/mwscript/transformationextensions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 570b8e081..5db557174 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -537,13 +537,13 @@ namespace MWScript { extensions.registerInstruction("setscale","f",opcodeSetScale,opcodeSetScaleExplicit); extensions.registerFunction("getscale",'f',"",opcodeGetScale,opcodeGetScaleExplicit); - extensions.registerInstruction("setangle","Sf",opcodeSetAngle,opcodeSetAngleExplicit); - extensions.registerFunction("getangle",'f',"S",opcodeGetAngle,opcodeGetAngleExplicit); + extensions.registerInstruction("setangle","cf",opcodeSetAngle,opcodeSetAngleExplicit); + extensions.registerFunction("getangle",'f',"c",opcodeGetAngle,opcodeGetAngleExplicit); extensions.registerInstruction("setpos","cf",opcodeSetPos,opcodeSetPosExplicit); extensions.registerFunction("getpos",'f',"c",opcodeGetPos,opcodeGetPosExplicit); extensions.registerFunction("getstartingpos",'f',"c",opcodeGetStartingPos,opcodeGetStartingPosExplicit); extensions.registerInstruction("position","ffff",opcodePosition,opcodePositionExplicit); - extensions.registerInstruction("positioncell","ffffS",opcodePositionCell,opcodePositionCellExplicit); + extensions.registerInstruction("positioncell","ffffc",opcodePositionCell,opcodePositionCellExplicit); extensions.registerInstruction("placeitemcell","ccffff",opcodePlaceItemCell); extensions.registerInstruction("placeitem","cffff",opcodePlaceItem); extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); From 747a4e1123c244671356655930ce510b86c82db5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 18 Sep 2012 11:19:21 +0200 Subject: [PATCH 638/688] increased version number and updated readme --- CMakeLists.txt | 2 +- readme.txt | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a24998d1..aee073d3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 17) +set (OPENMW_VERSION_MINOR 18) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") diff --git a/readme.txt b/readme.txt index a254e2672..20aff18ae 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.17.0 +Version: 0.18.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org @@ -97,6 +97,39 @@ Allowed options: CHANGELOG +0.18.0 + +Bug #310: Button of the "preferences menu" are too small +Bug #361: Hand-to-hand skill is always 100 +Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to +Bug #372: playSound3D uses original coordinates instead of current coordinates. +Bug #373: Static OGRE build faulty +Bug #375: Alt-tab toggle view +Bug #376: Screenshots are disable +Bug #378: Exception when drinking self-made potions +Bug #380: Cloth visibility problem +Bug #384: Weird character on doors tooltip. +Bug #398: Some objects do not collide in MW, but do so in OpenMW +Feature #22: Implement level-up +Feature #36: Hide Marker +Feature #88: Hotkey Window +Feature #91: Level-Up Dialogue +Feature #118: Keyboard and Mouse-Button bindings +Feature #119: Spell Buying Window +Feature #133: Handle resources across multiple data directories +Feature #134: Generate a suitable default-value for --data-local +Feature #292: Object Movement/Creation Script Instructions +Feature #340: AIPackage data structures +Feature #356: Ingredients use +Feature #358: Input system rewrite +Feature #370: Target handling in actions +Feature #379: Door markers on the local map +Feature #389: AI framework +Feature #395: Using keys to open doors / containers +Feature #396: Loading screens +Feature #397: Inventory avatar image and race selection head preview +Task #339: Move sounds into Action + 0.17.0 Bug #225: Valgrind reports about 40MB of leaked memory From 0ba996f290eefb21ae11e93f81ef0f6421426dea Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Sep 2012 18:29:03 +0200 Subject: [PATCH 639/688] dialog layout --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/waitdialog.cpp | 12 ++ apps/openmw/mwgui/waitdialog.hpp | 19 +++ apps/openmw/mwgui/widgets.cpp | 166 ++++++++++++++++++++++--- apps/openmw/mwgui/widgets.hpp | 19 ++- apps/openmw/mwgui/windowmanagerimp.cpp | 9 +- apps/openmw/mwgui/windowmanagerimp.hpp | 2 + files/mygui/CMakeLists.txt | 1 + files/mygui/openmw_wait_dialog.layout | 45 +++++++ 9 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 apps/openmw/mwgui/waitdialog.cpp create mode 100644 apps/openmw/mwgui/waitdialog.hpp create mode 100644 files/mygui/openmw_wait_dialog.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2542ef350..bcd5fc853 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -29,7 +29,7 @@ add_openmw_dir (mwgui map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu - itemselection spellbuyingwindow loadingscreen levelupdialog + itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp new file mode 100644 index 000000000..5b491a4f2 --- /dev/null +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -0,0 +1,12 @@ +#include "waitdialog.hpp" + +namespace MWGui +{ + + WaitDialog::WaitDialog(MWBase::WindowManager &parWindowManager) + : WindowBase("openmw_wait_dialog.layout", parWindowManager) + { + center(); + } + +} diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp new file mode 100644 index 000000000..3c68c1249 --- /dev/null +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -0,0 +1,19 @@ +#ifndef MWGUI_WAIT_DIALOG_H +#define MWGUI_WAIT_DIALOG_H + +#include "window_base.hpp" + +namespace MWGui +{ + + class WaitDialog : public WindowBase + { + public: + WaitDialog(MWBase::WindowManager& parWindowManager); + + + }; + +} + +#endif diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 97a5b2eb5..e8cee2141 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -869,29 +869,33 @@ void AutoSizedButton::setPropertyOverride(const std::string& _key, const std::st Box::Box() : mSpacing(4) + , mPadding(0) + , mAutoResize(false) { } -void Box::setPropertyOverride(const std::string& _key, const std::string& _value) -{ - if (_key == "Spacing") - { - mSpacing = MyGUI::utility::parseValue(_value); - } -} - void Box::notifyChildrenSizeChanged () { align(); } +void Box::_setPropertyImpl(const std::string& _key, const std::string& _value) +{ + if (_key == "Spacing") + mSpacing = MyGUI::utility::parseValue(_value); + else if (_key == "Padding") + mPadding = MyGUI::utility::parseValue(_value); + else if (_key == "AutoResize") + mAutoResize = MyGUI::utility::parseValue(_value); +} void HBox::align () { unsigned int count = getChildCount (); size_t h_stretched_count = 0; int total_width = 0; + int total_height = 0; std::vector< std::pair > sizes; for (unsigned int i = 0; i < count; ++i) @@ -904,28 +908,43 @@ void HBox::align () { sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); total_width += aw->getRequestedSize ().width; + total_height = std::max(total_height, aw->getRequestedSize ().height); } else { - if (!hstretch) h_stretched_count ++; - sizes.push_back (std::make_pair(MyGUI::IntSize(0,0), true)); + sizes.push_back (std::make_pair(w->getSize(), hstretch)); + total_width += w->getSize().width; } if (i != count-1) total_width += mSpacing; } + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } + + int curX = 0; for (unsigned int i = 0; i < count; ++i) { + if (i == 0) + curX += mPadding; + MyGUI::Widget* w = getChildAt(i); + + bool vstretch = w->getUserString ("VStretch") == "true"; + int height = vstretch ? total_height : sizes[i].first.height; + MyGUI::IntCoord widgetCoord; widgetCoord.left = curX; - widgetCoord.top = (getSize().height - sizes[i].first.height) / 2; - int width = sizes[i].second ? sizes[i].first.width + (getSize().width - total_width)/h_stretched_count + widgetCoord.top = mPadding + (getSize().height-mPadding*2 - height) / 2; + int width = sizes[i].second ? sizes[i].first.width + (getSize().width-mPadding*2 - total_width)/h_stretched_count : sizes[i].first.width; widgetCoord.width = width; - widgetCoord.height = sizes[i].first.height; + widgetCoord.height = height; w->setCoord(widgetCoord); curX += width; @@ -934,12 +953,33 @@ void HBox::align () } } +void HBox::setPropertyOverride(const std::string& _key, const std::string& _value) +{ + Box::_setPropertyImpl (_key, _value); +} + void HBox::setSize (const MyGUI::IntSize& _value) { MyGUI::Widget::setSize (_value); align(); } +void HBox::setCoord (const MyGUI::IntCoord& _value) +{ + MyGUI::Widget::setCoord (_value); + align(); +} + +void HBox::onWidgetCreated(MyGUI::Widget* _widget) +{ + align(); +} + +void HBox::onWidgetDestroy(MyGUI::Widget* _widget) +{ + align(); +} + MyGUI::IntSize HBox::getRequestedSize () { MyGUI::IntSize size(0,0); @@ -954,6 +994,16 @@ MyGUI::IntSize HBox::getRequestedSize () if (i != getChildCount()-1) size.width += mSpacing; } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.height = std::max(size.height, requested.height); + size.width = size.width + requested.width; + if (i != getChildCount()-1) + size.width += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; } return size; } @@ -963,7 +1013,69 @@ MyGUI::IntSize HBox::getRequestedSize () void VBox::align () { - // not yet implemented + unsigned int count = getChildCount (); + size_t v_stretched_count = 0; + int total_height = 0; + int total_width = 0; + std::vector< std::pair > sizes; + for (unsigned int i = 0; i < count; ++i) + { + MyGUI::Widget* w = getChildAt(i); + bool vstretch = w->getUserString ("VStretch") == "true"; + v_stretched_count += vstretch; + AutoSizedWidget* aw = dynamic_cast(w); + if (aw) + { + sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); + total_height += aw->getRequestedSize ().height; + total_width = std::max(total_width, aw->getRequestedSize ().width); + } + else + { + sizes.push_back (std::make_pair(w->getSize(), vstretch)); + total_height += w->getSize().height; + } + + if (i != count-1) + total_height += mSpacing; + } + + if (mAutoResize && (total_width+mPadding*2 != getSize().width || total_height+mPadding*2 != getSize().height)) + { + setSize(MyGUI::IntSize(total_width+mPadding*2, total_height+mPadding*2)); + return; + } + + + int curY = 0; + for (unsigned int i = 0; i < count; ++i) + { + if (i==0) + curY += mPadding; + + MyGUI::Widget* w = getChildAt(i); + + bool hstretch = w->getUserString ("HStretch") == "true"; + int width = hstretch ? total_width : sizes[i].first.width; + + MyGUI::IntCoord widgetCoord; + widgetCoord.top = curY; + widgetCoord.left = mPadding + (getSize().width-mPadding*2 - width) / 2; + int height = sizes[i].second ? sizes[i].first.height + (getSize().height-mPadding*2 - total_height)/v_stretched_count + : sizes[i].first.height; + widgetCoord.height = height; + widgetCoord.width = width; + w->setCoord(widgetCoord); + curY += height; + + if (i != count-1) + curY += mSpacing; + } +} + +void VBox::setPropertyOverride(const std::string& _key, const std::string& _value) +{ + Box::_setPropertyImpl (_key, _value); } void VBox::setSize (const MyGUI::IntSize& _value) @@ -972,6 +1084,12 @@ void VBox::setSize (const MyGUI::IntSize& _value) align(); } +void VBox::setCoord (const MyGUI::IntCoord& _value) +{ + MyGUI::Widget::setCoord (_value); + align(); +} + MyGUI::IntSize VBox::getRequestedSize () { MyGUI::IntSize size(0,0); @@ -986,6 +1104,26 @@ MyGUI::IntSize VBox::getRequestedSize () if (i != getChildCount()-1) size.height += mSpacing; } + else + { + MyGUI::IntSize requested = getChildAt(i)->getSize (); + size.width = std::max(size.width, requested.width); + size.height = size.height + requested.height; + if (i != getChildCount()-1) + size.height += mSpacing; + } + size.height += mPadding*2; + size.width += mPadding*2; } return size; } + +void VBox::onWidgetCreated(MyGUI::Widget* _widget) +{ + align(); +} + +void VBox::onWidgetDestroy(MyGUI::Widget* _widget) +{ + align(); +} diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 16c2adec9..6298ea77d 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -357,10 +357,13 @@ namespace MWGui protected: virtual void align() = 0; - virtual void setPropertyOverride(const std::string& _key, const std::string& _value); - + virtual void _setPropertyImpl(const std::string& _key, const std::string& _value); int mSpacing; // how much space to put between elements + + int mPadding; // outer padding + + bool mAutoResize; // auto resize the box so that it exactly fits all elements }; class HBox : public Box, public MyGUI::Widget @@ -369,10 +372,16 @@ namespace MWGui public: virtual void setSize (const MyGUI::IntSize &_value); + virtual void setCoord (const MyGUI::IntCoord &_value); protected: virtual void align(); virtual MyGUI::IntSize getRequestedSize(); + + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + + virtual void onWidgetCreated(MyGUI::Widget* _widget); + virtual void onWidgetDestroy(MyGUI::Widget* _widget); }; class VBox : public Box, public MyGUI::Widget @@ -381,10 +390,16 @@ namespace MWGui public: virtual void setSize (const MyGUI::IntSize &_value); + virtual void setCoord (const MyGUI::IntCoord &_value); protected: virtual void align(); virtual MyGUI::IntSize getRequestedSize(); + + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + + virtual void onWidgetCreated(MyGUI::Widget* _widget); + virtual void onWidgetDestroy(MyGUI::Widget* _widget); }; } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 0241cc734..5efd2b47a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -45,6 +45,7 @@ #include "quickkeysmenu.hpp" #include "loadingscreen.hpp" #include "levelupdialog.hpp" +#include "waitdialog.hpp" using namespace MWGui; @@ -71,6 +72,8 @@ WindowManager::WindowManager( , mSpellWindow(NULL) , mLoadingScreen(NULL) , mCharGen(NULL) + , mLevelupDialog(NULL) + , mWaitDialog(NULL) , mPlayerClass() , mPlayerName() , mPlayerRaceId() @@ -150,6 +153,7 @@ WindowManager::WindowManager( mSpellWindow = new SpellWindow(*this); mQuickKeysMenu = new QuickKeysMenu(*this); mLevelupDialog = new LevelupDialog(*this); + mWaitDialog = new WaitDialog(*this); mLoadingScreen = new LoadingScreen(mOgre->getScene (), mOgre->getWindow (), *this); mLoadingScreen->onResChange (w,h); @@ -204,6 +208,7 @@ WindowManager::~WindowManager() delete mSpellWindow; delete mLoadingScreen; delete mLevelupDialog; + delete mWaitDialog; cleanupGarbage(); @@ -252,6 +257,7 @@ void WindowManager::updateVisible() mSpellWindow->setVisible(false); mQuickKeysMenu->setVisible(false); mLevelupDialog->setVisible(false); + mWaitDialog->setVisible(false); mHud->setVisible(true); @@ -304,7 +310,8 @@ void WindowManager::updateVisible() mAlchemyWindow->setVisible(true); break; case GM_Rest: - mLevelupDialog->setVisible(true); + //mLevelupDialog->setVisible(true); + mWaitDialog->setVisible(true); break; case GM_Name: case GM_Race: diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a02aa17b5..15c9c6631 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -63,6 +63,7 @@ namespace MWGui class QuickKeysMenu; class LoadingScreen; class LevelupDialog; + class WaitDialog; class WindowManager : public MWBase::WindowManager { @@ -229,6 +230,7 @@ namespace MWGui QuickKeysMenu* mQuickKeysMenu; LoadingScreen* mLoadingScreen; LevelupDialog* mLevelupDialog; + WaitDialog* mWaitDialog; CharacterCreation* mCharGen; diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 55230fa76..44235fa0e 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -73,6 +73,7 @@ set(MYGUI_FILES openmw_spell_buying_window.layout openmw_loading_screen.layout openmw_levelup_dialog.layout + openmw_wait_dialog.layout smallbars.png VeraMono.ttf markers.png diff --git a/files/mygui/openmw_wait_dialog.layout b/files/mygui/openmw_wait_dialog.layout new file mode 100644 index 000000000..527d5158d --- /dev/null +++ b/files/mygui/openmw_wait_dialog.layout @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4d4ba6698abdd8e968fd98dc2386b748b19aa6a0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Sep 2012 19:00:20 +0200 Subject: [PATCH 640/688] fix character preview destruction; don't allow empty names in name dialog --- apps/openmw/mwgui/text_input.cpp | 16 ++++++++++++++-- apps/openmw/mwrender/characterpreview.cpp | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 9d5ba9896..3dbe75165 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -47,10 +47,22 @@ void TextInputDialog::open() void TextInputDialog::onOkClicked(MyGUI::Widget* _sender) { - eventDone(this); + if (mTextEdit->getCaption() == "") + { + mWindowManager.messageBox ("#{sNotifyMessage37}", std::vector()); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); } void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) { - eventDone(this); + if (mTextEdit->getCaption() == "") + { + mWindowManager.messageBox ("#{sNotifyMessage37}", std::vector()); + MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit); + } + else + eventDone(this); } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 01c5d594a..c8852bff5 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -72,6 +72,8 @@ namespace MWRender CharacterPreview::~CharacterPreview () { Ogre::TextureManager::getSingleton().remove(mName); + mSceneMgr->destroyCamera (mName); + delete mAnimation; } From ab698bb401ad668f9c6713471a49ee45d73d571c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Sep 2012 20:53:32 +0200 Subject: [PATCH 641/688] date time label --- apps/openmw/mwbase/world.hpp | 5 ++ apps/openmw/mwgui/waitdialog.cpp | 101 ++++++++++++++++++++++++++ apps/openmw/mwgui/waitdialog.hpp | 16 ++++ apps/openmw/mwgui/widgets.cpp | 10 ++- apps/openmw/mwworld/worldimp.cpp | 20 +++++ apps/openmw/mwworld/worldimp.hpp | 5 ++ files/mygui/openmw_wait_dialog.layout | 11 +-- 7 files changed, 159 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b841e5427..05b31b3df 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -160,6 +160,9 @@ namespace MWBase virtual void setDay (int day) = 0; ///< Set in-game time day. + virtual int getDay() = 0; + virtual int getMonth() = 0; + virtual MWWorld::TimeStamp getTimeStamp() const = 0; ///< Return current in-game time stamp. @@ -281,6 +284,8 @@ namespace MWBase virtual void renderPlayer() = 0; virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; + + virtual bool canRest() = 0; }; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 5b491a4f2..622191566 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -1,12 +1,113 @@ #include "waitdialog.hpp" +#include + +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwworld/timestamp.hpp" + +#include "widgets.hpp" + + namespace MWGui { WaitDialog::WaitDialog(MWBase::WindowManager &parWindowManager) : WindowBase("openmw_wait_dialog.layout", parWindowManager) { + getWidget(mDateTimeText, "DateTimeText"); + getWidget(mRestText, "RestText"); + getWidget(mHourText, "HourText"); + getWidget(mHourSlider, "HourSlider"); + getWidget(mUntilHealedButton, "UntilHealedButton"); + getWidget(mWaitButton, "WaitButton"); + getWidget(mCancelButton, "CancelButton"); + + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onCancelButtonClicked); + mUntilHealedButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onUntilHealedButtonClicked); + mWaitButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onWaitButtonClicked); + + mHourSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &WaitDialog::onHourSliderChangedPosition); + } + + void WaitDialog::open() + { + setCanRest(MWBase::Environment::get().getWorld ()->canRest ()); + + onHourSliderChangedPosition(mHourSlider, 0); + mHourSlider->setScrollPosition (0); + + // http://www.uesp.net/wiki/Lore:Calendar + std::string month; + int m = MWBase::Environment::get().getWorld ()->getMonth (); + if (m == 0) + month = "#{sMonthMorningstar}"; + else if (m == 1) + month = "#{sMonthSunsdawn}"; + else if (m == 2) + month = "#{sMonthFirstseed}"; + else if (m == 3) + month = "#{sMonthRainshand}"; + else if (m == 4) + month = "#{sMonthSecondseed}"; + else if (m == 5) + month = "#{sMonthMidyear}"; + else if (m == 6) + month = "#{sMonthSunsheight}"; + else if (m == 7) + month = "#{sMonthLastseed}"; + else if (m == 8) + month = "#{sMonthHeartfire}"; + else if (m == 9) + month = "#{sMonthFrostfall}"; + else if (m == 10) + month = "#{sMonthSunsdusk}"; + else if (m == 11) + month = "#{sMonthEveningstar}"; + + int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour (); + bool pm = hour >= 12; + if (hour >= 13) hour -= 12; + + std::string dateTimeText = + boost::lexical_cast(MWBase::Environment::get().getWorld ()->getDay ()+1) + " " + + month + " (#{sDay} " + boost::lexical_cast(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay ()+1) + + ") " + boost::lexical_cast(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); + + mDateTimeText->setCaptionWithReplacing (dateTimeText); + center(); } + void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender) + { + + } + + void WaitDialog::onWaitButtonClicked(MyGUI::Widget* sender) + { + //MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); + } + + void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) + { + mWindowManager.removeGuiMode (GM_Rest); + } + + void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) + { + mHourText->setCaptionWithReplacing (boost::lexical_cast(position+1) + " #{sRestMenu2}"); + } + + void WaitDialog::setCanRest (bool canRest) + { + mUntilHealedButton->setVisible(canRest); + mWaitButton->setCaptionWithReplacing (canRest ? "#{sRest}" : "#{sWait}"); + mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : "#{sRestIllegal}"); + } + } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 3c68c1249..040779819 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -11,7 +11,23 @@ namespace MWGui public: WaitDialog(MWBase::WindowManager& parWindowManager); + virtual void open(); + protected: + MyGUI::TextBox* mDateTimeText; + MyGUI::TextBox* mRestText; + MyGUI::TextBox* mHourText; + MyGUI::ScrollBar* mHourSlider; + MyGUI::Button* mUntilHealedButton; + MyGUI::Button* mWaitButton; + MyGUI::Button* mCancelButton; + + void onUntilHealedButtonClicked(MyGUI::Widget* sender); + void onWaitButtonClicked(MyGUI::Widget* sender); + void onCancelButtonClicked(MyGUI::Widget* sender); + void onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position); + + void setCanRest(bool canRest); }; } diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index e8cee2141..4f7ecb775 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -998,7 +998,10 @@ MyGUI::IntSize HBox::getRequestedSize () { MyGUI::IntSize requested = getChildAt(i)->getSize (); size.height = std::max(size.height, requested.height); - size.width = size.width + requested.width; + + if (getChildAt(i)->getUserString("HStretch") != "true") + size.width = size.width + requested.width; + if (i != getChildCount()-1) size.width += mSpacing; } @@ -1108,7 +1111,10 @@ MyGUI::IntSize VBox::getRequestedSize () { MyGUI::IntSize requested = getChildAt(i)->getSize (); size.width = std::max(size.width, requested.width); - size.height = size.height + requested.height; + + if (getChildAt(i)->getUserString("VStretch") != "true") + size.height = size.height + requested.height; + if (i != getChildCount()-1) size.height += mSpacing; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 831611618..af0a53655 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -450,6 +450,16 @@ namespace MWWorld mRendering->skySetDate (mGlobalVariables->getInt ("day"), month); } + int World::getDay() + { + return mGlobalVariables->getInt("day"); + } + + int World::getMonth() + { + return mGlobalVariables->getInt("month"); + } + TimeStamp World::getTimeStamp() const { return TimeStamp (mGlobalVariables->getFloat ("gamehour"), @@ -1241,4 +1251,14 @@ namespace MWWorld { mRendering->setupExternalRendering (rendering); } + + bool World::canRest () + { + Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); + assert (currentCell); + if (currentCell->cell->data.flags & ESM::Cell::NoSleep) + return false; + else + return true; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d48a0ec81..e918a85e0 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -179,6 +179,9 @@ namespace MWWorld virtual void setDay (int day); ///< Set in-game time day. + virtual int getDay(); + virtual int getMonth(); + virtual TimeStamp getTimeStamp() const; ///< Return current in-game time stamp. @@ -312,6 +315,8 @@ namespace MWWorld virtual void renderPlayer(); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); + + virtual bool canRest(); }; } diff --git a/files/mygui/openmw_wait_dialog.layout b/files/mygui/openmw_wait_dialog.layout index 527d5158d..66e0ec22f 100644 --- a/files/mygui/openmw_wait_dialog.layout +++ b/files/mygui/openmw_wait_dialog.layout @@ -6,22 +6,19 @@ - + - - + - - + - - + From 4829f47a4f86234658cc1a11406b761636d34151 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Sep 2012 21:04:43 +0200 Subject: [PATCH 642/688] rewrote the fader to use Rectangle2d instead of overlay, in order to not cover the UI --- libs/openengine/ogre/fader.cpp | 61 +++++++++++++------------- libs/openengine/ogre/fader.hpp | 72 +++++++++++++++---------------- libs/openengine/ogre/renderer.cpp | 2 +- 3 files changed, 66 insertions(+), 69 deletions(-) diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 41b7773ea..498b659ee 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -1,52 +1,49 @@ #include "fader.hpp" -#include -#include -#include #include #include #include #include +#include +#include -#define FADE_OVERLAY_NAME "FadeInOutOverlay" -#define FADE_OVERLAY_PANEL_NAME "FadeInOutOverlayPanel" -#define FADE_MATERIAL_NAME "FadeInOutMaterial" using namespace Ogre; using namespace OEngine::Render; -Fader::Fader() : - mMode(FadingMode_In), - mRemainingTime(0.f), - mTargetTime(0.f), - mTargetAlpha(0.f), - mCurrentAlpha(0.f), - mStartAlpha(0.f) +Fader::Fader(Ogre::SceneManager* sceneMgr) + : mSceneMgr(sceneMgr) + , mMode(FadingMode_In) + , mRemainingTime(0.f) + , mTargetTime(0.f) + , mTargetAlpha(0.f) + , mCurrentAlpha(0.f) + , mStartAlpha(0.f) { - // Create the fading material - MaterialPtr material = MaterialManager::getSingleton().create( FADE_MATERIAL_NAME, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); + MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); Pass* pass = material->getTechnique(0)->getPass(0); pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); mFadeTextureUnit = pass->createTextureUnitState(); mFadeTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(0.f, 0.f, 0.f)); // always black colour - // Create the overlay - OverlayManager& ovm = OverlayManager::getSingleton(); + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mRectangle->setMaterial("FadeInOutMaterial"); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY-1); + // Use infinite AAB to always stay visible + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + mRectangle->setBoundingBox(aabInf); + // Attach background to the scene + Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(mRectangle); + mRectangle->setVisible(false); +} - mOverlay = ovm.create( FADE_OVERLAY_NAME ); - - OverlayContainer* overlay_panel; - overlay_panel = (OverlayContainer*)ovm.createOverlayElement("Panel", FADE_OVERLAY_PANEL_NAME); - - // position it over the whole screen - overlay_panel->_setPosition(0, 0); - overlay_panel->_setDimensions(1, 1); - - overlay_panel->setMaterialName( FADE_MATERIAL_NAME ); - overlay_panel->show(); - mOverlay->add2D(overlay_panel); - mOverlay->hide(); +Fader::~Fader() +{ + delete mRectangle; } void Fader::update(float dt) @@ -69,12 +66,12 @@ void Fader::update(float dt) mRemainingTime -= dt; } - if (mCurrentAlpha == 0.f) mOverlay->hide(); + if (mCurrentAlpha == 0.f) mRectangle->setVisible(false); } void Fader::applyAlpha() { - mOverlay->show(); + mRectangle->setVisible(true); mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha); } diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp index efb12a5d2..bddf5dc91 100644 --- a/libs/openengine/ogre/fader.hpp +++ b/libs/openengine/ogre/fader.hpp @@ -4,52 +4,52 @@ /* A class that handles fading in the screen from black or fading it out to black. - To achieve this, it uses a full-screen Ogre::Overlay - - inspired by http://www.ogre3d.org/tikiwiki/FadeEffectOverlay (heavily adjusted) + To achieve this, it uses a full-screen Rectangle2d */ namespace Ogre { class TextureUnitState; - class Overlay; + class Rectangle2D; + class SceneManager; } namespace OEngine { namespace Render { - class Fader - { - public: - Fader(); - - void update(float dt); - - void fadeIn(const float time); - void fadeOut(const float time); - void fadeTo(const int percent, const float time); - - private: - enum FadingMode + class Fader { - FadingMode_In, - FadingMode_Out + public: + Fader(Ogre::SceneManager* sceneMgr); + ~Fader(); + + void update(float dt); + + void fadeIn(const float time); + void fadeOut(const float time); + void fadeTo(const int percent, const float time); + + private: + enum FadingMode + { + FadingMode_In, + FadingMode_Out + }; + + void applyAlpha(); + + Ogre::TextureUnitState* mFadeTextureUnit; + Ogre::Rectangle2D* mRectangle; + + FadingMode mMode; + + float mRemainingTime; + float mTargetTime; + float mTargetAlpha; + float mCurrentAlpha; + float mStartAlpha; + + Ogre::SceneManager* mSceneMgr; }; - - void applyAlpha(); - - Ogre::TextureUnitState* mFadeTextureUnit; - Ogre::Overlay* mOverlay; - - FadingMode mMode; - - float mRemainingTime; - float mTargetTime; - float mTargetAlpha; - float mCurrentAlpha; - float mStartAlpha; - - protected: - }; -}} + }} #endif diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 65911df34..ec68c37ab 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -243,7 +243,7 @@ void OgreRenderer::createScene(const std::string& camName, float fov, float near // Alter the camera aspect ratio to match the viewport mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight())); - mFader = new Fader(); + mFader = new Fader(mScene); } void OgreRenderer::adjustViewport() From bf5e30b24fc22c3d68126ae6f159a40787633240 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Sep 2012 02:53:06 +0200 Subject: [PATCH 643/688] everything done except health/mana restore --- apps/openmw/mwbase/world.hpp | 7 +- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/mode.hpp | 1 + apps/openmw/mwgui/race.cpp | 2 +- apps/openmw/mwgui/waitdialog.cpp | 112 +++++++++++++++++- apps/openmw/mwgui/waitdialog.hpp | 29 +++++ apps/openmw/mwgui/widgets.cpp | 1 - apps/openmw/mwgui/windowmanagerimp.cpp | 10 +- apps/openmw/mwscript/guiextensions.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 24 +++- apps/openmw/mwworld/worldimp.hpp | 7 +- files/mygui/CMakeLists.txt | 1 + files/mygui/openmw_alchemy_window.layout | 5 +- files/mygui/openmw_chargen_birth.layout | 4 +- files/mygui/openmw_chargen_class.layout | 4 +- .../mygui/openmw_chargen_create_class.layout | 4 +- ...penmw_chargen_generate_class_result.layout | 4 +- files/mygui/openmw_chargen_race.layout | 5 +- files/mygui/openmw_chargen_review.layout | 4 +- files/mygui/openmw_confirmation_dialog.layout | 4 +- files/mygui/openmw_container_window.layout | 4 +- files/mygui/openmw_count_window.layout | 4 +- files/mygui/openmw_trade_window.layout | 4 +- .../openmw_wait_dialog_progressbar.layout | 13 ++ libs/openengine/ogre/fader.cpp | 1 + 25 files changed, 228 insertions(+), 30 deletions(-) create mode 100644 files/mygui/openmw_wait_dialog_progressbar.layout diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 05b31b3df..521bbb988 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -285,7 +285,12 @@ namespace MWBase virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; - virtual bool canRest() = 0; + virtual int canRest() = 0; + ///< check if the player is allowed to rest \n + /// 0 - yes \n + /// 1 - only waiting \n + /// 2 - player is underwater \n + /// 3 - enemies are nearby (not implemented) }; } diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 3616283d9..980a9e13b 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -173,7 +173,7 @@ namespace MWGui creatureStats.setLevel (creatureStats.getLevel()+1); pcStats.levelUp (); - mWindowManager.removeGuiMode (GM_Rest); + mWindowManager.removeGuiMode (GM_Levelup); } } diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index eb2c52b26..64aa1dc21 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -20,6 +20,7 @@ namespace MWGui GM_Dialogue, // NPC interaction GM_Barter, GM_Rest, + GM_RestBed, GM_SpellBuying, GM_Levelup, diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 3fe70d959..5cb73e682 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -40,7 +40,7 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) getWidget(mHeadRotate, "HeadRotate"); mHeadRotate->setScrollRange(50); - mHeadRotate->setScrollPosition(20); + mHeadRotate->setScrollPosition(25); mHeadRotate->setScrollViewPage(10); mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 622191566..cc9019f2b 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -9,6 +9,11 @@ #include "../mwbase/environment.hpp" #include "../mwworld/timestamp.hpp" +#include "../mwworld/player.hpp" +#include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" #include "widgets.hpp" @@ -16,8 +21,34 @@ namespace MWGui { + WaitDialogProgressBar::WaitDialogProgressBar(MWBase::WindowManager &parWindowManager) + : WindowBase("openmw_wait_dialog_progressbar.layout", parWindowManager) + { + getWidget(mProgressBar, "ProgressBar"); + getWidget(mProgressText, "ProgressText"); + } + + void WaitDialogProgressBar::open() + { + center(); + } + + void WaitDialogProgressBar::setProgress (int cur, int total) + { + mProgressBar->setProgressRange (total); + mProgressBar->setProgressPosition (cur); + mProgressText->setCaption(boost::lexical_cast(cur) + "/" + boost::lexical_cast(total)); + } + + // --------------------------------------------------------------------------------------------------------- + WaitDialog::WaitDialog(MWBase::WindowManager &parWindowManager) : WindowBase("openmw_wait_dialog.layout", parWindowManager) + , mProgressBar(parWindowManager) + , mWaiting(false) + , mSleeping(false) + , mHours(1) + , mRemainingTime(0.05) { getWidget(mDateTimeText, "DateTimeText"); getWidget(mRestText, "RestText"); @@ -32,11 +63,27 @@ namespace MWGui mWaitButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WaitDialog::onWaitButtonClicked); mHourSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &WaitDialog::onHourSliderChangedPosition); + + mProgressBar.setVisible (false); } void WaitDialog::open() { - setCanRest(MWBase::Environment::get().getWorld ()->canRest ()); + if (!MWBase::Environment::get().getWindowManager ()->getRestEnabled ()) + { + mWindowManager.popGuiMode (); + } + + int canRest = MWBase::Environment::get().getWorld ()->canRest (); + + if (canRest == 2) + { + // resting underwater or mid-air not allowed + mWindowManager.messageBox ("#{sNotifyMessage1}", std::vector()); + mWindowManager.popGuiMode (); + } + + setCanRest(canRest == 0); onHourSliderChangedPosition(mHourSlider, 0); mHourSlider->setScrollPosition (0); @@ -79,28 +126,38 @@ namespace MWGui + ") " + boost::lexical_cast(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); mDateTimeText->setCaptionWithReplacing (dateTimeText); - - center(); } void WaitDialog::onUntilHealedButtonClicked(MyGUI::Widget* sender) { - + startWaiting(); } void WaitDialog::onWaitButtonClicked(MyGUI::Widget* sender) { - //MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1); + startWaiting(); + } + + void WaitDialog::startWaiting () + { + MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.2); + setVisible(false); + mProgressBar.setVisible (true); + mWaiting = true; + mCurHour = 0; + mRemainingTime = 0.05; + mProgressBar.setProgress (0, mHours); } void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) { - mWindowManager.removeGuiMode (GM_Rest); + mWindowManager.popGuiMode (); } void WaitDialog::onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position) { mHourText->setCaptionWithReplacing (boost::lexical_cast(position+1) + " #{sRestMenu2}"); + mHours = position+1; } void WaitDialog::setCanRest (bool canRest) @@ -108,6 +165,49 @@ namespace MWGui mUntilHealedButton->setVisible(canRest); mWaitButton->setCaptionWithReplacing (canRest ? "#{sRest}" : "#{sWait}"); mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : "#{sRestIllegal}"); + + mSleeping = canRest; + + dynamic_cast(mMainWidget)->notifyChildrenSizeChanged(); + center(); + } + + void WaitDialog::onFrame(float dt) + { + if (!mWaiting) + return; + + mRemainingTime -= dt; + + if (mRemainingTime < 0) + { + mRemainingTime = 0.05; + ++mCurHour; + mProgressBar.setProgress (mCurHour, mHours); + + if (mCurHour <= mHours) + MWBase::Environment::get().getWorld ()->advanceTime (1); + } + + if (mCurHour > mHours) + stopWaiting(); + } + + void WaitDialog::stopWaiting () + { + MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2); + mProgressBar.setVisible (false); + mWindowManager.popGuiMode (); + mWaiting = false; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats pcstats = MWWorld::Class::get(player).getNpcStats(player); + + // trigger levelup if possible + if (mSleeping && pcstats.getLevelProgress () >= 10) + { + mWindowManager.pushGuiMode (GM_Levelup); + } } } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 040779819..1d079a8aa 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -6,6 +6,20 @@ namespace MWGui { + class WaitDialogProgressBar : public WindowBase + { + public: + WaitDialogProgressBar(MWBase::WindowManager& parWindowManager); + + virtual void open(); + + void setProgress(int cur, int total); + + protected: + MyGUI::ProgressBar* mProgressBar; + MyGUI::TextBox* mProgressText; + }; + class WaitDialog : public WindowBase { public: @@ -13,6 +27,10 @@ namespace MWGui virtual void open(); + void onFrame(float dt); + + void bedActivated() { setCanRest(true); } + protected: MyGUI::TextBox* mDateTimeText; MyGUI::TextBox* mRestText; @@ -22,12 +40,23 @@ namespace MWGui MyGUI::Button* mWaitButton; MyGUI::Button* mCancelButton; + bool mWaiting; + bool mSleeping; + int mCurHour; + int mHours; + float mRemainingTime; + + WaitDialogProgressBar mProgressBar; + void onUntilHealedButtonClicked(MyGUI::Widget* sender); void onWaitButtonClicked(MyGUI::Widget* sender); void onCancelButtonClicked(MyGUI::Widget* sender); void onHourSliderChangedPosition(MyGUI::ScrollBar* sender, size_t position); void setCanRest(bool canRest); + + void startWaiting(); + void stopWaiting(); }; } diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 4f7ecb775..313820534 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -795,7 +795,6 @@ void MWDynamicStat::initialiseOverride() // --------------------------------------------------------------------------------------------------------------------- - void AutoSizedWidget::notifySizeChange (MyGUI::Widget* w) { if (w->getParent () != 0) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5efd2b47a..5de35367a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -310,9 +310,15 @@ void WindowManager::updateVisible() mAlchemyWindow->setVisible(true); break; case GM_Rest: - //mLevelupDialog->setVisible(true); mWaitDialog->setVisible(true); break; + case GM_RestBed: + mWaitDialog->setVisible(true); + mWaitDialog->bedActivated(); + break; + case GM_Levelup: + mLevelupDialog->setVisible(true); + break; case GM_Name: case GM_Race: case GM_Class: @@ -547,6 +553,8 @@ void WindowManager::onFrame (float frameDuration) mStatsWindow->onFrame(); + mWaitDialog->onFrame(frameDuration); + mHud->onFrame(frameDuration); mDialogueWindow->checkReferenceAvailable(); diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 7f3b9777b..6e5540fa8 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -160,7 +160,7 @@ opcodeEnableStatsReviewMenu); new OpEnableRest ()); interpreter.installSegment5 (opcodeShowRestMenu, - new OpShowDialogue (MWGui::GM_Rest)); + new OpShowDialogue (MWGui::GM_RestBed)); interpreter.installSegment5 (opcodeGetButtonPressed, new OpGetButtonPressed); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index af0a53655..dae95255e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1252,13 +1252,27 @@ namespace MWWorld mRendering->setupExternalRendering (rendering); } - bool World::canRest () + int World::canRest () { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); - assert (currentCell); + + Ogre::Vector3 playerPos; + float* pos = mPlayer->getPlayer ().getRefData ().getPosition ().pos; + playerPos.x = pos[0]; + playerPos.y = pos[1]; + playerPos.z = pos[2]; + + std::pair hit = + mPhysics->castRay(playerPos, Ogre::Vector3(0,0,-1), 50); + bool isOnGround = (hit.first ? (hit.second.distance (playerPos) < 25) : false); + + if (!isOnGround || isUnderwater (*currentCell->cell, playerPos)) + return 2; + if (currentCell->cell->data.flags & ESM::Cell::NoSleep) - return false; - else - return true; + return 1; + + return 0; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e918a85e0..90cd2151b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -316,7 +316,12 @@ namespace MWWorld virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); - virtual bool canRest(); + virtual int canRest(); + ///< check if the player is allowed to rest \n + /// 0 - yes \n + /// 1 - only waiting \n + /// 2 - player is underwater \n + /// 3 - enemies are nearby (not implemented) }; } diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 44235fa0e..ae8d3afbe 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -74,6 +74,7 @@ set(MYGUI_FILES openmw_loading_screen.layout openmw_levelup_dialog.layout openmw_wait_dialog.layout + openmw_wait_dialog_progressbar.layout smallbars.png VeraMono.ttf markers.png diff --git a/files/mygui/openmw_alchemy_window.layout b/files/mygui/openmw_alchemy_window.layout index 458d24b2c..8471f69df 100644 --- a/files/mygui/openmw_alchemy_window.layout +++ b/files/mygui/openmw_alchemy_window.layout @@ -77,8 +77,9 @@ - - + + + diff --git a/files/mygui/openmw_chargen_birth.layout b/files/mygui/openmw_chargen_birth.layout index 5edec72d3..f4b8c518d 100644 --- a/files/mygui/openmw_chargen_birth.layout +++ b/files/mygui/openmw_chargen_birth.layout @@ -15,7 +15,9 @@ - + + + diff --git a/files/mygui/openmw_chargen_class.layout b/files/mygui/openmw_chargen_class.layout index baa36b24a..00e3793bc 100644 --- a/files/mygui/openmw_chargen_class.layout +++ b/files/mygui/openmw_chargen_class.layout @@ -61,7 +61,9 @@ - + + + diff --git a/files/mygui/openmw_chargen_create_class.layout b/files/mygui/openmw_chargen_create_class.layout index 5e08db6e5..54f73f221 100644 --- a/files/mygui/openmw_chargen_create_class.layout +++ b/files/mygui/openmw_chargen_create_class.layout @@ -59,7 +59,9 @@ - + + + diff --git a/files/mygui/openmw_chargen_generate_class_result.layout b/files/mygui/openmw_chargen_generate_class_result.layout index 65dbf016c..aa4a89d28 100644 --- a/files/mygui/openmw_chargen_generate_class_result.layout +++ b/files/mygui/openmw_chargen_generate_class_result.layout @@ -21,7 +21,9 @@ - + + + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index b69073899..a9ec9905d 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -57,7 +57,10 @@ - + + + + diff --git a/files/mygui/openmw_chargen_review.layout b/files/mygui/openmw_chargen_review.layout index dbe7a4780..97e32cfe2 100644 --- a/files/mygui/openmw_chargen_review.layout +++ b/files/mygui/openmw_chargen_review.layout @@ -112,7 +112,9 @@ - + + + diff --git a/files/mygui/openmw_confirmation_dialog.layout b/files/mygui/openmw_confirmation_dialog.layout index fe7f7cbf6..46b477407 100644 --- a/files/mygui/openmw_confirmation_dialog.layout +++ b/files/mygui/openmw_confirmation_dialog.layout @@ -14,7 +14,9 @@ - + + + diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 896566fdd..94e9458a5 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -12,7 +12,9 @@ - + + + diff --git a/files/mygui/openmw_count_window.layout b/files/mygui/openmw_count_window.layout index a021d7df9..5812ec7fd 100644 --- a/files/mygui/openmw_count_window.layout +++ b/files/mygui/openmw_count_window.layout @@ -22,7 +22,9 @@ - + + + diff --git a/files/mygui/openmw_trade_window.layout b/files/mygui/openmw_trade_window.layout index 7de6c85e6..d38377f98 100644 --- a/files/mygui/openmw_trade_window.layout +++ b/files/mygui/openmw_trade_window.layout @@ -56,7 +56,9 @@ - + + + diff --git a/files/mygui/openmw_wait_dialog_progressbar.layout b/files/mygui/openmw_wait_dialog_progressbar.layout new file mode 100644 index 000000000..93793fd8e --- /dev/null +++ b/files/mygui/openmw_wait_dialog_progressbar.layout @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 498b659ee..ba532b527 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -39,6 +39,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr) Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); node->attachObject(mRectangle); mRectangle->setVisible(false); + mRectangle->setVisibilityFlags (0x01); } Fader::~Fader() From be44810623c8c970defb65bebe6c8c292cf0b79e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Sep 2012 03:11:23 +0200 Subject: [PATCH 644/688] implemented GetPCSleep --- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/waitdialog.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 14 ++++++++++++++ 6 files changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 0fd78225a..49f1f95b0 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -222,6 +222,8 @@ namespace MWBase virtual void enableRest() = 0; virtual bool getRestEnabled() = 0; + + virtual bool getPlayerSleeping() = 0; }; } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 1d079a8aa..4a401c0c6 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -31,6 +31,8 @@ namespace MWGui void bedActivated() { setCanRest(true); } + bool getSleeping() { return mWaiting && mSleeping; } + protected: MyGUI::TextBox* mDateTimeText; MyGUI::TextBox* mRestText; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5de35367a..9d4aaf749 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -946,3 +946,8 @@ void WindowManager::loadingDone () { mLoadingScreen->loadingDone (); } + +bool WindowManager::getPlayerSleeping () +{ + return mWaitDialog->getSleeping(); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 15c9c6631..0ed5e1fea 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -204,6 +204,8 @@ namespace MWGui virtual void enableRest() { mRestAllowed = true; } virtual bool getRestEnabled() { return mRestAllowed; } + virtual bool getPlayerSleeping(); + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index d58045a0a..a9f2cd143 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -202,5 +202,6 @@ op 0x200019b: PlaceItem op 0x200019c: PlaceAtPc op 0x200019d: PlaceAtMe op 0x200019e: PlaceAtMe Explicit -opcodes 0x200019f-0x3ffffff unused +op 0x200019f: GetPcSleep +opcodes 0x20001a0-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 864c1a1ee..a869f882b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -10,6 +10,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" @@ -20,6 +21,16 @@ namespace MWScript { namespace Misc { + class OpGetPcSleep : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push (MWBase::Environment::get().getWindowManager ()->getPlayerSleeping()); + } + }; + class OpXBox : public Interpreter::Opcode0 { public: @@ -249,6 +260,7 @@ namespace MWScript const int opcodeTogglePathgrid = 0x2000146; const int opcodeDontSaveObject = 0x2000153; const int opcodeToggleVanityMode = 0x2000174; + const int opcodeGetPcSleep = 0x200019f; void registerExtensions (Compiler::Extensions& extensions) { @@ -273,6 +285,7 @@ namespace MWScript extensions.registerInstruction ("dontsaveobject", "", opcodeDontSaveObject); extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); + extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -293,6 +306,7 @@ namespace MWScript interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater); interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); + interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep); } } } From 4b5706d65292a627c528cf573a79eee73c4fdc0b Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Wed, 19 Sep 2012 11:16:13 +0400 Subject: [PATCH 645/688] Fixed packaging on OS X --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aee073d3e..614a840c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -633,7 +633,7 @@ if (APPLE) include(CPack) endif (APPLE) -if (NOT WIN32 AND NOT DPKG_PROGRAM) +if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) ## Non Debian based Linux building # paths set(BINDIR "${CMAKE_INSTALL_PREFIX}/usr/bin" CACHE PATH "Where to install binaries") @@ -664,4 +664,4 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM) # Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) -endif(NOT WIN32 AND NOT DPKG_PROGRAM) +endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) From 32de090079690f683d7c38db50b5f61669d1c98e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Sep 2012 19:34:02 +0200 Subject: [PATCH 646/688] Allow changing OpenGL RTT mode, useful for example if the driver reports incorrect capabilities --- apps/openmw/engine.cpp | 1 + files/settings-default.cfg | 4 ++++ libs/openengine/ogre/renderer.cpp | 4 ++++ libs/openengine/ogre/renderer.hpp | 1 + 4 files changed, 10 insertions(+) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index db6edf397..d10ae9979 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -299,6 +299,7 @@ void OMW::Engine::go() mOgre->configure( mCfgMgr.getLogPath().string(), renderSystem, + Settings::Manager::getString("opengl rtt mode", "Video"), false); // This has to be added BEFORE MyGUI is initialized, as it needs diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 8fab98da2..6a77b7fc0 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -24,6 +24,10 @@ antialiasing = none vsync = false +# opengl render to texture mode, valid options: +# PBuffer, FBO, Copy +opengl rtt mode = FBO + [GUI] # 1 is fully opaque menu transparency = 0.84 diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 65911df34..5f3435c52 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -145,6 +145,7 @@ float OgreRenderer::getFPS() void OgreRenderer::configure(const std::string &logPath, const std::string& renderSystem, + const std::string& rttMode, bool _logging) { // Set up logging first @@ -198,6 +199,9 @@ void OgreRenderer::configure(const std::string &logPath, if (rs == 0) throw std::runtime_error ("RenderSystem with name " + renderSystem + " not found, make sure the plugins are loaded"); mRoot->setRenderSystem(rs); + + if (rs->getName().find("OpenGL") != std::string::npos) + rs->setConfigOption ("RTT Preferred Mode", rttMode); } void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 95bcac3b8..a8788dfca 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -132,6 +132,7 @@ namespace OEngine void configure( const std::string &logPath, // Path to directory where to store log files const std::string &renderSystem, + const std::string &rttMode, bool _logging); // Enable or disable logging /// Create a window with the given title From ede9a2b170be2e51998440d2e71ede7cc178b52f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 19 Sep 2012 19:56:02 +0200 Subject: [PATCH 647/688] fixed an orientation problem --- apps/openmw/mwscript/transformationextensions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 5db557174..706d8d720 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -442,7 +442,7 @@ namespace MWScript ESM::Position ipos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); - Ogre::Quaternion rot(Ogre::Radian(ipos.rot[2]), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); if(direction == 0) pos = pos + distance*rot.yAxis(); else if(direction == 1) pos = pos - distance*rot.yAxis(); else if(direction == 2) pos = pos - distance*rot.xAxis(); @@ -485,7 +485,7 @@ namespace MWScript ESM::Position ipos = me.getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); - Ogre::Quaternion rot(Ogre::Radian(ipos.rot[2]), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); if(direction == 0) pos = pos + distance*rot.yAxis(); else if(direction == 1) pos = pos - distance*rot.yAxis(); else if(direction == 2) pos = pos - distance*rot.xAxis(); From d9f6072f02b279963b6e1929dfe519fb5580f999 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Sep 2012 23:25:07 +0200 Subject: [PATCH 648/688] gamma correct rendering & adjustment setting --- apps/openmw/mwrender/renderingmanager.cpp | 2 ++ files/materials/core.h | 4 ++++ files/materials/objects.shader | 14 +++++++++----- files/materials/terrain.shader | 16 ++++++++++------ files/settings-default.cfg | 2 ++ 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 308a01688..5fffa30d3 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -129,6 +129,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(0))); sh::Factory::getInstance ().setSharedParameter ("windDir_windSpeed", sh::makeProperty(new sh::Vector3(0.5, -0.8, 0.2))); sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(1, 0.6))); + sh::Factory::getInstance ().setSharedParameter ("gammaCorrection", sh::makeProperty(new sh::FloatValue( + Settings::Manager::getFloat ("gamma", "Video")))); applyCompositors(); diff --git a/files/materials/core.h b/files/materials/core.h index cba716777..e9577bbf3 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -1,3 +1,7 @@ +#define gammaCorrectRead(v) pow(v, float3(gammaCorrection,gammaCorrection,gammaCorrection)) +#define gammaCorrectOutput(v) pow(v, float3(1.f/gammaCorrection,1.f/gammaCorrection,1.f/gammaCorrection)) + + #if SH_HLSL == 1 || SH_CG == 1 #define shTexture2D sampler2D diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 8e5cbf76e..45f33774d 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -112,6 +112,8 @@ shUniform(float, far) @shAutoConstant(far, far_clip_distance) #endif + shUniform(float, gammaCorrection) @shSharedParameter(gammaCorrection, gammaCorrection) + #if LIGHTING shInput(float3, normalPassthrough) shInput(float3, objSpacePositionPassthrough) @@ -173,7 +175,8 @@ SH_START_PROGRAM { shOutputColour(0) = shSample(diffuseMap, UV); - + shOutputColour(0).xyz = gammaCorrectRead(shOutputColour(0).xyz); + #if LIGHTING float3 normal = normalize(normalPassthrough); float3 lightDir; @@ -259,7 +262,7 @@ // regular fog only if fragment is above water if (worldPos.y > waterLevel) #endif - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColour), fogValue); #endif // prevent negative colour output (for example with negative lights) @@ -274,12 +277,11 @@ float waterSunGradient = dot(eyeVec, -normalize(lightDirectionWS0.xyz)); waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); - float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; + float3 waterSunColour = gammaCorrectRead(float3(0.0,1.0,0.85)) *waterSunGradient * 0.5; float waterGradient = dot(eyeVec, float3(0.0,-1.0,0.0)); waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; - float3 waterext = float3(0.6, 0.9, 1.0);//water extinction + float3 watercolour = ( gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; @@ -292,6 +294,8 @@ shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater * waterEnabled); #endif + shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz); + #if MRT shOutputColour(1) = float4(depthPassthrough / far,1,1,1); #endif diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index 7ef26d035..9183595e3 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -137,6 +137,8 @@ shSampler2D(normalMap) // global normal map + shUniform(float, gammaCorrection) @shSharedParameter(gammaCorrection, gammaCorrection) + @shForeach(@shPropertyString(num_blendmaps)) shSampler2D(blendMap@shIterator) @@ -247,9 +249,9 @@ #if IS_FIRST_PASS == 1 && @shIterator == 0 // first layer of first pass doesn't need a blend map - albedo = shSample(diffuseMap0, UV * 10).rgb; + albedo = gammaCorrectRead(shSample(diffuseMap0, UV * 10).rgb); #else - albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator)); + albedo = shLerp(albedo, gammaCorrectRead(shSample(diffuseMap@shIterator, UV * 10).rgb), blendValues@shPropertyString(blendmap_component_@shIterator)); #endif @shEndForeach @@ -336,7 +338,7 @@ // regular fog only if fragment is above water if (worldPos.y > waterLevel) #endif - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColour, fogValue); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColour), fogValue); #endif // prevent negative colour output (for example with negative lights) @@ -351,12 +353,12 @@ float waterSunGradient = dot(eyeVec, -normalize(lightDirectionWS0.xyz)); waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); - float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; + float3 waterSunColour = gammaCorrectRead(float3(0.0,1.0,0.85))*waterSunGradient * 0.5; float waterGradient = dot(eyeVec, float3(0.0,-1.0,0.0)); waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; - float3 waterext = float3(0.6, 0.9, 1.0);//water extinction + float3 watercolour = (gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; + float3 waterext = gammaCorrectRead(float3(0.6, 0.9, 1.0));//water extinction watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; @@ -369,6 +371,8 @@ shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater); #endif + shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz); + #if MRT shOutputColour(1) = float4(depth / far,1,1,1); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6a77b7fc0..1768b2f5e 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -28,6 +28,8 @@ vsync = false # PBuffer, FBO, Copy opengl rtt mode = FBO +gamma = 2.2 + [GUI] # 1 is fully opaque menu transparency = 0.84 From b9a3f8e8d74e2fc97fc48c1ad6e613d6f4d5b280 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Sep 2012 23:47:42 +0200 Subject: [PATCH 649/688] gamma slider --- apps/openmw/mwgui/settingswindow.cpp | 19 +++++++++++++++++ apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ files/mygui/openmw_settings_window.layout | 25 +++++++++++++++++++---- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index d1e206a95..cdfe4d2b6 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -122,6 +122,7 @@ namespace MWGui getWidget(mInvertYButton, "InvertYButton"); getWidget(mUISensitivitySlider, "UISensitivitySlider"); getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); + getWidget(mGammaSlider, "GammaSlider"); mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -143,6 +144,7 @@ namespace MWGui mViewDistanceSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mResolutionList->eventListChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onResolutionSelected); mAnisotropySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + mGammaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mShadowsEnabledButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadowsLargeDistance->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -200,6 +202,14 @@ namespace MWGui getWidget(fovText, "FovText"); fovText->setCaption("Field of View (" + boost::lexical_cast(int(Settings::Manager::getFloat("field of view", "General"))) + ")"); + float gammaVal = (Settings::Manager::getFloat("gamma", "Video")-0.1f)/(3.f-0.1f); + mGammaSlider->setScrollPosition(gammaVal * (mGammaSlider->getScrollRange()-1)); + MyGUI::TextBox* gammaText; + getWidget(gammaText, "GammaText"); + std::stringstream gamma; + gamma << std::setprecision (2) << Settings::Manager::getFloat("gamma", "Video"); + gammaText->setCaption("Gamma (" + gamma.str() + ")"); + float anisotropyVal = Settings::Manager::getInt("anisotropy", "General") / 16.0; mAnisotropySlider->setScrollPosition(anisotropyVal * (mAnisotropySlider->getScrollRange()-1)); std::string tf = Settings::Manager::getString("texture filtering", "General"); @@ -511,6 +521,15 @@ namespace MWGui fovText->setCaption("Field of View (" + boost::lexical_cast(int((1-val) * sFovMin + val * sFovMax)) + ")"); Settings::Manager::setFloat("field of view", "General", (1-val) * sFovMin + val * sFovMax); } + else if (scroller == mGammaSlider) + { + Settings::Manager::setFloat("gamma", "Video", (1-val) * 0.1f + val * 3.f); + MyGUI::TextBox* gammaText; + getWidget(gammaText, "GammaText"); + std::stringstream gamma; + gamma << std::setprecision (2) << Settings::Manager::getFloat("gamma", "Video"); + gammaText->setCaption("Gamma (" + gamma.str() + ")"); + } else if (scroller == mAnisotropySlider) { mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(int(val*16)) + ")"); diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index d1f35ed71..e878d0abe 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -40,6 +40,7 @@ namespace MWGui MyGUI::Button* mFPSButton; MyGUI::ScrollBar* mViewDistanceSlider; MyGUI::ScrollBar* mFOVSlider; + MyGUI::ScrollBar* mGammaSlider; MyGUI::ScrollBar* mAnisotropySlider; MyGUI::Button* mTextureFilteringButton; MyGUI::TextBox* mAnisotropyLabel; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5fffa30d3..d63f9c2fc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -749,6 +749,11 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); mObjects.rebuildStaticGeometry (); } + else if (it->second == "gamma" && it->first == "Video") + { + sh::Factory::getInstance ().setSharedParameter ("gammaCorrection", sh::makeProperty(new sh::FloatValue( + Settings::Manager::getFloat ("gamma", "Video")))); + } else if (it->second == "shader mode" && it->first == "General") { sh::Language lang; diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 38d2786e5..2f9b5a67f 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -157,7 +157,7 @@ - + @@ -174,20 +174,37 @@ - + - + - + + + + + + + + + + + + + + + + + + From 86cfc91ef357988564105fa60d254059c6e09b37 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Sep 2012 13:56:37 +0200 Subject: [PATCH 650/688] global map rendering --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 3 + apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwgui/map_window.cpp | 30 +++-- apps/openmw/mwgui/map_window.hpp | 3 + apps/openmw/mwrender/globalmap.cpp | 145 ++++++++++++++++++++++ apps/openmw/mwrender/globalmap.hpp | 23 ++++ apps/openmw/mwrender/renderingmanager.cpp | 14 ++- apps/openmw/mwrender/renderingmanager.hpp | 5 + apps/openmw/mwworld/worldimp.cpp | 5 + apps/openmw/mwworld/worldimp.hpp | 1 + components/esm/loadland.cpp | 6 +- components/esm/loadland.hpp | 2 +- files/mygui/openmw_map_window.layout | 15 ++- 14 files changed, 233 insertions(+), 22 deletions(-) create mode 100644 apps/openmw/mwrender/globalmap.cpp create mode 100644 apps/openmw/mwrender/globalmap.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2542ef350..95d15f118 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows - compositors characterpreview externalrendering + compositors characterpreview externalrendering globalmap ) add_openmw_dir (mwinput diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d10ae9979..b526d8e0d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -307,6 +307,8 @@ void OMW::Engine::go() //addResourcesDirectory(mResDir); + addResourcesDirectory(mCfgMgr.getCachePath ().string()); + addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); addResourcesDirectory(mResDir / "gbuffer"); @@ -367,6 +369,7 @@ void OMW::Engine::go() pos.pos[2] = 0; mEnvironment.getWorld()->renderPlayer(); + mEnvironment.getWorld()->renderGlobalMap(); if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) { diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b841e5427..a725d389d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -279,6 +279,7 @@ namespace MWBase virtual void togglePlayerLooking(bool enable) = 0; virtual void renderPlayer() = 0; + virtual void renderGlobalMap() = 0; virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; }; diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index f92e4ee4f..b2b8538ba 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" @@ -254,8 +255,11 @@ MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : getWidget(mLocalMap, "LocalMap"); getWidget(mGlobalMap, "GlobalMap"); + getWidget(mGlobalMapImage, "GlobalMapImage"); getWidget(mPlayerArrow, "Compass"); + mGlobalMap->setVisible (false); + getWidget(mButton, "WorldButton"); mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); mButton->setCaptionWithReplacing("#{sWorld}"); @@ -276,21 +280,22 @@ void MapWindow::setCellName(const std::string& cellName) void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { if (_id!=MyGUI::MouseButton::Left) return; - if (!mGlobal) - mLastDragPos = MyGUI::IntPoint(_left, _top); + mLastDragPos = MyGUI::IntPoint(_left, _top); } void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { if (_id!=MyGUI::MouseButton::Left) return; - if (!mGlobal) - { - MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; - mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); + MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos; - mLastDragPos = MyGUI::IntPoint(_left, _top); - } + if (!mGlobal) + mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff ); + else + mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); + + + mLastDragPos = MyGUI::IntPoint(_left, _top); } void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) @@ -307,3 +312,12 @@ void MapWindow::onPinToggled() { mWindowManager.setMinimapVisibility(!mPinned); } + +void MapWindow::open() +{ + mGlobalMapImage->setImageTexture("GlobalMap.png"); + + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().getByName("GlobalMap.png"); + mGlobalMap->setCanvasSize (tex->getWidth(), tex->getHeight()); + mGlobalMapImage->setSize(tex->getWidth(), tex->getHeight()); +} diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 1203233bf..abb6a0653 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -62,12 +62,15 @@ namespace MWGui void setCellName(const std::string& cellName); + virtual void open(); + private: void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onWorldButtonClicked(MyGUI::Widget* _sender); MyGUI::ScrollView* mGlobalMap; + MyGUI::ImageBox* mGlobalMapImage; MyGUI::ImageBox* mPlayerArrow; MyGUI::Button* mButton; MyGUI::IntPoint mLastDragPos; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp new file mode 100644 index 000000000..2edd32595 --- /dev/null +++ b/apps/openmw/mwrender/globalmap.cpp @@ -0,0 +1,145 @@ +#include "globalmap.hpp" + +#include +#include +#include +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include +#include + +namespace MWRender +{ + + GlobalMap::GlobalMap(const std::string &cacheDir) + : mCacheDir(cacheDir) + { + } + + + void GlobalMap::render () + { + + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().getByName ("GlobalMap.png"); + + if (tex.isNull ()) + { + + int cellSize = 24; + + Ogre::Image image; + + int width = cellSize*61; + int height = cellSize*61; + + Ogre::uchar data[width * height * 3]; + + for (int x = -30; x <= 30; ++x) + { + for (int y = -30; y <= 30; ++y) + { + ESM::Land* land = MWBase::Environment::get().getWorld ()->getStore ().lands.search (x,y); + + if (land) + { + if (!land->dataLoaded) + { + land->loadData(); + } + } + + for (int cellY=0; cellYlandData->heights[vertexY * ESM::Land::LAND_SIZE + vertexX]; + + + if (landHeight >= 0) + { + if (landHeight >= hillHeight) + { + float factor = std::min(1.f, float(landHeight-hillHeight)/mountainHeight); + r = (hillColour.r * (1-factor) + mountainColour.r * factor) * 255; + g = (hillColour.g * (1-factor) + mountainColour.g * factor) * 255; + b = (hillColour.b * (1-factor) + mountainColour.b * factor) * 255; + } + else + { + float factor = std::min(1.f, float(landHeight)/hillHeight); + r = (groundColour.r * (1-factor) + hillColour.r * factor) * 255; + g = (groundColour.g * (1-factor) + hillColour.g * factor) * 255; + b = (groundColour.b * (1-factor) + hillColour.b * factor) * 255; + } + } + else + { + if (landHeight >= -100) + { + float factor = std::min(1.f, -1*landHeight/100.f); + r = (((waterShallowColour+groundColour)/2).r * (1-factor) + waterShallowColour.r * factor) * 255; + g = (((waterShallowColour+groundColour)/2).g * (1-factor) + waterShallowColour.g * factor) * 255; + b = (((waterShallowColour+groundColour)/2).b * (1-factor) + waterShallowColour.b * factor) * 255; + } + else + { + float factor = std::min(1.f, -1*(landHeight-100)/1000.f); + r = (waterShallowColour.r * (1-factor) + waterDeepColour.r * factor) * 255; + g = (waterShallowColour.g * (1-factor) + waterDeepColour.g * factor) * 255; + b = (waterShallowColour.b * (1-factor) + waterDeepColour.b * factor) * 255; + } + } + + } + else + { + r = waterDeepColour.r * 255; + g = waterDeepColour.g * 255; + b = waterDeepColour.b * 255; + } + + data[texelY * height * 3 + texelX * 3] = r; + data[texelY * height * 3 + texelX * 3+1] = g; + data[texelY * height * 3 + texelX * 3+2] = b; + } + assert(0); + } + } + } + + image.loadDynamicImage (data, width, height, Ogre::PF_B8G8R8); + + image.save (mCacheDir + "/GlobalMap.png"); + + tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, width, height, 0, Ogre::PF_B8G8R8, Ogre::TU_DEFAULT); + tex->loadImage(image); + } + tex->load(); + } + +} diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp new file mode 100644 index 000000000..b7c199dcf --- /dev/null +++ b/apps/openmw/mwrender/globalmap.hpp @@ -0,0 +1,23 @@ +#ifndef _GAME_RENDER_GLOBALMAP_H +#define _GAME_RENDER_GLOBALMAP_H + +#include + +namespace MWRender +{ + + class GlobalMap + { + public: + GlobalMap(const std::string& cacheDir); + + void render(); + + private: + std::string mCacheDir; + }; + +} + +#endif + diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 308a01688..603d2e16c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -35,6 +35,7 @@ #include "compositors.hpp" #include "npcanimation.hpp" #include "externalrendering.hpp" +#include "globalmap.hpp" using namespace MWRender; using namespace Ogre; @@ -43,7 +44,7 @@ namespace MWRender { RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine) - :mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0), mPhysicsEngine(engine) + : mRendering(_rend), mObjects(mRendering), mActors(mRendering), mAmbientMode(0), mSunEnabled(0), mPhysicsEngine(engine) { // select best shader mode bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); @@ -100,7 +101,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); - // Load resources + ResourceGroupManager::getSingleton ().declareResource ("GlobalMap.png", "Texture", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); // causes light flicker in opengl when moving.. @@ -160,6 +162,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mDebugging = new Debugging(mMwRoot, engine); mLocalMap = new MWRender::LocalMap(&mRendering, this); + mGlobalMap = new GlobalMap(cacheDir.string()); + setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } @@ -176,6 +180,7 @@ RenderingManager::~RenderingManager () delete mOcclusionQuery; delete mCompositors; delete mWater; + delete mGlobalMap; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -887,4 +892,9 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } +void RenderingManager::renderGlobalMap () +{ + mGlobalMap->render (); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index e994e4909..24ec8b15b 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -44,6 +44,7 @@ namespace MWRender class Water; class Compositors; class ExternalRendering; + class GlobalMap; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -194,6 +195,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); + void renderGlobalMap(); + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); @@ -218,6 +221,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Water *mWater; + GlobalMap* mGlobalMap; + OEngine::Render::OgreRenderer &mRendering; MWRender::Objects mObjects; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 831611618..592f2b4b6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1237,6 +1237,11 @@ namespace MWWorld mRendering->renderPlayer(mPlayer->getPlayer()); } + void World::renderGlobalMap () + { + mRendering->renderGlobalMap (); + } + void World::setupExternalRendering (MWRender::ExternalRendering& rendering) { mRendering->setupExternalRendering (rendering); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d48a0ec81..dfb9238f5 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -310,6 +310,7 @@ namespace MWWorld } virtual void renderPlayer(); + virtual void renderGlobalMap(); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); }; diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 96afdf831..024767e90 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -84,11 +84,7 @@ void Land::loadData() { mEsm->restoreContext(context); - //esm.getHNExact(landData->normals, sizeof(VNML), "VNML"); - if (mEsm->isNextSub("VNML")) - { - mEsm->skipHSubSize(12675); - } + mEsm->getHNExact(landData->normals, sizeof(VNML), "VNML"); VHGT rawHeights; diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index ebc314a28..955fb9518 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -60,7 +60,7 @@ struct Land { float heightOffset; float heights[LAND_NUM_VERTS]; - //float normals[LAND_NUM_VERTS * 3]; + float normals[LAND_NUM_VERTS * 3]; uint16_t textures[LAND_NUM_TEXTURES]; bool usingColours; diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 8b64de22e..2b9b43906 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -3,10 +3,6 @@ - - - - @@ -15,9 +11,18 @@ - + + + + + + + + + + From 5f014f7411be4859243895eb4f6d2b19852f5e98 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Sep 2012 14:04:29 +0200 Subject: [PATCH 651/688] oops, left in a testing assertion --- apps/openmw/mwrender/globalmap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 2edd32595..90fd25aff 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -126,7 +126,6 @@ namespace MWRender data[texelY * height * 3 + texelX * 3+1] = g; data[texelY * height * 3 + texelX * 3+2] = b; } - assert(0); } } } From e8bba2b833af9f466ce3b8df63ceb37ad7edca05 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Sep 2012 14:06:30 +0200 Subject: [PATCH 652/688] disabled loading of land normals again, didn't need them after all --- components/esm/loadland.cpp | 6 +++++- components/esm/loadland.hpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 024767e90..05cadae7f 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -84,7 +84,11 @@ void Land::loadData() { mEsm->restoreContext(context); - mEsm->getHNExact(landData->normals, sizeof(VNML), "VNML"); + //mEsm->getHNExact(landData->normals, sizeof(VNML), "VNML"); + if (mEsm->isNextSub("VNML")) + { + mEsm->skipHSubSize(12675); + } VHGT rawHeights; diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 955fb9518..ebc314a28 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -60,7 +60,7 @@ struct Land { float heightOffset; float heights[LAND_NUM_VERTS]; - float normals[LAND_NUM_VERTS * 3]; + //float normals[LAND_NUM_VERTS * 3]; uint16_t textures[LAND_NUM_TEXTURES]; bool usingColours; From e5e3d829d039c5963b45be56f24abf1d0ca16317 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Sep 2012 17:30:09 +0200 Subject: [PATCH 653/688] player arrow & markers --- apps/openmw/mwgui/map_window.cpp | 69 +++++++++++++++++++++++--- apps/openmw/mwgui/map_window.hpp | 11 +++- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++ files/mygui/openmw_map_window.layout | 10 +++- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index b2b8538ba..824e72bd8 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -4,10 +4,12 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwworld/player.hpp" using namespace MWGui; @@ -256,7 +258,8 @@ MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : getWidget(mLocalMap, "LocalMap"); getWidget(mGlobalMap, "GlobalMap"); getWidget(mGlobalMapImage, "GlobalMapImage"); - getWidget(mPlayerArrow, "Compass"); + getWidget(mPlayerArrowLocal, "CompassLocal"); + getWidget(mPlayerArrowGlobal, "CompassGlobal"); mGlobalMap->setVisible (false); @@ -264,12 +267,14 @@ MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); mButton->setCaptionWithReplacing("#{sWorld}"); - MyGUI::Button* eventbox; - getWidget(eventbox, "EventBox"); - eventbox->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); - eventbox->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + getWidget(mEventBoxGlobal, "EventBoxGlobal"); + mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); + getWidget(mEventBoxLocal, "EventBoxLocal"); + mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag); + mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart); - LocalMapBase::init(mLocalMap, mPlayerArrow, this); + LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); } void MapWindow::setCellName(const std::string& cellName) @@ -277,6 +282,35 @@ void MapWindow::setCellName(const std::string& cellName) setTitle(cellName); } +void MapWindow::addVisitedLocation(const std::string& name, int x, int y) +{ + const int cellSize = 24; + + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().getByName("GlobalMap.png"); + + MyGUI::IntCoord widgetCoord( + (x+30)*cellSize+6, + (tex->getHeight()-1) - (y+30)*cellSize+6, + 12, 12); + + + static int _counter=0; + MyGUI::Button* markerWidget = mGlobalMapImage->createWidget("ButtonImage", + widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast(_counter)); + markerWidget->setImageResource("DoorMarker"); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTip"); + markerWidget->setUserString("Caption_Text", name); + ++_counter; + + markerWidget = mEventBoxGlobal->createWidget("", + widgetCoord, MyGUI::Align::Default); + markerWidget->setNeedMouseFocus (true); + markerWidget->setUserString("ToolTipType", "Layout"); + markerWidget->setUserString("ToolTipLayout", "TextToolTip"); + markerWidget->setUserString("Caption_Text", name); +} + void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { if (_id!=MyGUI::MouseButton::Left) return; @@ -320,4 +354,27 @@ void MapWindow::open() Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().getByName("GlobalMap.png"); mGlobalMap->setCanvasSize (tex->getWidth(), tex->getHeight()); mGlobalMapImage->setSize(tex->getWidth(), tex->getHeight()); + + for (unsigned int i=0; igetChildCount (); ++i) + { + if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker") + mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); + } + + Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); + Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); + Ogre::Vector2 dir (orient.yAxis ().x, -orient.yAxis().z); + + float worldX = ((pos.x / 8192.f-0.5) / 30.f+1)/2.f; + float worldY = ((pos.z / 8192.f+1.5) / 30.f+1)/2.f; + + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(tex->getWidth() * worldX - 16, tex->getHeight() * worldY - 16)); + + MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(dir.x, dir.y); + rotatingSubskin->setAngle(angle); + + mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); } diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index abb6a0653..e9c473a29 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -62,6 +62,8 @@ namespace MWGui void setCellName(const std::string& cellName); + void addVisitedLocation(const std::string& name, int x, int y); // adds the marker to the global map + virtual void open(); private: @@ -71,11 +73,18 @@ namespace MWGui MyGUI::ScrollView* mGlobalMap; MyGUI::ImageBox* mGlobalMapImage; - MyGUI::ImageBox* mPlayerArrow; + MyGUI::ImageBox* mPlayerArrowLocal; + MyGUI::ImageBox* mPlayerArrowGlobal; MyGUI::Button* mButton; MyGUI::IntPoint mLastDragPos; bool mGlobal; + MyGUI::Button* mEventBoxGlobal; + MyGUI::Button* mEventBoxLocal; + + int mGlobalMapSizeX; + int mGlobalMapSizeY; + protected: virtual void onPinToggled(); }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 0241cc734..728243d5f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -555,7 +555,10 @@ void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) { std::string name; if (cell->cell->name != "") + { name = cell->cell->name; + mMap->addVisitedLocation (name, cell->cell->getGridX (), cell->cell->getGridY ()); + } else { const ESM::Region* region = MWBase::Environment::get().getWorld()->getStore().regions.search(cell->cell->region); diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 2b9b43906..20cfbbd6e 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -7,10 +7,11 @@ - + + @@ -19,9 +20,14 @@ + + + + + + - From 451a9fd6ac8a03f20462d3d07d674ec3157a9688 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Sep 2012 17:35:30 +0200 Subject: [PATCH 654/688] don't update world arrow position in interior --- apps/openmw/mwgui/map_window.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 824e72bd8..fe357660d 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -368,13 +368,18 @@ void MapWindow::open() float worldX = ((pos.x / 8192.f-0.5) / 30.f+1)/2.f; float worldY = ((pos.z / 8192.f+1.5) / 30.f+1)/2.f; - mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(tex->getWidth() * worldX - 16, tex->getHeight() * worldY - 16)); + // for interiors, we have no choice other than using the last position & direction. + /// \todo save this last position in the savegame? + if (MWBase::Environment::get().getWorld ()->isCellExterior ()) + { + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(tex->getWidth() * worldX - 16, tex->getHeight() * worldY - 16)); - MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); - MyGUI::RotatingSkin* rotatingSubskin = main->castType(); - rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); - float angle = std::atan2(dir.x, dir.y); - rotatingSubskin->setAngle(angle); + MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); + float angle = std::atan2(dir.x, dir.y); + rotatingSubskin->setAngle(angle); + } mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); } From 98c1dc115145ebcf8ce0deb6efc50900e8f82b99 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Sep 2012 18:02:37 +0200 Subject: [PATCH 655/688] ShowMap & FillMap commands, merged next --- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwscript/docs/vmformat.txt | 4 +- apps/openmw/mwscript/guiextensions.cpp | 54 ++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 49f1f95b0..429163136 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -189,6 +189,8 @@ namespace MWBase virtual void allowMouse() = 0; virtual void notifyInputActionBound() = 0; + virtual void addVisitedLocation(const std::string& name, int x, int y) = 0; + virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c72ad9854..c8e56f2c7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -954,3 +954,8 @@ bool WindowManager::getPlayerSleeping () { return mWaitDialog->getSleeping(); } + +void WindowManager::addVisitedLocation(const std::string& name, int x, int y) +{ + mMap->addVisitedLocation (name, x, y); +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 0ed5e1fea..47461f877 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -172,6 +172,8 @@ namespace MWGui virtual void allowMouse(); virtual void notifyInputActionBound(); + virtual void addVisitedLocation(const std::string& name, int x, int y); + virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. virtual void messageBox (const std::string& message, const std::vector& buttons); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index a9f2cd143..a4a9e99fd 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -203,5 +203,7 @@ op 0x200019c: PlaceAtPc op 0x200019d: PlaceAtMe op 0x200019e: PlaceAtMe Explicit op 0x200019f: GetPcSleep -opcodes 0x20001a0-0x3ffffff unused +op 0x20001a0: ShowMap +op 0x20001a1: FillMap +opcodes 0x20001a2-0x3ffffff unused diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 6e5540fa8..a93de0ede 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -1,12 +1,17 @@ #include "guiextensions.hpp" +#include + #include #include #include #include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -91,6 +96,47 @@ namespace MWScript } }; + class OpShowMap : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string cell = (runtime.getStringLiteral (runtime[0].mInteger)); + boost::algorithm::to_lower(cell); + runtime.pop(); + + // "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well." + // http://www.uesp.net/wiki/Tes3Mod:ShowMap + + const ESMS::CellList::ExtCells& extCells = MWBase::Environment::get().getWorld ()->getStore ().cells.extCells; + for (ESMS::CellList::ExtCells::const_iterator it = extCells.begin(); it != extCells.end(); ++it) + { + std::string name = it->second->name; + boost::algorithm::to_lower(name); + if (name.find(cell) != std::string::npos) + MWBase::Environment::get().getWindowManager()->addVisitedLocation (it->second->name, it->first.first, it->first.second); + } + } + }; + + class OpFillMap : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + const ESMS::CellList::ExtCells& extCells = MWBase::Environment::get().getWorld ()->getStore ().cells.extCells; + for (ESMS::CellList::ExtCells::const_iterator it = extCells.begin(); it != extCells.end(); ++it) + { + std::string name = it->second->name; + if (name != "") + MWBase::Environment::get().getWindowManager()->addVisitedLocation (name, it->first.first, it->first.second); + } + } + }; + + const int opcodeEnableBirthMenu = 0x200000e; const int opcodeEnableClassMenu = 0x200000f; const int opcodeEnableNameMenu = 0x2000010; @@ -105,6 +151,8 @@ namespace MWScript const int opcodeGetButtonPressed = 0x2000137; const int opcodeToggleFogOfWar = 0x2000145; const int opcodeToggleFullHelp = 0x2000151; + const int opcodeShowMap = 0x20001a0; + const int opcodeFillMap = 0x20001a1; void registerExtensions (Compiler::Extensions& extensions) { @@ -132,6 +180,9 @@ opcodeEnableStatsReviewMenu); extensions.registerInstruction ("togglefullhelp", "", opcodeToggleFullHelp); extensions.registerInstruction ("tfh", "", opcodeToggleFullHelp); + + extensions.registerInstruction ("showmap", "S", opcodeShowMap); + extensions.registerInstruction ("fillmap", "", opcodeFillMap); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -167,6 +218,9 @@ opcodeEnableStatsReviewMenu); interpreter.installSegment5 (opcodeToggleFogOfWar, new OpToggleFogOfWar); interpreter.installSegment5 (opcodeToggleFullHelp, new OpToggleFullHelp); + + interpreter.installSegment5 (opcodeShowMap, new OpShowMap); + interpreter.installSegment5 (opcodeFillMap, new OpFillMap); } } } From a161b34c67ad41acb412fa3d249457ffb2cd791c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Sep 2012 18:23:27 +0200 Subject: [PATCH 656/688] remove useless member, fix map rendering crash --- apps/openmw/mwgui/map_window.cpp | 13 +++++++------ apps/openmw/mwgui/map_window.hpp | 3 --- apps/openmw/mwrender/globalmap.cpp | 10 +++++++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index fe357660d..ea7b44259 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -286,11 +286,11 @@ void MapWindow::addVisitedLocation(const std::string& name, int x, int y) { const int cellSize = 24; - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().getByName("GlobalMap.png"); + int size = 24 * 61; MyGUI::IntCoord widgetCoord( (x+30)*cellSize+6, - (tex->getHeight()-1) - (y+30)*cellSize+6, + (size-1) - (y+30)*cellSize+6, 12, 12); @@ -351,9 +351,10 @@ void MapWindow::open() { mGlobalMapImage->setImageTexture("GlobalMap.png"); - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().getByName("GlobalMap.png"); - mGlobalMap->setCanvasSize (tex->getWidth(), tex->getHeight()); - mGlobalMapImage->setSize(tex->getWidth(), tex->getHeight()); + int size = 24 * 61; + + mGlobalMap->setCanvasSize (size, size); + mGlobalMapImage->setSize(size, size); for (unsigned int i=0; igetChildCount (); ++i) { @@ -372,7 +373,7 @@ void MapWindow::open() /// \todo save this last position in the savegame? if (MWBase::Environment::get().getWorld ()->isCellExterior ()) { - mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(tex->getWidth() * worldX - 16, tex->getHeight() * worldY - 16)); + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(size * worldX - 16, size * worldY - 16)); MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); MyGUI::RotatingSkin* rotatingSubskin = main->castType(); diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index e9c473a29..a8b5b9cb9 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -82,9 +82,6 @@ namespace MWGui MyGUI::Button* mEventBoxGlobal; MyGUI::Button* mEventBoxLocal; - int mGlobalMapSizeX; - int mGlobalMapSizeY; - protected: virtual void onPinToggled(); }; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 90fd25aff..72adb7579 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -1,5 +1,7 @@ #include "globalmap.hpp" +#include + #include #include #include @@ -23,10 +25,9 @@ namespace MWRender void GlobalMap::render () { + Ogre::TexturePtr tex; - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().getByName ("GlobalMap.png"); - - if (tex.isNull ()) + if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) { int cellSize = 24; @@ -138,6 +139,9 @@ namespace MWRender Ogre::TEX_TYPE_2D, width, height, 0, Ogre::PF_B8G8R8, Ogre::TU_DEFAULT); tex->loadImage(image); } + else + tex = Ogre::TextureManager::getSingleton ().getByName ("GlobalMap.png"); + tex->load(); } From 9106e5307c7deadedc19855e2e931f4eda795cdb Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Sep 2012 14:11:01 +0200 Subject: [PATCH 657/688] moved global map from RenderingManager to gui and removed world method --- apps/openmw/engine.cpp | 3 +-- apps/openmw/mwbase/world.hpp | 1 - apps/openmw/mwgui/map_window.cpp | 15 ++++++++++++--- apps/openmw/mwgui/map_window.hpp | 11 +++++++++-- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++-- apps/openmw/mwgui/windowmanagerimp.hpp | 4 +++- apps/openmw/mwrender/renderingmanager.cpp | 8 -------- apps/openmw/mwrender/renderingmanager.hpp | 2 -- apps/openmw/mwworld/worldimp.cpp | 5 ----- apps/openmw/mwworld/worldimp.hpp | 1 - 10 files changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b526d8e0d..b86923a1d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -339,7 +339,7 @@ void OMW::Engine::go() mEnvironment.setWindowManager (new MWGui::WindowManager( mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), - mScriptConsoleMode)); + mCfgMgr.getCachePath ().string(), mScriptConsoleMode)); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -369,7 +369,6 @@ void OMW::Engine::go() pos.pos[2] = 0; mEnvironment.getWorld()->renderPlayer(); - mEnvironment.getWorld()->renderGlobalMap(); if (const ESM::Cell *exterior = MWBase::Environment::get().getWorld()->getExterior (mCellName)) { diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index ee474f357..521bbb988 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -282,7 +282,6 @@ namespace MWBase virtual void togglePlayerLooking(bool enable) = 0; virtual void renderPlayer() = 0; - virtual void renderGlobalMap() = 0; virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index ea7b44259..f4a387be4 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -11,6 +11,8 @@ #include "../mwbase/environment.hpp" #include "../mwworld/player.hpp" +#include "../mwrender/globalmap.hpp" + using namespace MWGui; LocalMapBase::LocalMapBase() @@ -249,12 +251,14 @@ void LocalMapBase::setPlayerDir(const float x, const float y) // ------------------------------------------------------------------------------------------ -MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : - MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager), - mGlobal(false) +MapWindow::MapWindow(MWBase::WindowManager& parWindowManager, const std::string& cacheDir) + : MWGui::WindowPinnableBase("openmw_map_window.layout", parWindowManager) + , mGlobal(false) { setCoord(500,0,320,300); + mGlobalMapRender = new MWRender::GlobalMap(cacheDir); + getWidget(mLocalMap, "LocalMap"); getWidget(mGlobalMap, "GlobalMap"); getWidget(mGlobalMapImage, "GlobalMapImage"); @@ -277,6 +281,11 @@ MapWindow::MapWindow(MWBase::WindowManager& parWindowManager) : LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this); } +MapWindow::~MapWindow() +{ + delete mGlobalMapRender; +} + void MapWindow::setCellName(const std::string& cellName) { setTitle(cellName); diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index a8b5b9cb9..a094d1862 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -3,6 +3,11 @@ #include "window_pinnable_base.hpp" +namespace MWRender +{ + class GlobalMap; +} + namespace MWGui { class LocalMapBase @@ -57,8 +62,8 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase { public: - MapWindow(MWBase::WindowManager& parWindowManager); - virtual ~MapWindow(){} + MapWindow(MWBase::WindowManager& parWindowManager, const std::string& cacheDir); + virtual ~MapWindow(); void setCellName(const std::string& cellName); @@ -82,6 +87,8 @@ namespace MWGui MyGUI::Button* mEventBoxGlobal; MyGUI::Button* mEventBoxLocal; + MWRender::GlobalMap* mGlobalMapRender; + protected: virtual void onPinToggled(); }; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c8e56f2c7..e6f9e8565 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -50,7 +50,8 @@ using namespace MWGui; WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts) + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts) : mGuiManager(NULL) , mHud(NULL) , mMap(NULL) @@ -132,7 +133,7 @@ WindowManager::WindowManager( mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; mMenu = new MainMenu(w,h); - mMap = new MapWindow(*this); + mMap = new MapWindow(*this, cacheDir); mStatsWindow = new StatsWindow(*this); mConsole = new Console(w,h, consoleOnlyScripts); mJournal = new JournalWindow(*this); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 47461f877..d7773e261 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -71,7 +71,9 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, + OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, + const std::string& cacheDir, bool consoleOnlyScripts); virtual ~WindowManager(); /** diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3f1bf6826..e0d19f8bd 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -164,8 +164,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mDebugging = new Debugging(mMwRoot, engine); mLocalMap = new MWRender::LocalMap(&mRendering, this); - mGlobalMap = new GlobalMap(cacheDir.string()); - setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } @@ -182,7 +180,6 @@ RenderingManager::~RenderingManager () delete mOcclusionQuery; delete mCompositors; delete mWater; - delete mGlobalMap; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -901,9 +898,4 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } -void RenderingManager::renderGlobalMap () -{ - mGlobalMap->render (); -} - } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 24ec8b15b..359809b71 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -195,8 +195,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); - void renderGlobalMap(); - protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d0fbf4bd6..e9b6e6d12 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1253,11 +1253,6 @@ namespace MWWorld mRendering->renderPlayer(mPlayer->getPlayer()); } - void World::renderGlobalMap () - { - mRendering->renderGlobalMap (); - } - void World::setupExternalRendering (MWRender::ExternalRendering& rendering) { mRendering->setupExternalRendering (rendering); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 5c5727905..90cd2151b 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -313,7 +313,6 @@ namespace MWWorld } virtual void renderPlayer(); - virtual void renderGlobalMap(); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); From 1d012d1680c9d4521550a9859aa77e38809e1a29 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Sep 2012 15:24:26 +0200 Subject: [PATCH 658/688] don't update input during loading. Fixes the OSX crash, and doesn't suffer from the issue anymore that I had while implementing this, so let's accept it as a hotfix for now. Note this means we can't switch the window during loading anymore. --- apps/openmw/mwgui/loadingscreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index e41f2eec9..170fc3bc5 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -126,7 +126,7 @@ namespace MWGui // always update input before rendering something, otherwise mygui goes crazy when something was entered in the frame before // (e.g. when using "coc" console command, it would enter an infinite loop and crash due to overflow) - MWBase::Environment::get().getInputManager()->update(0, true); + //MWBase::Environment::get().getInputManager()->update(0, true); Ogre::CompositorChain* chain = Ogre::CompositorManager::getSingleton().getCompositorChain(mWindow->getViewport(0)); From f49401bb5368aec939fbaf379b717a0e69bce005 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Sep 2012 16:26:04 +0200 Subject: [PATCH 659/688] center map on the player, dynamic map size --- apps/openmw/mwgui/map_window.cpp | 40 ++++++++++++------ apps/openmw/mwgui/map_window.hpp | 2 + apps/openmw/mwrender/globalmap.cpp | 66 ++++++++++++++++++++++-------- apps/openmw/mwrender/globalmap.hpp | 14 +++++++ 4 files changed, 93 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index f4a387be4..c4f1ae47d 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -258,6 +258,7 @@ MapWindow::MapWindow(MWBase::WindowManager& parWindowManager, const std::string& setCoord(500,0,320,300); mGlobalMapRender = new MWRender::GlobalMap(cacheDir); + mGlobalMapRender->render(); getWidget(mLocalMap, "LocalMap"); getWidget(mGlobalMap, "GlobalMap"); @@ -293,13 +294,12 @@ void MapWindow::setCellName(const std::string& cellName) void MapWindow::addVisitedLocation(const std::string& name, int x, int y) { - const int cellSize = 24; - - int size = 24 * 61; + float worldX, worldY; + mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY); MyGUI::IntCoord widgetCoord( - (x+30)*cellSize+6, - (size-1) - (y+30)*cellSize+6, + worldX * mGlobalMapRender->getWidth()+6, + worldY * mGlobalMapRender->getHeight()+6, 12, 12); @@ -349,6 +349,9 @@ void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender) mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : "#{sWorld}"); + + if (mGlobal) + globalMapUpdatePlayer (); } void MapWindow::onPinToggled() @@ -360,10 +363,8 @@ void MapWindow::open() { mGlobalMapImage->setImageTexture("GlobalMap.png"); - int size = 24 * 61; - - mGlobalMap->setCanvasSize (size, size); - mGlobalMapImage->setSize(size, size); + mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); for (unsigned int i=0; igetChildCount (); ++i) { @@ -371,18 +372,28 @@ void MapWindow::open() mGlobalMapImage->getChildAt (i)->castType()->setImageResource("DoorMarker"); } + globalMapUpdatePlayer(); + + mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); +} + +void MapWindow::globalMapUpdatePlayer () +{ Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); Ogre::Vector2 dir (orient.yAxis ().x, -orient.yAxis().z); - float worldX = ((pos.x / 8192.f-0.5) / 30.f+1)/2.f; - float worldY = ((pos.z / 8192.f+1.5) / 30.f+1)/2.f; + float worldX, worldY; + mGlobalMapRender->worldPosToImageSpace (pos.x, pos.z, worldX, worldY); + worldX *= mGlobalMapRender->getWidth(); + worldY *= mGlobalMapRender->getHeight(); + // for interiors, we have no choice other than using the last position & direction. /// \todo save this last position in the savegame? if (MWBase::Environment::get().getWorld ()->isCellExterior ()) { - mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(size * worldX - 16, size * worldY - 16)); + mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16)); MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain(); MyGUI::RotatingSkin* rotatingSubskin = main->castType(); @@ -391,5 +402,8 @@ void MapWindow::open() rotatingSubskin->setAngle(angle); } - mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds"); + // set the view offset so that player is in the center + MyGUI::IntSize viewsize = mGlobalMap->getSize(); + MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); + mGlobalMap->setViewOffset(viewoffs); } diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index a094d1862..042e6c8ec 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -76,6 +76,8 @@ namespace MWGui void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onWorldButtonClicked(MyGUI::Widget* _sender); + void globalMapUpdatePlayer(); + MyGUI::ScrollView* mGlobalMap; MyGUI::ImageBox* mGlobalMapImage; MyGUI::ImageBox* mPlayerArrowLocal; diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 72adb7579..be9851baa 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -19,6 +19,8 @@ namespace MWRender GlobalMap::GlobalMap(const std::string &cacheDir) : mCacheDir(cacheDir) + , mMinX(0), mMaxX(0) + , mMinY(0), mMaxY(0) { } @@ -27,21 +29,34 @@ namespace MWRender { Ogre::TexturePtr tex; + // get the size of the world + const ESMS::CellList::ExtCells& extCells = MWBase::Environment::get().getWorld ()->getStore ().cells.extCells; + for (ESMS::CellList::ExtCells::const_iterator it = extCells.begin(); it != extCells.end(); ++it) + { + if (it->first.first < mMinX) + mMinX = it->first.first; + if (it->first.first > mMaxX) + mMaxX = it->first.first; + if (it->first.second < mMinY) + mMinY = it->first.second; + if (it->first.second > mMaxY) + mMaxY = it->first.second; + } + + int cellSize = 24; + mWidth = cellSize*(mMaxX-mMinX+1); + mHeight = cellSize*(mMaxY-mMinY+1); + + if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) { - - int cellSize = 24; - Ogre::Image image; - int width = cellSize*61; - int height = cellSize*61; + Ogre::uchar data[mWidth * mHeight * 3]; - Ogre::uchar data[width * height * 3]; - - for (int x = -30; x <= 30; ++x) + for (int x = mMinX; x <= mMaxX; ++x) { - for (int y = -30; y <= 30; ++y) + for (int y = mMinY; y <= mMaxY; ++y) { ESM::Land* land = MWBase::Environment::get().getWorld ()->getStore ().lands.search (x,y); @@ -61,8 +76,8 @@ namespace MWRender int vertexY = float(cellY)/float(cellSize) * ESM::Land::LAND_SIZE; - int texelX = (x+30) * cellSize + cellX; - int texelY = (height-1) - ((y+30) * cellSize + cellY); + int texelX = (x-mMinX) * cellSize + cellX; + int texelY = (mHeight-1) - ((y-mMinY) * cellSize + cellY); Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19); Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13); @@ -123,20 +138,23 @@ namespace MWRender b = waterDeepColour.b * 255; } - data[texelY * height * 3 + texelX * 3] = r; - data[texelY * height * 3 + texelX * 3+1] = g; - data[texelY * height * 3 + texelX * 3+2] = b; + // uncomment this line to outline cell borders + //if (cellX == 0 || cellX == cellSize-1 || cellY == 0|| cellY == cellSize-1) r = 255; + + data[texelY * mWidth * 3 + texelX * 3] = r; + data[texelY * mWidth * 3 + texelX * 3+1] = g; + data[texelY * mWidth * 3 + texelX * 3+2] = b; } } } } - image.loadDynamicImage (data, width, height, Ogre::PF_B8G8R8); + image.loadDynamicImage (data, mWidth, mHeight, Ogre::PF_B8G8R8); image.save (mCacheDir + "/GlobalMap.png"); tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, width, height, 0, Ogre::PF_B8G8R8, Ogre::TU_DEFAULT); + Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_DEFAULT); tex->loadImage(image); } else @@ -145,4 +163,20 @@ namespace MWRender tex->load(); } + void GlobalMap::worldPosToImageSpace(float x, float z, float& imageX, float& imageY) + { + imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1); + + imageY = 1.f-float(-z / 8192.f - mMinY) / (mMaxY - mMinY + 1); + } + + void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY) + { + imageX = float(x - mMinX) / (mMaxX - mMinX + 1); + + // NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is + imageY = 1.f-float(y - mMinY + 1) / (mMaxY - mMinY + 1); + } + + } diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index b7c199dcf..01f5fdb9e 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -13,8 +13,22 @@ namespace MWRender void render(); + int getWidth() { return mWidth; } + int getHeight() { return mHeight; } + + void worldPosToImageSpace(float x, float z, float& imageX, float& imageY); + ///< @param x x ogre coords + /// @param z z ogre coords + + void cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY); + private: std::string mCacheDir; + + int mWidth; + int mHeight; + + int mMinX, mMaxX, mMinY, mMaxY; }; } From 872fcf3e3daae89f8f97a39adbcbf8293cf0746a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Sep 2012 17:53:16 +0200 Subject: [PATCH 660/688] sleeping restoration of health, magicka, and fatigue --- apps/openmw/mwbase/mechanicsmanager.hpp | 3 + apps/openmw/mwgui/waitdialog.cpp | 5 ++ apps/openmw/mwmechanics/actors.cpp | 56 ++++++++++++++++++- apps/openmw/mwmechanics/actors.hpp | 5 ++ .../mwmechanics/mechanicsmanagerimp.cpp | 5 ++ .../mwmechanics/mechanicsmanagerimp.hpp | 3 + components/esm/loadgmst.cpp | 2 +- components/esm/loadgmst.hpp | 2 +- 8 files changed, 78 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index c5f1847af..39d7e6e1a 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -71,6 +71,9 @@ namespace MWBase virtual void setPlayerClass (const ESM::Class& class_) = 0; ///< Set player class to custom class. + + virtual void restoreDynamicStats() = 0; + ///< If the player is sleeping, this should be called every hour. }; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index cc9019f2b..3210f1db9 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -7,6 +7,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/timestamp.hpp" #include "../mwworld/player.hpp" @@ -186,7 +187,11 @@ namespace MWGui mProgressBar.setProgress (mCurHour, mHours); if (mCurHour <= mHours) + { MWBase::Environment::get().getWorld ()->advanceTime (1); + if (mSleeping) + MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats (); + } } if (mCurHour > mHours) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index b4cc40fec..ab52693f2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -7,9 +7,15 @@ #include +#include + #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + #include "creaturestats.hpp" namespace MWMechanics @@ -20,7 +26,7 @@ namespace MWMechanics adjustMagicEffects (ptr); calculateCreatureStatModifiers (ptr); calculateDynamicStats (ptr); - + // AI CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); creatureStats.getAiSequence().execute (ptr); @@ -76,6 +82,46 @@ namespace MWMechanics creatureStats.getFatigue().setBase(strength+willpower+agility+endurance); } + void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) + { + CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + + if (duration == 3600) + { + // stunted magicka + bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(136)).mMagnitude > 0; + + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + stats.getHealth().setCurrent(stats.getHealth ().getCurrent () + + 0.1 * endurance); + + const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + float fFatigueReturnBase = store.gameSettings.find("fFatigueReturnBase")->getFloat (); + float fFatigueReturnMult = store.gameSettings.find("fFatigueReturnMult")->getFloat (); + float fEndFatigueMult = store.gameSettings.find("fEndFatigueMult")->getFloat (); + + float capacity = MWWorld::Class::get(ptr).getCapacity(ptr); + float encumbrance = MWWorld::Class::get(ptr).getEncumbrance(ptr); + float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); + + float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); + x *= fEndFatigueMult * endurance; + stats.getFatigue ().setCurrent (stats.getFatigue ().getCurrent () + 3600 * x); + + if (!stunted) + { + float fRestMagicMult = store.gameSettings.find("fRestMagicMult")->getFloat (); + stats.getMagicka().setCurrent (stats.getMagicka ().getCurrent () + + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified ()); + } + } + + + + } + + void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr) { CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); @@ -159,4 +205,12 @@ namespace MWMechanics movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector)); } } + + void Actors::restoreDynamicStats() + { + for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter) + { + calculateRestoration (*iter, 3600); + } + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index d5dcef487..7e5a0ac86 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -31,6 +31,9 @@ namespace MWMechanics void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr); + void calculateRestoration (const MWWorld::Ptr& ptr, float duration); + + public: Actors(); @@ -54,6 +57,8 @@ namespace MWMechanics ///< This function is normally called automatically during the update process, but it can /// also be called explicitly at any time to force an update. + void restoreDynamicStats(); + ///< If the player is sleeping, this should be called every hour. }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2ac146c2f..de016811b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -282,6 +282,11 @@ namespace MWMechanics mActors.update (movement, duration, paused); } + void MechanicsManager::restoreDynamicStats() + { + mActors.restoreDynamicStats (); + } + void MechanicsManager::setPlayerName (const std::string& name) { MWBase::Environment::get().getWorld()->getPlayer().setName (name); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index d5fd3b6f2..3a41e8fa6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -73,6 +73,9 @@ namespace MWMechanics virtual void setPlayerClass (const ESM::Class& class_); ///< Set player class to custom class. + + virtual void restoreDynamicStats(); + ///< If the player is sleeping, this should be called every hour. }; } diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index 4bd464da6..491592bb9 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -186,7 +186,7 @@ int GameSetting::getInt() const } } -int GameSetting::getFloat() const +float GameSetting::getFloat() const { switch (type) { diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index f63028731..df6003462 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -87,7 +87,7 @@ struct GameSetting int getInt() const; ///< Throws an exception if GMST is not of type int or float. - int getFloat() const; + float getFloat() const; ///< Throws an exception if GMST is not of type int or float. std::string getString() const; From b91d74d394f16b5625834507044a79a1926ebf2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Sep 2012 17:56:15 +0200 Subject: [PATCH 661/688] fix normalized encumbrance --- apps/openmw/mwmechanics/actors.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ab52693f2..87a058394 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -104,6 +104,8 @@ namespace MWMechanics float capacity = MWWorld::Class::get(ptr).getCapacity(ptr); float encumbrance = MWWorld::Class::get(ptr).getEncumbrance(ptr); float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); + if (normalizedEncumbrance > 1) + normalizedEncumbrance = 1; float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; From 845dddbab8cd01715ebacfb2148528ac6d47f926 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Sep 2012 18:09:07 +0200 Subject: [PATCH 662/688] fix a weird problem with the fading overlay --- libs/openengine/ogre/fader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index ba532b527..6965ffc78 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -24,7 +24,8 @@ Fader::Fader(Ogre::SceneManager* sceneMgr) MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); Pass* pass = material->getTechnique(0)->getPass(0); pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); - mFadeTextureUnit = pass->createTextureUnitState(); + pass->setDepthWriteEnabled (false); + mFadeTextureUnit = pass->createTextureUnitState("black.png"); mFadeTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(0.f, 0.f, 0.f)); // always black colour mRectangle = new Ogre::Rectangle2D(true); From 47d9fae89f649258bc2d038c4bac2ffd0297291f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Sep 2012 20:35:50 +0200 Subject: [PATCH 663/688] don't use cache --- apps/openmw/mwrender/globalmap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index be9851baa..5e0a63c77 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -48,7 +48,8 @@ namespace MWRender mHeight = cellSize*(mMaxY-mMinY+1); - if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) + //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) + if (1) { Ogre::Image image; @@ -151,7 +152,7 @@ namespace MWRender image.loadDynamicImage (data, mWidth, mHeight, Ogre::PF_B8G8R8); - image.save (mCacheDir + "/GlobalMap.png"); + //image.save (mCacheDir + "/GlobalMap.png"); tex = Ogre::TextureManager::getSingleton ().createManual ("GlobalMap.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mWidth, mHeight, 0, Ogre::PF_B8G8R8, Ogre::TU_DEFAULT); From f365cab824b0b806dd774b89db2912fb6dc754d8 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Fri, 21 Sep 2012 16:10:08 -0400 Subject: [PATCH 664/688] Possible fix for a issue --- libs/openengine/bullet/pmove.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp index 645abf205..6a2127eae 100644 --- a/libs/openengine/bullet/pmove.cpp +++ b/libs/openengine/bullet/pmove.cpp @@ -237,7 +237,7 @@ bool PM_SlideMove( bool gravity ) { // entity is completely trapped in another solid //pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration - pm->ps.velocity.z = 0; + pm->ps.velocity = Ogre::Vector3(0,0,0); return true; } From 8a81a4a3346535567d64f1bbbaf7b49804f66db3 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 22 Sep 2012 13:03:58 +0400 Subject: [PATCH 665/688] Fixed typo on openmw.cfg that caused launcher crash on OS X --- files/openmw.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/openmw.cfg b/files/openmw.cfg index c79369d05..b37e5a22b 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,4 +1,4 @@ -data="?global?"data" +data="?global?data" data="?mw?Data Files" data-local="?local?data" resources=${MORROWIND_RESOURCE_FILES} From 78b2d51cfce0d378bf2bc1993ddf6469a589c596 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Sep 2012 16:52:31 +0200 Subject: [PATCH 666/688] fix a map crash --- apps/openmw/mwgui/map_window.cpp | 4 ++-- apps/openmw/mwrender/renderingmanager.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index c4f1ae47d..0fc4233b6 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -266,6 +266,8 @@ MapWindow::MapWindow(MWBase::WindowManager& parWindowManager, const std::string& getWidget(mPlayerArrowLocal, "CompassLocal"); getWidget(mPlayerArrowGlobal, "CompassGlobal"); + mGlobalMapImage->setImageTexture("GlobalMap.png"); + mGlobalMap->setVisible (false); getWidget(mButton, "WorldButton"); @@ -361,8 +363,6 @@ void MapWindow::onPinToggled() void MapWindow::open() { - mGlobalMapImage->setImageTexture("GlobalMap.png"); - mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e0d19f8bd..f0833e82d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -101,7 +101,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); - ResourceGroupManager::getSingleton ().declareResource ("GlobalMap.png", "Texture", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + //ResourceGroupManager::getSingleton ().declareResource ("GlobalMap.png", "Texture", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); From dbf59d14dc02410a71904d728888d06d2d01c5ba Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Sep 2012 16:52:53 +0200 Subject: [PATCH 667/688] water gamma correction --- files/materials/water.shader | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/files/materials/water.shader b/files/materials/water.shader index 8f46aed34..947dc72f5 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -123,9 +123,9 @@ #define REFR_BUMP 0.06 // refraction distortion amount #define SCATTER_AMOUNT 3.0 // amount of sunlight scattering - #define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering + #define SCATTER_COLOUR gammaCorrectRead(float3(0.0,1.0,0.95)) // colour of sunlight scattering - #define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction + #define SUN_EXT gammaCorrectRead(float3(0.45, 0.55, 0.68)) //sunlight extinction #define SPEC_HARDNESS 256 // specular highlights hardness @@ -177,6 +177,7 @@ shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0) shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0) + shUniform(float, gammaCorrection) @shSharedParameter(gammaCorrection, gammaCorrection) shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping) @@ -242,7 +243,7 @@ float s = shSaturate(dot(lR, vVec)*2.0-1.2); float lightScatter = shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); - float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); + float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*gammaCorrectRead(float3(1.0,0.4,0.0)), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); // fresnel float ior = (cameraPos.y>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air @@ -251,7 +252,7 @@ fresnel = shSaturate(fresnel); // reflection - float3 reflection = shSample(reflectionMap, screenCoords+(normal.xz*REFL_BUMP)).rgb; + float3 reflection = gammaCorrectRead(shSample(reflectionMap, screenCoords+(normal.xz*REFL_BUMP)).rgb); // refraction float3 R = reflect(vVec, normal); @@ -262,8 +263,7 @@ float refractDepth = shSample(depthMap, screenCoords-(shoreFade * normal.xz*REFR_BUMP)).x * far - depthPassthrough; float doRefraction = (refractDepth < 0) ? 0.f : 1.f; - float3 refraction = float3(0,0,0); - refraction.rgb = shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP * doRefraction))*1.0).rgb; + float3 refraction = gammaCorrectRead(shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP * doRefraction))*1.0).rgb); // brighten up the refraction underwater refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; @@ -281,12 +281,12 @@ { float waterSunGradient = dot(-vVec, -lVec); waterSunGradient = shSaturate(pow(waterSunGradient*0.7+0.3,2.0)); - float3 waterSunColour = float3(0.0,1.0,0.85)*waterSunGradient * 0.5; + float3 waterSunColour = gammaCorrectRead(float3(0.0,1.0,0.85))*waterSunGradient * 0.5; float waterGradient = dot(-vVec, float3(0.0,-1.0,0.0)); waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); - float3 watercolour = (float3(0.0078, 0.5176, 0.700)+waterSunColour)*waterGradient*2.0; - float3 waterext = float3(0.6, 0.9, 1.0);//water extinction + float3 watercolour = (gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; + float3 waterext = gammaCorrectRead(float3(0.6, 0.9, 1.0));//water extinction watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); float darkness = VISIBILITY*2.0; @@ -299,9 +299,11 @@ else { float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w); - shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue); + shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColor), fogValue); } + shOutputColour(0).xyz = gammaCorrectOutput(shOutputColour(0).xyz); + shOutputColour(0).w = 1; } From 9f2668b45bdb919ee3826043da4c84eae3d1596a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Sep 2012 21:35:57 +0200 Subject: [PATCH 668/688] Task #400 Review GMST access --- apps/openmw/mwclass/apparatus.cpp | 8 +++----- apps/openmw/mwclass/armor.cpp | 22 ++++++++++------------ apps/openmw/mwclass/book.cpp | 4 ++-- apps/openmw/mwclass/clothing.cpp | 6 ++---- apps/openmw/mwclass/container.cpp | 6 ++---- apps/openmw/mwclass/door.cpp | 6 +++--- apps/openmw/mwclass/ingredient.cpp | 6 ++---- apps/openmw/mwclass/light.cpp | 6 ++---- apps/openmw/mwclass/lockpick.cpp | 10 ++++------ apps/openmw/mwclass/misc.cpp | 12 ++++++------ apps/openmw/mwclass/potion.cpp | 4 ++-- apps/openmw/mwclass/probe.cpp | 10 ++++------ apps/openmw/mwclass/repair.cpp | 9 ++++----- apps/openmw/mwclass/weapon.cpp | 20 ++++++++++---------- apps/openmw/mwdialogue/journalimp.cpp | 2 +- apps/openmw/mwgui/container.cpp | 13 ++++++------- apps/openmw/mwgui/countdialog.cpp | 2 +- apps/openmw/mwgui/dialogue.cpp | 10 +++++----- apps/openmw/mwgui/spellbuyingwindow.cpp | 7 +++---- apps/openmw/mwgui/stats_window.cpp | 2 +- apps/openmw/mwgui/tooltips.cpp | 12 ++++++------ apps/openmw/mwgui/tradewindow.cpp | 18 ++++++++---------- apps/openmw/mwmechanics/npcstats.cpp | 8 ++++---- apps/openmw/mwrender/player.cpp | 1 + apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 26 files changed, 94 insertions(+), 114 deletions(-) diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 3d362e8c8..7c20eadec 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -128,12 +128,10 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; - text += "\n" + store.gameSettings.search("sQuality")->str + ": " + MWGui::ToolTips::toString(ref->base->data.quality); - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->base->data.quality); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index f1400dc01..93d481e93 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -152,13 +152,13 @@ namespace MWClass if (typeGmst.empty()) return -1; - float iWeight = MWBase::Environment::get().getWorld()->getStore().gameSettings.find (typeGmst)->i; + float iWeight = MWBase::Environment::get().getWorld()->getStore().gameSettings.find (typeGmst)->getInt(); - if (iWeight * MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fLightMaxMod")->f>= + if (iWeight * MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fLightMaxMod")->getFloat()>= ref->base->data.weight) return ESM::Skill::LightArmor; - if (iWeight * MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMedMaxMod")->f>= + if (iWeight * MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMedMaxMod")->getFloat()>= ref->base->data.weight) return ESM::Skill::MediumArmor; @@ -229,25 +229,23 @@ namespace MWClass std::string text; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - // get armor type string (light/medium/heavy) int armorType = getEquipmentSkill(ptr); std::string typeText; if (armorType == ESM::Skill::LightArmor) - typeText = store.gameSettings.search("sLight")->str; + typeText = "#{sLight}"; else if (armorType == ESM::Skill::MediumArmor) - typeText = store.gameSettings.search("sMedium")->str; + typeText = "#{sMedium}"; else - typeText = store.gameSettings.search("sHeavy")->str; + typeText = "#{sHeavy}"; - text += "\n" + store.gameSettings.search("sArmorRating")->str + ": " + MWGui::ToolTips::toString(ref->base->data.armor); + text += "\n#{sArmorRating}: " + MWGui::ToolTips::toString(ref->base->data.armor); /// \todo store the current armor health somewhere - text += "\n" + store.gameSettings.search("sCondition")->str + ": " + MWGui::ToolTips::toString(ref->base->data.health); + text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->base->data.health); - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight) + " (" + typeText + ")"; - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight) + " (" + typeText + ")"; + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 1e187342d..bb29e79fe 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -128,8 +128,8 @@ namespace MWClass std::string text; - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 21069e667..3dee0df89 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -193,12 +193,10 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 1164873a0..be88dd631 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -185,13 +185,11 @@ namespace MWClass MWGui::ToolTipInfo info; info.caption = ref->base->name; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; if (ref->ref.lockLevel > 0) - text += "\n" + store.gameSettings.search("sLockLevel")->str + ": " + MWGui::ToolTips::toString(ref->ref.lockLevel); + text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->ref.lockLevel); if (ref->ref.trap != "") - text += "\n" + store.gameSettings.search("sTrapped")->str; + text += "\n#{sTrapped}"; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 96c6eba5f..ffa901fe4 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -223,14 +223,14 @@ namespace MWClass dest = region->name; } } - text += "\n" + store.gameSettings.search("sTo")->str; + text += "\n#{sTo}"; text += "\n"+dest; } if (ref->ref.lockLevel > 0) - text += "\n" + store.gameSettings.search("sLockLevel")->str + ": " + MWGui::ToolTips::toString(ref->ref.lockLevel); + text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->ref.lockLevel); if (ref->ref.trap != "") - text += "\n" + store.gameSettings.search("sTrapped")->str; + text += "\n#{sTrapped}"; if (MWBase::Environment::get().getWindowManager()->getFullHelp()) text += MWGui::ToolTips::getMiscString(ref->base->script, "Script"); diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bdeb0e82b..5d621caf7 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -145,12 +145,10 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 570221503..560bdd71d 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -173,12 +173,10 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 44498e479..d4c9a01d0 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -137,16 +137,14 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; /// \todo store remaining uses somewhere - text += "\n" + store.gameSettings.search("sUses")->str + ": " + MWGui::ToolTips::toString(ref->base->data.uses); - text += "\n" + store.gameSettings.search("sQuality")->str + ": " + MWGui::ToolTips::toString(ref->base->data.quality); - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->base->data.uses); + text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->base->data.quality); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index eb44b8103..a2d868a6e 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -101,7 +101,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sGold")->getString()) { return std::string("Item Gold Up"); } @@ -113,7 +113,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + if (ref->base->name == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sGold")->getString()) { return std::string("Item Gold Down"); } @@ -147,7 +147,7 @@ namespace MWClass int count = ptr.getRefData().getCount(); - bool isGold = (ref->base->name == store.gameSettings.search("sGold")->str); + bool isGold = (ref->base->name == store.gameSettings.find("sGold")->getString()); if (isGold && count == 1) count = ref->base->data.value; @@ -170,8 +170,8 @@ namespace MWClass if (!isGold) { - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); } if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { @@ -192,7 +192,7 @@ namespace MWClass const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - if (MWWorld::Class::get(ptr).getName(ptr) == store.gameSettings.search("sGold")->str) { + if (MWWorld::Class::get(ptr).getName(ptr) == store.gameSettings.find("sGold")->getString()) { int goldAmount = ptr.getRefData().getCount(); std::string base = "Gold_001"; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 0b79e11ca..60167742c 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -133,8 +133,8 @@ namespace MWClass std::string text; - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->base->effects); info.isPotion = true; diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 73258e528..15ef2c1ad 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -136,16 +136,14 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; /// \todo store remaining uses somewhere - text += "\n" + store.gameSettings.search("sUses")->str + ": " + MWGui::ToolTips::toString(ref->base->data.uses); - text += "\n" + store.gameSettings.search("sQuality")->str + ": " + MWGui::ToolTips::toString(ref->base->data.quality); - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->base->data.uses); + text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->base->data.quality); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index a4240d0c4..7de3b3476 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -130,11 +130,10 @@ namespace MWClass /// \todo store remaining uses somewhere - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - text += "\n" + store.gameSettings.search("sUses")->str + ": " + MWGui::ToolTips::toString(ref->base->data.uses); - text += "\n" + store.gameSettings.search("sQuality")->str + ": " + MWGui::ToolTips::toString(ref->base->data.quality); - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sUses}: " + MWGui::ToolTips::toString(ref->base->data.uses); + text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->base->data.quality); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { text += MWGui::ToolTips::getMiscString(ref->ref.owner, "Owner"); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index b2397f4af..a0ec16369 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -284,7 +284,7 @@ namespace MWClass // weapon type & damage. arrows / bolts don't have his info. if (ref->base->data.type < 12) { - text += "\n" + store.gameSettings.search("sType")->str + " "; + text += "\n#{sType} "; std::map > mapping; mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); @@ -303,29 +303,29 @@ namespace MWClass std::string type = mapping[ref->base->data.type].first; std::string oneOrTwoHanded = mapping[ref->base->data.type].second; - text += store.gameSettings.search(type)->str + - ((oneOrTwoHanded != "") ? ", " + store.gameSettings.search(oneOrTwoHanded)->str : ""); + text += store.gameSettings.find(type)->getString() + + ((oneOrTwoHanded != "") ? ", " + store.gameSettings.find(oneOrTwoHanded)->getString() : ""); // weapon damage if (ref->base->data.type >= 9) { // marksman - text += "\n" + store.gameSettings.search("sAttack")->str + ": " + text += "\n#{sAttack}: " + MWGui::ToolTips::toString(static_cast(ref->base->data.chop[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->base->data.chop[1])); } else { // Chop - text += "\n" + store.gameSettings.search("sChop")->str + ": " + text += "\n#{sChop}: " + MWGui::ToolTips::toString(static_cast(ref->base->data.chop[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->base->data.chop[1])); // Slash - text += "\n" + store.gameSettings.search("sSlash")->str + ": " + text += "\n#{sSlash}: " + MWGui::ToolTips::toString(static_cast(ref->base->data.slash[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->base->data.slash[1])); // Thrust - text += "\n" + store.gameSettings.search("sThrust")->str + ": " + text += "\n#{sThrust}: " + MWGui::ToolTips::toString(static_cast(ref->base->data.thrust[0])) + " - " + MWGui::ToolTips::toString(static_cast(ref->base->data.thrust[1])); } @@ -333,10 +333,10 @@ namespace MWClass /// \todo store the current weapon health somewhere if (ref->base->data.type < 11) // thrown weapons and arrows/bolts don't have health, only quantity - text += "\n" + store.gameSettings.search("sCondition")->str + ": " + MWGui::ToolTips::toString(ref->base->data.health); + text += "\n#{sCondition}: " + MWGui::ToolTips::toString(ref->base->data.health); - text += "\n" + store.gameSettings.search("sWeight")->str + ": " + MWGui::ToolTips::toString(ref->base->data.weight); - text += MWGui::ToolTips::getValueString(ref->base->data.value, store.gameSettings.search("sValue")->str); + text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); + text += MWGui::ToolTips::getValueString(ref->base->data.value, "#{sValue}"); info.enchant = ref->base->enchant; diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index d626cd315..ac4b7a6de 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -40,7 +40,7 @@ namespace MWDialogue quest.addEntry (entry); // we are doing slicing on purpose here std::vector empty; - std::string notification = MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sJournalEntry")->str; + std::string notification = "#{sJournalEntry}"; MWBase::Environment::get().getWindowManager()->messageBox (notification, empty); } diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 93ec71343..5bf17fdb9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -110,7 +110,7 @@ void ContainerBase::onSelectedItem(MyGUI::Widget* _sender) } else { - std::string message = MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sTake")->str; + std::string message = "#{sTake}"; CountDialog* dialog = MWBase::Environment::get().getWindowManager()->getCountDialog(); dialog->open(MWWorld::Class::get(object).getName(object), message, count); dialog->eventOkClicked.clear(); @@ -130,18 +130,17 @@ void ContainerBase::onSelectedItem(MyGUI::Widget* _sender) // the player is trying to sell an item, check if the merchant accepts it // also, don't allow selling gold (let's be better than Morrowind at this, can we?) if (!MWBase::Environment::get().getWindowManager()->getTradeWindow()->npcAcceptsItem(object) - || MWWorld::Class::get(object).getName(object) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + || MWWorld::Class::get(object).getName(object) == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sGold")->getString()) { // user notification "i don't buy this item" MWBase::Environment::get().getWindowManager()-> - messageBox(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarterDialog4")->str, std::vector()); + messageBox("#{sBarterDialog4}", std::vector()); return; } } bool buying = isTradeWindow(); // buying or selling? - std::string message = buying ? MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sQuanityMenuMessage02")->str - : MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sQuanityMenuMessage01")->str; + std::string message = buying ? "#{sQuanityMenuMessage02}" : "#{sQuanityMenuMessage01}"; if (std::find(mBoughtItems.begin(), mBoughtItems.end(), object) != mBoughtItems.end()) { @@ -279,7 +278,7 @@ void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sContentsMessage2")->str, std::vector()); + messageBox("#{sContentsMessage2}", std::vector()); return; } } @@ -302,7 +301,7 @@ void ContainerBase::onContainerClicked(MyGUI::Widget* _sender) object.getRefData().setCount(origCount); // user notification MWBase::Environment::get().getWindowManager()-> - messageBox(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sContentsMessage3")->str, std::vector()); + messageBox("#{sContentsMessage3}", std::vector()); return; } else diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index baf14e3fb..61c3c358a 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -27,7 +27,7 @@ namespace MWGui { setVisible(true); - mLabelText->setCaption(message); + mLabelText->setCaptionWithReplacing(message); MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index a01da7f9f..245487a04 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -123,13 +123,13 @@ void DialogueWindow::onSelectTopic(std::string topic) { if (!mEnabled) return; - if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarter")->str) + if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sBarter")->getString()) { /// \todo check if the player is allowed to trade with this actor (e.g. faction rank high enough)? mWindowManager.pushGuiMode(GM_Barter); mWindowManager.getTradeWindow()->startTrade(mPtr); } - else if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sSpells")->str) + else if (topic == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sSpells")->getString()) { mWindowManager.pushGuiMode(GM_SpellBuying); mWindowManager.getSpellBuyingWindow()->startSpellBuying(mPtr); @@ -158,10 +158,10 @@ void DialogueWindow::setKeywords(std::list keyWords) bool anyService = mShowTrade||mShowSpells; if (mShowTrade) - mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarter")->str); + mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sBarter")->getString()); if (mShowSpells) - mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sSpells")->str); + mTopicsList->addItem(MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sSpells")->getString()); if (anyService) mTopicsList->addSeparator(); @@ -266,7 +266,7 @@ void DialogueWindow::updateOptions() void DialogueWindow::goodbye() { - mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGoodbye")->str); + mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sGoodbye")->getString()); mTopicsList->setEnabled(false); mEnabled = false; } diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index a41869e32..5306d94fe 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -51,12 +51,12 @@ namespace MWGui void SpellBuyingWindow::addSpell(const std::string& spellId) { const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().spells.find(spellId); - int price = spell->data.cost*MWBase::Environment::get().getWorld()->getStore().gameSettings.search("fSpellValueMult")->f; + int price = spell->data.cost*MWBase::Environment::get().getWorld()->getStore().gameSettings.find("fSpellValueMult")->getFloat(); MyGUI::Button* toAdd = mSpellsView->createWidget((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SpellText", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default); mCurrentY += sLineHeight; /// \todo price adjustment depending on merchantile skill toAdd->setUserData(price); - toAdd->setCaption(spell->name+" - "+boost::lexical_cast(price)+MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sgp")->str); + toAdd->setCaptionWithReplacing(spell->name+" - "+boost::lexical_cast(price)+"#{sgp}"); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel); toAdd->setUserString("ToolTipType", "Spell"); @@ -127,8 +127,7 @@ namespace MWGui void SpellBuyingWindow::updateLabels() { - mPlayerGold->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str - + ": " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); mPlayerGold->setCoord(8, mPlayerGold->getTop(), mPlayerGold->getTextSize().width, diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 701c39b0d..3d394859f 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -57,7 +57,7 @@ StatsWindow::StatsWindow (MWBase::WindowManager& parWindowManager) const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); for (int i=0; names[i][0]; ++i) { - setText (names[i][0], store.gameSettings.find (names[i][1])->str); + setText (names[i][0], store.gameSettings.find (names[i][1])->getString()); } getWidget(mSkillView, "SkillView"); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 8186279d1..c50f544a5 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -374,13 +374,13 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) { enchant = store.enchants.search(info.enchant); if (enchant->data.type == ESM::Enchantment::CastOnce) - text += "\n" + store.gameSettings.search("sItemCastOnce")->str; + text += "\n#{sItemCastOnce}"; else if (enchant->data.type == ESM::Enchantment::WhenStrikes) - text += "\n" + store.gameSettings.search("sItemCastWhenStrikes")->str; + text += "\n#{sItemCastWhenStrikes}"; else if (enchant->data.type == ESM::Enchantment::WhenUsed) - text += "\n" + store.gameSettings.search("sItemCastWhenUsed")->str; + text += "\n#{sItemCastWhenUsed}"; else if (enchant->data.type == ESM::Enchantment::ConstantEffect) - text += "\n" + store.gameSettings.search("sItemCastConstant")->str; + text += "\n#{sItemCastConstant}"; } // this the maximum width of the tooltip before it starts word-wrapping @@ -405,7 +405,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) textWidget->setProperty("Static", "true"); textWidget->setProperty("MultiLine", "true"); textWidget->setProperty("WordWrap", "true"); - textWidget->setCaption(text); + textWidget->setCaptionWithReplacing(text); textWidget->setTextAlign(Align::HCenter | Align::Top); IntSize textSize = textWidget->getTextSize(); @@ -466,7 +466,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) const int chargeWidth = 204; TextBox* chargeText = enchantArea->createWidget("SandText", IntCoord(0, 0, 10, 18), Align::Default, "ToolTipEnchantChargeText"); - chargeText->setCaption(store.gameSettings.search("sCharges")->str); + chargeText->setCaptionWithReplacing("#{sCharges}"); const int chargeTextWidth = chargeText->getTextSize().width + 5; const int chargeAndTextWidth = chargeWidth + chargeTextWidth; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 27a24c22c..fc4220fc3 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -110,7 +110,7 @@ namespace MWGui for (MWWorld::ContainerStoreIterator it = playerStore.begin(); it != playerStore.end(); ++it) { - if (MWWorld::Class::get(*it).getName(*it) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + if (MWWorld::Class::get(*it).getName(*it) == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sGold")->getString()) { goldFound = true; gold = *it; @@ -138,7 +138,7 @@ namespace MWGui { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarterDialog11")->str, std::vector()); + messageBox("#{sBarterDialog11}", std::vector()); return; } @@ -147,7 +147,7 @@ namespace MWGui { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarterDialog1")->str, std::vector()); + messageBox("#{sBarterDialog1}", std::vector()); return; } @@ -170,7 +170,7 @@ namespace MWGui { // user notification MWBase::Environment::get().getWindowManager()-> - messageBox(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sBarterDialog2")->str, std::vector()); + messageBox("#{sBarterDialog2}", std::vector()); return; } @@ -200,17 +200,16 @@ namespace MWGui void TradeWindow::updateLabels() { - mPlayerGold->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sYourGold")->str - + " " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); + mPlayerGold->setCaptionWithReplacing("#{sYourGold} " + boost::lexical_cast(mWindowManager.getInventoryWindow()->getPlayerGold())); if (mCurrentBalance > 0) { - mTotalBalanceLabel->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sTotalSold")->str); + mTotalBalanceLabel->setCaptionWithReplacing("#{sTotalSold}"); mTotalBalance->setCaption(boost::lexical_cast(mCurrentBalance)); } else { - mTotalBalanceLabel->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sTotalCost")->str); + mTotalBalanceLabel->setCaptionWithReplacing("#{sTotalCost}"); mTotalBalance->setCaption(boost::lexical_cast(-mCurrentBalance)); } @@ -229,8 +228,7 @@ namespace MWGui merchantgold = ref->base->data.gold; } - mMerchantGold->setCaption(MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sSellerGold")->str - + " " + boost::lexical_cast(merchantgold)); + mMerchantGold->setCaptionWithReplacing("#{sSellerGold} " + boost::lexical_cast(merchantgold)); } std::vector TradeWindow::getEquippedItems() diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 5e3896af2..39a5d6803 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -97,13 +97,13 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla } float typeFactor = - MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMiscSkillBonus")->f; + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMiscSkillBonus")->getFloat(); for (int i=0; i<5; ++i) if (class_.data.skills[i][0]==skillIndex) { typeFactor = - MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMinorSkillBonus")->f; + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMinorSkillBonus")->getFloat(); break; } @@ -112,7 +112,7 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla if (class_.data.skills[i][1]==skillIndex) { typeFactor = - MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMajorSkillBonus")->f; + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fMajorSkillBonus")->getFloat(); break; } @@ -125,7 +125,7 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla if (skill->data.specialization==class_.data.specialization) { specialisationFactor = - MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fSpecialSkillBonus")->f; + MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fSpecialSkillBonus")->getFloat(); if (specialisationFactor<=0) throw std::runtime_error ("invalid skill specialisation factor"); diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 297cb8d7a..8c8bbb86f 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -323,6 +323,7 @@ namespace MWRender bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) { float xch; + mCamera->getParentSceneNode ()->needUpdate(true); camera = mCamera->getRealPosition(); xch = camera.z, camera.z = camera.y, camera.y = -xch; player = mPlayerNode->getPosition(); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 1d473246c..ed7336e97 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -76,7 +76,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) // gold needs special handling: when it is inserted into a container, the base object automatically becomes Gold_001 // this ensures that gold piles of different sizes stack with each other (also, several scripts rely on Gold_001 for detecting player gold) - if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) + if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.find("sGold")->getString()) { MWWorld::LiveCellRef *gold = ptr.get(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e9b6e6d12..834dffe79 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -526,7 +526,7 @@ namespace MWWorld std::pair result = mPhysics->getFacedHandle (*this); if (result.first.empty() || - result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) + result.second>getStore().gameSettings.find ("iMaxActivateDist")->getInt()) return ""; return result.first; From b4db339fd55d5e9d25fe4ae40f9eb6f4bf9b5114 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Sep 2012 21:39:05 +0200 Subject: [PATCH 669/688] everything uses find now --- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e6f9e8565..5a04a90c0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -522,7 +522,7 @@ int WindowManager::readPressedButton () const std::string &WindowManager::getGameSettingString(const std::string &id, const std::string &default_) { - const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().gameSettings.search(id); + const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().gameSettings.find(id); if (setting && setting->type == ESM::VT_String) return setting->str; return default_; @@ -680,7 +680,7 @@ void WindowManager::setDragDrop(bool dragDrop) void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) { - const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().gameSettings.search(_tag); + const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().gameSettings.find(_tag); if (setting && setting->type == ESM::VT_String) _result = setting->str; else From bf3aad8be049351ac7599dd3495e1f99a7b598f7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 23 Sep 2012 18:30:13 +0200 Subject: [PATCH 670/688] handle NCC flag --- components/nifbullet/bullet_nif_loader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index a70eb7629..6449ad246 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -193,7 +193,8 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // affecting the entire subtree of this node Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; - if (sd->string == "NCO") + // not sure what the difference between NCO and NCC is, or if there even is one + if (sd->string == "NCO" || sd->string == "NCC") { // No collision. Use an internal flag setting to mark this. flags |= 0x800; From 1cb1259e18b5ffa525489aa1d18e3ad4982e18e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 23 Sep 2012 19:36:37 +0200 Subject: [PATCH 671/688] fixing some unrelated warnings --- apps/openmw/mwclass/book.cpp | 2 -- apps/openmw/mwclass/potion.cpp | 2 -- apps/openmw/mwgui/container.cpp | 2 +- apps/openmw/mwgui/stats_window.cpp | 2 -- apps/openmw/mwgui/tooltips.cpp | 3 ++- libs/openengine/bullet/physic.cpp | 9 ++------- libs/openengine/bullet/pmove.cpp | 6 +++--- libs/openengine/bullet/trace.cpp | 4 ++-- 8 files changed, 10 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index bb29e79fe..3653ad901 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -124,8 +124,6 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 60167742c..259406b37 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -129,8 +129,6 @@ namespace MWClass info.caption = ref->base->name + MWGui::ToolTips::getCountString(ptr.getRefData().getCount()); info.icon = ref->base->icon; - const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - std::string text; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->base->data.weight); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 5bf17fdb9..e20cf5914 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -362,7 +362,7 @@ void ContainerBase::drawItems() int maxHeight = mItemView->getSize().height - 58; bool onlyMagic = false; - int categories; + int categories = 0; if (mFilter == Filter_All) categories = MWWorld::ContainerStore::Type_All; else if (mFilter == Filter_Weapon) diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 3d394859f..9a23082e6 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -475,8 +475,6 @@ void StatsWindow::updateSkillArea() text += "\n#BF9959"; for (int i=0; i<6; ++i) { - const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().skills.search(faction->data.skillID[i]); - assert(skill); text += "#{"+ESM::Skill::sSkillNameIds[faction->data.skillID[i]]+"}"; if (i<5) text += ", "; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index c50f544a5..b6aea583b 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -368,7 +368,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) if (text.size() > 0 && text[0] == '\n') text.erase(0, 1); - const ESM::Enchantment* enchant; + const ESM::Enchantment* enchant = 0; const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); if (info.enchant != "") { @@ -440,6 +440,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) if (info.enchant != "") { + assert(enchant); Widget* enchantArea = mDynamicToolTipBox->createWidget("", IntCoord(0, totalSize.height, 300, 300-totalSize.height), Align::Stretch, "ToolTipEnchantArea"); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 74352b358..b42ffb84c 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -303,16 +303,11 @@ namespace Physic + boost::lexical_cast(y); // find the minimum and maximum heights (needed for bullet) - float minh; - float maxh; + float minh = heights[0]; + float maxh = heights[0]; for (int i=0; imaxh) maxh = h; if (h Date: Mon, 24 Sep 2012 00:42:05 +0200 Subject: [PATCH 672/688] fix the race selection escape crash --- apps/openmw/mwgui/race.cpp | 19 +++++++++++++------ apps/openmw/mwgui/race.hpp | 3 ++- apps/openmw/mwrender/characterpreview.cpp | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 3fe70d959..99cb5f3b2 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -33,11 +33,6 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); getWidget(mPreviewImage, "PreviewImage"); - MWBase::Environment::get().getWorld ()->setupExternalRendering (mPreview); - mPreview.update (0); - - mPreviewImage->setImageTexture ("CharacterHeadPreview"); - getWidget(mHeadRotate, "HeadRotate"); mHeadRotate->setScrollRange(50); mHeadRotate->setScrollPosition(20); @@ -107,6 +102,12 @@ void RaceDialog::open() updateRaces(); updateSkills(); updateSpellPowers(); + + mPreview = new MWRender::RaceSelectionPreview(); + MWBase::Environment::get().getWorld ()->setupExternalRendering (*mPreview); + mPreview->update (0); + + mPreviewImage->setImageTexture ("CharacterHeadPreview"); } @@ -138,6 +139,12 @@ int wrap(int index, int max) return index; } +void RaceDialog::close() +{ + delete mPreview; + mPreview = 0; +} + // widget controls void RaceDialog::onOkClicked(MyGUI::Widget* _sender) @@ -154,7 +161,7 @@ void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) { float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; float diff = angle - mCurrentAngle; - mPreview.update (diff); + mPreview->update (diff); mCurrentAngle += diff; } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 0505e58e1..c4734eae8 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -46,6 +46,7 @@ namespace MWGui void setNextButtonShow(bool shown); virtual void open(); + virtual void close(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; @@ -94,7 +95,7 @@ namespace MWGui float mCurrentAngle; - MWRender::RaceSelectionPreview mPreview; + MWRender::RaceSelectionPreview* mPreview; }; } #endif diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c8852bff5..4a0554568 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -25,6 +25,7 @@ namespace MWRender , mPosition(position) , mLookAt(lookAt) , mCharacter(character) + , mAnimation(NULL) { } From f8a18d58a628584b7bd9b180d6cd00a615c5086c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 24 Sep 2012 01:06:17 +0200 Subject: [PATCH 673/688] re-use the texture across preview instances --- apps/openmw/mwrender/characterpreview.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 4a0554568..175f48e42 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -54,10 +54,13 @@ namespace MWRender mCamera->setNearClipDistance (0.01); mCamera->setFarClipDistance (1000); - mTexture = Ogre::TextureManager::getSingleton().createManual(mName, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); + mTexture = Ogre::TextureManager::getSingleton().getByName (mName); + if (mTexture.isNull ()) + mTexture = Ogre::TextureManager::getSingleton().createManual(mName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + mRenderTarget->removeAllViewports (); mViewport = mRenderTarget->addViewport(mCamera); mViewport->setOverlaysEnabled(false); mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); From 02e7b3df4fb4a3574c0bb9853c9319003106a5c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 24 Sep 2012 01:07:33 +0200 Subject: [PATCH 674/688] oops --- apps/openmw/mwrender/characterpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 175f48e42..f755f34ef 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -75,7 +75,7 @@ namespace MWRender CharacterPreview::~CharacterPreview () { - Ogre::TextureManager::getSingleton().remove(mName); + //Ogre::TextureManager::getSingleton().remove(mName); mSceneMgr->destroyCamera (mName); delete mAnimation; } From a34bdb962355f2d2938eea141339cbe2ef94629c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Sep 2012 10:48:57 +0200 Subject: [PATCH 675/688] per NPC ID interaction tracking --- apps/openmw/mwmechanics/npcstats.cpp | 10 ++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 39a5d6803..5367d3110 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -227,3 +227,13 @@ int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const else return 5; } + +void MWMechanics::NpcStats::flagAsUsed (const std::string& id) +{ + mUsedIds.insert (id); +} + +bool MWMechanics::NpcStats::hasBeenUsed (const std::string& id) const +{ + return mUsedIds.find (id)!=mUsedIds.end(); +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 7c3055783..48e63d7b6 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -50,6 +50,8 @@ namespace MWMechanics std::vector mSkillIncreases; // number of skill increases for each attribute + std::set mUsedIds; + public: NpcStats(); @@ -86,6 +88,10 @@ namespace MWMechanics int getLevelupAttributeMultiplier(int attribute) const; void levelUp(); + + void flagAsUsed (const std::string& id); + + bool hasBeenUsed (const std::string& id) const; }; } From 853d62adba94b292cd1bbcff5df76ec9dc813064 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 18:59:24 +0200 Subject: [PATCH 676/688] book skillgain --- apps/openmw/mwworld/actionread.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index fe5e2d58f..59c1e722f 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -35,22 +35,21 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->getBookWindow()->open(getTarget()); } - /* + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); + // Skill gain from books - if (ref->base->data.skillID >= 0 && ref->base->data.skillID < ESM::Skill::Length) + if (ref->base->data.skillID >= 0 && ref->base->data.skillID < ESM::Skill::Length + && !npcStats.hasBeenUsed (ref->base->name)) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); - MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); MWWorld::LiveCellRef *playerRef = player.get(); const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().classes.find ( playerRef->base->cls); npcStats.increaseSkill (ref->base->data.skillID, *class_, true); - /// \todo Remove skill from the book. Right now you can read as many times as you want - /// and the skill will still increase. + npcStats.flagAsUsed (ref->base->name); } - */ } } From d163f8203c17355d5ce80fe895582a54a735c757 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 21:38:34 +0200 Subject: [PATCH 677/688] change books to RecListWithIDT --- apps/esmtool/esmtool.cpp | 2 +- apps/openmw/mwworld/actionread.cpp | 4 ++-- components/esm/loadbook.cpp | 3 ++- components/esm/loadbook.hpp | 3 ++- components/esm_store/store.hpp | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 4888d3ceb..ac2746a89 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -226,7 +226,7 @@ int main(int argc, char**argv) case REC_BOOK: { Book b; - b.load(esm); + b.load(esm, id); if(quiet) break; cout << " Name: " << b.name << endl; cout << " Mesh: " << b.model << endl; diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 59c1e722f..ed118d9b8 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -40,7 +40,7 @@ namespace MWWorld // Skill gain from books if (ref->base->data.skillID >= 0 && ref->base->data.skillID < ESM::Skill::Length - && !npcStats.hasBeenUsed (ref->base->name)) + && !npcStats.hasBeenUsed (ref->base->id)) { MWWorld::LiveCellRef *playerRef = player.get(); const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().classes.find ( @@ -48,7 +48,7 @@ namespace MWWorld npcStats.increaseSkill (ref->base->data.skillID, *class_, true); - npcStats.flagAsUsed (ref->base->name); + npcStats.flagAsUsed (ref->base->id); } } diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index ffa958e14..48d9aed0d 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -3,7 +3,7 @@ namespace ESM { -void Book::load(ESMReader &esm) +void Book::load(ESMReader &esm, const std::string& recordId) { model = esm.getHNString("MODL"); name = esm.getHNOString("FNAM"); @@ -12,6 +12,7 @@ void Book::load(ESMReader &esm) icon = esm.getHNOString("ITEX"); text = esm.getHNOString("TEXT"); enchant = esm.getHNOString("ENAM"); + id = recordId; } } diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 3a4ab441e..525c21d33 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -20,8 +20,9 @@ struct Book BKDTstruct data; std::string name, model, icon, script, enchant, text; + std::string id; - void load(ESMReader &esm); + void load(ESMReader &esm, const std::string& recordId); }; } #endif diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp index 991925bd4..7329386d4 100644 --- a/components/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -34,7 +34,7 @@ namespace ESMS RecListT appas; RecListT armors; RecListT bodyParts; - RecListT books; + RecListWithIDT books; RecListT birthSigns; RecListT classes; RecListT clothes; From e2b13296202fc7e190610a6eb081a0c2ede84c08 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 24 Sep 2012 00:42:05 +0200 Subject: [PATCH 678/688] fix the race selection escape crash --- apps/openmw/mwgui/race.cpp | 19 +++++++++++++------ apps/openmw/mwgui/race.hpp | 3 ++- apps/openmw/mwrender/characterpreview.cpp | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 5cb73e682..4b74ee2c8 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -33,11 +33,6 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); getWidget(mPreviewImage, "PreviewImage"); - MWBase::Environment::get().getWorld ()->setupExternalRendering (mPreview); - mPreview.update (0); - - mPreviewImage->setImageTexture ("CharacterHeadPreview"); - getWidget(mHeadRotate, "HeadRotate"); mHeadRotate->setScrollRange(50); mHeadRotate->setScrollPosition(25); @@ -107,6 +102,12 @@ void RaceDialog::open() updateRaces(); updateSkills(); updateSpellPowers(); + + mPreview = new MWRender::RaceSelectionPreview(); + MWBase::Environment::get().getWorld ()->setupExternalRendering (*mPreview); + mPreview->update (0); + + mPreviewImage->setImageTexture ("CharacterHeadPreview"); } @@ -138,6 +139,12 @@ int wrap(int index, int max) return index; } +void RaceDialog::close() +{ + delete mPreview; + mPreview = 0; +} + // widget controls void RaceDialog::onOkClicked(MyGUI::Widget* _sender) @@ -154,7 +161,7 @@ void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) { float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; float diff = angle - mCurrentAngle; - mPreview.update (diff); + mPreview->update (diff); mCurrentAngle += diff; } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 0505e58e1..c4734eae8 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -46,6 +46,7 @@ namespace MWGui void setNextButtonShow(bool shown); virtual void open(); + virtual void close(); // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; @@ -94,7 +95,7 @@ namespace MWGui float mCurrentAngle; - MWRender::RaceSelectionPreview mPreview; + MWRender::RaceSelectionPreview* mPreview; }; } #endif diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c8852bff5..4a0554568 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -25,6 +25,7 @@ namespace MWRender , mPosition(position) , mLookAt(lookAt) , mCharacter(character) + , mAnimation(NULL) { } From 44534593788136d6aa86943a65903561f1c4742f Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 24 Sep 2012 01:06:17 +0200 Subject: [PATCH 679/688] re-use the texture across preview instances --- apps/openmw/mwrender/characterpreview.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 4a0554568..175f48e42 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -54,10 +54,13 @@ namespace MWRender mCamera->setNearClipDistance (0.01); mCamera->setFarClipDistance (1000); - mTexture = Ogre::TextureManager::getSingleton().createManual(mName, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); + mTexture = Ogre::TextureManager::getSingleton().getByName (mName); + if (mTexture.isNull ()) + mTexture = Ogre::TextureManager::getSingleton().createManual(mName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + mRenderTarget->removeAllViewports (); mViewport = mRenderTarget->addViewport(mCamera); mViewport->setOverlaysEnabled(false); mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); From 359824d6142c40f4ed57aa90a93d7885a1248fe1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 24 Sep 2012 01:07:33 +0200 Subject: [PATCH 680/688] oops --- apps/openmw/mwrender/characterpreview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 175f48e42..f755f34ef 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -75,7 +75,7 @@ namespace MWRender CharacterPreview::~CharacterPreview () { - Ogre::TextureManager::getSingleton().remove(mName); + //Ogre::TextureManager::getSingleton().remove(mName); mSceneMgr->destroyCamera (mName); delete mAnimation; } From caef570c8a3f8629b4d221bc9a36fb2062c4a682 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 21:28:25 +0200 Subject: [PATCH 681/688] fix the weather not changing when sleeping/waiting --- apps/openmw/mwworld/weather.cpp | 10 +++++++--- apps/openmw/mwworld/weather.hpp | 7 +++++++ apps/openmw/mwworld/worldimp.cpp | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 0adf87dae..74c9cc9cd 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -41,7 +41,8 @@ const float WeatherGlobals::mThunderSoundDelay = 0.25; WeatherManager::WeatherManager(MWRender::RenderingManager* rendering) : mHour(14), mCurrentWeather("clear"), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), - mRemainingTransitionTime(0), mMonth(0), mDay(0) + mRemainingTransitionTime(0), mMonth(0), mDay(0), + mTimePassed(0) { mRendering = rendering; @@ -487,7 +488,10 @@ WeatherResult WeatherManager::transition(float factor) void WeatherManager::update(float duration) { - mWeatherUpdateTime -= duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + float timePassed = mTimePassed; + mTimePassed = 0; + + mWeatherUpdateTime -= timePassed; bool exterior = (MWBase::Environment::get().getWorld()->isCellExterior() || MWBase::Environment::get().getWorld()->isCellQuasiExterior()); @@ -558,7 +562,7 @@ void WeatherManager::update(float duration) if (mNextWeather != "") { - mRemainingTransitionTime -= duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + mRemainingTransitionTime -= timePassed; if (mRemainingTransitionTime < 0) { mCurrentWeather = mNextWeather; diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 5e0388751..589dff3eb 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -231,6 +231,11 @@ namespace MWWorld void setDate(const int day, const int month); + void advanceTime(double hours) + { + mTimePassed += hours*3600; + } + unsigned int getWeatherID() const; private: @@ -261,6 +266,8 @@ namespace MWWorld float mThunderChanceNeeded; float mThunderSoundDelay; + double mTimePassed; // time passed since last update + WeatherResult transition(const float factor); WeatherResult getResult(const Ogre::String& weather); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 834dffe79..38063b051 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -366,6 +366,8 @@ namespace MWWorld void World::advanceTime (double hours) { + mWeatherManager->advanceTime (hours); + hours += mGlobalVariables->getFloat ("gamehour"); setHour (hours); From 71d04e16da9d2d54591cdcbb620ff14043254a6d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 21:57:57 +0200 Subject: [PATCH 682/688] fix the global map updating when pinned --- apps/openmw/mwgui/map_window.cpp | 12 ++++++++++++ apps/openmw/mwgui/map_window.hpp | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 0fc4233b6..8b1de6258 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -224,6 +224,9 @@ void LocalMapBase::setPlayerPos(const float x, const float y) { if (x == mLastPositionX && y == mLastPositionY) return; + + notifyPlayerUpdate (); + MyGUI::IntSize size = mLocalMap->getCanvasSize(); MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height); MyGUI::IntCoord viewsize = mLocalMap->getCoord(); @@ -239,6 +242,9 @@ void LocalMapBase::setPlayerDir(const float x, const float y) { if (x == mLastDirectionX && y == mLastDirectionY) return; + + notifyPlayerUpdate (); + MyGUI::ISubWidget* main = mCompass->getSubWidgetMain(); MyGUI::RotatingSkin* rotatingSubskin = main->castType(); rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); @@ -407,3 +413,9 @@ void MapWindow::globalMapUpdatePlayer () MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); mGlobalMap->setViewOffset(viewoffs); } + +void MapWindow::notifyPlayerUpdate () +{ + if (mGlobal) + globalMapUpdatePlayer (); +} diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 042e6c8ec..73eaad1cd 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -49,6 +49,8 @@ namespace MWGui void onMarkerFocused(MyGUI::Widget* w1, MyGUI::Widget* w2); void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); + virtual void notifyPlayerUpdate() {} + OEngine::GUI::Layout* mLayout; bool mMapDragAndDrop; @@ -93,6 +95,8 @@ namespace MWGui protected: virtual void onPinToggled(); + + virtual void notifyPlayerUpdate(); }; } #endif From 9b0254a158716904d9d0963a9a29a3e52bc5d42c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Sep 2012 23:34:35 +0200 Subject: [PATCH 683/688] small map fix --- apps/openmw/mwgui/map_window.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 8b1de6258..1913ed8dd 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -406,16 +406,15 @@ void MapWindow::globalMapUpdatePlayer () rotatingSubskin->setCenter(MyGUI::IntPoint(16,16)); float angle = std::atan2(dir.x, dir.y); rotatingSubskin->setAngle(angle); - } - // set the view offset so that player is in the center - MyGUI::IntSize viewsize = mGlobalMap->getSize(); - MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); - mGlobalMap->setViewOffset(viewoffs); + // set the view offset so that player is in the center + MyGUI::IntSize viewsize = mGlobalMap->getSize(); + MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY); + mGlobalMap->setViewOffset(viewoffs); + } } void MapWindow::notifyPlayerUpdate () { - if (mGlobal) - globalMapUpdatePlayer (); + globalMapUpdatePlayer (); } From 044ab3d2b6d7715fdb059586036f1664fb225c07 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Sep 2012 17:02:18 +0200 Subject: [PATCH 684/688] fix error in framelistener with tcg enabled --- apps/openmw/mwrender/debugging.cpp | 5 +++++ apps/openmw/mwrender/renderconst.hpp | 4 ++-- libs/openengine/bullet/BtOgreExtras.h | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 91b217a36..71366307a 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -19,6 +19,7 @@ #include "../mwworld/ptr.hpp" #include "player.hpp" +#include "renderconst.hpp" using namespace Ogre; @@ -86,6 +87,8 @@ ManualObject *Debugging::createPathgridLines(const ESM::Pathgrid *pathgrid) } result->end(); + result->setVisibilityFlags (RV_Debug); + return result; } @@ -140,6 +143,8 @@ ManualObject *Debugging::createPathgridPoints(const ESM::Pathgrid *pathgrid) result->end(); + result->setVisibilityFlags (RV_Debug); + return result; } diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index e6ecb5150..75e243ec7 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -56,9 +56,9 @@ enum VisibilityFlags RV_PlayerPreview = 512, - RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water + RV_Debug = 1024, - /// \todo markers (normally hidden) + RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water }; } diff --git a/libs/openengine/bullet/BtOgreExtras.h b/libs/openengine/bullet/BtOgreExtras.h index f3e1aa87a..423924eda 100644 --- a/libs/openengine/bullet/BtOgreExtras.h +++ b/libs/openengine/bullet/BtOgreExtras.h @@ -206,6 +206,8 @@ public: } mLineDrawer->setMaterial("BtOgre/DebugLines"); + + mLineDrawer->setVisibilityFlags (1024); } ~DebugDrawer() From 9b491edbf81374663b09ecec6de8584ef1952aa2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 28 Sep 2012 18:50:07 -0700 Subject: [PATCH 685/688] Small cleanup for loading animation keyframes --- components/nifogre/ogre_nif_loader.cpp | 72 +++++++++++--------------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 5127af966..91432ed6a 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -274,23 +274,18 @@ void loadResource(Ogre::Resource *resource) if(scaleiter != scalekeys.mKeys.end()) lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale; bool didlast = false; - + while(!didlast) { float curtime = kfc->timeStop; - //Get latest time - if(quatiter != quatkeys.mKeys.end()){ + if(quatiter != quatkeys.mKeys.end()) curtime = std::min(curtime, quatiter->mTime); - } - if(traniter != trankeys.mKeys.end()){ + if(traniter != trankeys.mKeys.end()) curtime = std::min(curtime, traniter->mTime); - - } - if(scaleiter != scalekeys.mKeys.end()){ + if(scaleiter != scalekeys.mKeys.end()) curtime = std::min(curtime, scaleiter->mTime); - } curtime = std::max(curtime, kfc->timeStart); if(curtime >= kfc->timeStop) @@ -299,15 +294,33 @@ void loadResource(Ogre::Resource *resource) curtime = kfc->timeStop; } - bool rinterpolate = quatiter != quatkeys.mKeys.end() && quatiter != quatkeys.mKeys.begin() && curtime != quatiter->mTime; - bool tinterpolate = traniter != trankeys.mKeys.end() && traniter != trankeys.mKeys.begin() && curtime != traniter->mTime; - bool sinterpolate = scaleiter != scalekeys.mKeys.end() && scaleiter != scalekeys.mKeys.begin() && curtime != scaleiter->mTime; + // Get the latest quaternions, translations, and scales for the + // current time + while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) + { + lastquat = curquat; + quatiter++; + if(quatiter != quatkeys.mKeys.end()) + curquat = startquat.Inverse() * quatiter->mValue ; + } + while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) + { + lasttrans = curtrans; + traniter++; + if(traniter != trankeys.mKeys.end()) + curtrans = traniter->mValue - starttrans; + } + while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) + { + lastscale = curscale; + scaleiter++; + if(scaleiter != scalekeys.mKeys.end()) + curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + } - - Ogre::TransformKeyFrame *kframe; kframe = nodetrack->createNodeKeyFrame(curtime); - if(!rinterpolate) + if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) kframe->setRotation(curquat); else { @@ -315,7 +328,7 @@ void loadResource(Ogre::Resource *resource) float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); } - if(!tinterpolate) + if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) kframe->setTranslate(curtrans); else { @@ -323,7 +336,7 @@ void loadResource(Ogre::Resource *resource) float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); } - if(!sinterpolate) + if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) kframe->setScale(curscale); else { @@ -331,31 +344,6 @@ void loadResource(Ogre::Resource *resource) float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); kframe->setScale(lastscale + ((curscale-lastscale)*diff)); } - - // Get the latest quaternion, translation, and scale for the - // current time - while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) - { - quatiter++; - lastquat = curquat; - if(quatiter != quatkeys.mKeys.end()) - curquat = startquat.Inverse() * quatiter->mValue ; - } - while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) - { - traniter++; - lasttrans = curtrans; - if(traniter != trankeys.mKeys.end()) - curtrans = traniter->mValue - starttrans; - } - while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) - { - scaleiter++; - lastscale = curscale; - if(scaleiter != scalekeys.mKeys.end()) - curscale = Ogre::Vector3(scaleiter->mValue) / startscale; - } - } } anim->optimise(); From cd8515396adfa654574c30d80f89075efb76b7f5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 28 Sep 2012 21:57:26 -0700 Subject: [PATCH 686/688] Use a multimap to store the text keys --- apps/openmw/mwrender/animation.cpp | 53 +++++++++----------------- components/nifogre/ogre_nif_loader.cpp | 25 +++++++++++- components/nifogre/ogre_nif_loader.hpp | 2 +- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 3d7629e5b..f7a175332 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -49,44 +49,27 @@ bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTim std::string::const_iterator strpos = iter->second.begin(); std::string::const_iterator strend = iter->second.end(); + size_t strlen = strend-strpos; - while(strpos != strend) + if(start.size() <= strlen && std::mismatch(strpos, strend, start.begin(), checklow()).first == strend) { - size_t strlen = strend-strpos; - std::string::const_iterator striter; - - if(start.size() <= strlen && - ((striter=std::mismatch(strpos, strend, start.begin(), checklow()).first) == strend || - *striter == '\r' || *striter == '\n')) - { - times->mStart = iter->first; - times->mLoopStart = iter->first; - } - else if(startloop.size() <= strlen && - ((striter=std::mismatch(strpos, strend, startloop.begin(), checklow()).first) == strend || - *striter == '\r' || *striter == '\n')) - { - times->mLoopStart = iter->first; - } - else if(stoploop.size() <= strlen && - ((striter=std::mismatch(strpos, strend, stoploop.begin(), checklow()).first) == strend || - *striter == '\r' || *striter == '\n')) - { + times->mStart = iter->first; + times->mLoopStart = iter->first; + } + else if(startloop.size() <= strlen && std::mismatch(strpos, strend, startloop.begin(), checklow()).first == strend) + { + times->mLoopStart = iter->first; + } + else if(stoploop.size() <= strlen && std::mismatch(strpos, strend, stoploop.begin(), checklow()).first == strend) + { + times->mLoopStop = iter->first; + } + else if(stop.size() <= strlen && std::mismatch(strpos, strend, stop.begin(), checklow()).first == strend) + { + times->mStop = iter->first; + if(times->mLoopStop < 0.0f) times->mLoopStop = iter->first; - } - else if(stop.size() <= strlen && - ((striter=std::mismatch(strpos, strend, stop.begin(), checklow()).first) == strend || - *striter == '\r' || *striter == '\n')) - { - times->mStop = iter->first; - if(times->mLoopStop < 0.0f) - times->mLoopStop = iter->first; - break; - } - - strpos = std::find(strpos+1, strend, '\n'); - while(strpos != strend && *strpos == '\n') - strpos++; + break; } } diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 91432ed6a..ad51d50b9 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -152,6 +152,28 @@ static void fail(const std::string &msg) } +static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textkeys) +{ + for(size_t i = 0;i < tk->list.size();i++) + { + const std::string &str = tk->list[i].text; + std::string::size_type pos = 0; + while(pos < str.length()) + { + while(pos < str.length() && ::isspace(str[pos])) + pos++; + if(pos >= str.length()) + break; + + std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); + textkeys->insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); + + pos = nextpos; + } + } +} + + void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector &ctrls, Ogre::Bone *parent=NULL) { Ogre::Bone *bone; @@ -359,8 +381,7 @@ bool createSkeleton(const std::string &name, const std::string &group, TextKeyMa if(e->recType == Nif::RC_NiTextKeyExtraData) { const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - for(size_t i = 0;i < tk->list.size();i++) - (*textkeys)[tk->list[i].time] = tk->list[i].text; + insertTextKeys(tk, textkeys); } e = e->extra; } diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index b6610d8a7..a203112b5 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -59,7 +59,7 @@ namespace NifOgre { // FIXME: These should not be in NifOgre, it works agnostic of what model format is used -typedef std::map TextKeyMap; +typedef std::multimap TextKeyMap; struct EntityList { std::vector mEntities; Ogre::Entity *mSkelBase; From 0c6ad1336465dd571de5ab954f00b6d273eadf1d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 29 Sep 2012 09:41:34 +0200 Subject: [PATCH 687/688] WakeUpPc instruction --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/waitdialog.cpp | 9 ++++++++- apps/openmw/mwgui/waitdialog.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 1 + apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/miscextensions.cpp | 13 +++++++++++++ 7 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 429163136..8ea9e5cef 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -226,6 +226,7 @@ namespace MWBase virtual bool getRestEnabled() = 0; virtual bool getPlayerSleeping() = 0; + virtual void wakeUpPlayer() = 0; }; } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 3210f1db9..380fb8dd5 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -202,7 +202,7 @@ namespace MWGui { MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2); mProgressBar.setVisible (false); - mWindowManager.popGuiMode (); + mWindowManager.removeGuiMode (GM_Rest); mWaiting = false; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -215,4 +215,11 @@ namespace MWGui } } + void WaitDialog::wakeUp () + { + mSleeping = false; + mWaiting = false; + stopWaiting(); + } + } diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index 4a401c0c6..6af565c6e 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -32,6 +32,7 @@ namespace MWGui void bedActivated() { setCanRest(true); } bool getSleeping() { return mWaiting && mSleeping; } + void wakeUp(); protected: MyGUI::TextBox* mDateTimeText; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5a04a90c0..de8def126 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -956,6 +956,11 @@ bool WindowManager::getPlayerSleeping () return mWaitDialog->getSleeping(); } +void WindowManager::wakeUpPlayer() +{ + mWaitDialog->wakeUp(); +} + void WindowManager::addVisitedLocation(const std::string& name, int x, int y) { mMap->addVisitedLocation (name, x, y); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index d7773e261..e30e110d3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -209,6 +209,7 @@ namespace MWGui virtual bool getRestEnabled() { return mRestAllowed; } virtual bool getPlayerSleeping(); + virtual void wakeUpPlayer(); private: OEngine::GUI::MyGUIManager *mGuiManager; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index a4a9e99fd..584a29926 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -205,5 +205,6 @@ op 0x200019e: PlaceAtMe Explicit op 0x200019f: GetPcSleep op 0x20001a0: ShowMap op 0x20001a1: FillMap -opcodes 0x20001a2-0x3ffffff unused +op 0x20001a2: WakeUpPc +opcodes 0x20001a3-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a869f882b..674548cd6 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -31,6 +31,16 @@ namespace MWScript } }; + class OpWakeUpPc : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::Environment::get().getWindowManager ()->wakeUpPlayer(); + } + }; + class OpXBox : public Interpreter::Opcode0 { public: @@ -261,6 +271,7 @@ namespace MWScript const int opcodeDontSaveObject = 0x2000153; const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; + const int opcodeWakeUpPc = 0x20001a2; void registerExtensions (Compiler::Extensions& extensions) { @@ -286,6 +297,7 @@ namespace MWScript extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); + extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -307,6 +319,7 @@ namespace MWScript interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep); + interpreter.installSegment5 (opcodeWakeUpPc, new OpWakeUpPc); } } } From 41f80908d96697979409f61e75258b5635b841d8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 29 Sep 2012 01:10:49 -0700 Subject: [PATCH 688/688] Simplify getting the stop time when playing all animation groups --- apps/openmw/mwrender/animation.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f7a175332..ef92497e5 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -87,17 +87,9 @@ void Animation::playGroup(std::string groupname, int mode, int loops) times.mStart = times.mLoopStart = 0.0f; times.mLoopStop = times.mStop = 0.0f; - if(mEntityList.mSkelBase) - { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - times.mLoopStop = times.mStop = state->getLength(); - break; - } - } + NifOgre::TextKeyMap::const_reverse_iterator iter = mTextKeys.rbegin(); + if(iter != mTextKeys.rend()) + times.mLoopStop = times.mStop = iter->first; } else if(!findGroupTimes(groupname, ×)) throw std::runtime_error("Failed to find animation group "+groupname);