From 03ee7663a3e89698c36108d03a7fcd036762d827 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sat, 5 Jan 2013 17:33:28 -0800 Subject: [PATCH 01/50] reworked NIFStream to use a type-handler Reworked NIFStream to use a type-handler system to decide how to extract data from the NIF. It also has the capability to perform bulk reads on compatible platforms, thus improving cell-load performance. --- components/nif/nifstream.hpp | 338 +++++++++++++++++++++++------------ 1 file changed, 225 insertions(+), 113 deletions(-) diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index a2595d17b8..7abfae9e84 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -1,6 +1,8 @@ #ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP #define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP +#include + namespace Nif { @@ -11,168 +13,278 @@ class NIFStream { /// Input stream Ogre::DataStreamPtr inp; - uint8_t read_byte() + template + value_t read_le () { - 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 { - uint32_t i; - float f; - } u = { read_le32() }; - return u.f; + uint8_t buffer [sizeof (value_t)]; + + if (inp->read (buffer, sizeof (buffer)) != sizeof (buffer)) + throw std::runtime_error ("unexpected"); + + value_t Value = 0; + value_t Shift = 0; + + for (size_t i = 0; i < sizeof (value_t); ++i) + { + Value |= value_t (buffer[i]) << Shift; + Shift += 8; + } + + return Value; } public: + /* + * This should be true for any processor/platform whose endianess, alignment + * and packing rules would be compatible with x86 and not fault or degrade + * with misaligned reads. This enables some pretty big savings when reading in + * animations and meshes. + */ +#if defined (__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) + static const bool FileCompatiblePlatform = true; +#else + static const bool FileCompatiblePlatform = false; +#endif + + template + struct handler; + + template + struct le_handler; + + template + friend struct le_handler; + + template + struct conversion_handler; + NIFFile * const file; - NIFStream (NIFFile * file, Ogre::DataStreamPtr inp): file (file), inp (inp) {} + NIFStream (NIFFile * file, Ogre::DataStreamPtr inp) : file (file), inp (inp) {} /************************************************* Parser functions ****************************************************/ - template - struct GetHandler - { - typedef T (NIFStream::*fn_t)(); - - static const fn_t sValue; // this is specialized per supported type in the .cpp file - - static T read (NIFStream* nif) - { - return (nif->*sValue) (); - } - }; + void skip(size_t size) { return inp->skip (size); } + size_t read (void * data, size_t size) { return inp->read (data, size); } template - void read (NIFStream* nif, T & Value) + void uncheckedRead (T & Value) { - Value = GetHandler ::read (nif); + typedef handler handler_t; + handler_t::extract (*this, Value); } - void skip(size_t size) { inp->skip(size); } - void read (void * data, size_t size) { inp->read (data, size); } + template + T getValue () + { + T Value; + getValue (Value); + return Value; + } - char getChar() { return read_byte(); } - short getShort() { return read_le16(); } - unsigned short getUShort() { return read_le16(); } - int getInt() { return read_le32(); } - int getUInt() { return read_le32(); } - float getFloat() { return read_le32f(); } - Ogre::Vector2 getVector2() + template + void getValue (T & Value) { - float a[2]; - for(size_t i = 0;i < 2;i++) - a[i] = getFloat(); - return Ogre::Vector2(a); - } - Ogre::Vector3 getVector3() - { - float a[3]; - for(size_t i = 0;i < 3;i++) - a[i] = getFloat(); - return Ogre::Vector3(a); - } - Ogre::Vector4 getVector4() - { - float a[4]; - for(size_t i = 0;i < 4;i++) - a[i] = getFloat(); - return Ogre::Vector4(a); - } - Ogre::Matrix3 getMatrix3() - { - Ogre::Real a[3][3]; - for(size_t i = 0;i < 3;i++) + typedef handler handler_t; + if (FileCompatiblePlatform && handler_t::FileCompatibleLayout) { - for(size_t j = 0;j < 3;j++) - a[i][j] = Ogre::Real(getFloat()); + BOOST_STATIC_ASSERT_MSG (handler_t::FixedLength, "non-fixed length encoding not supported..."); + BOOST_STATIC_ASSERT_MSG (handler_t::EncodedLength == sizeof (T), "unexpected structure size"); + + inp->read (&Value, handler_t::EncodedLength); + } + else + { + handler_t::extract (*this, Value); } - return Ogre::Matrix3(a); } - Ogre::Quaternion getQuaternion() + + template + void getArray (element_type * Array, size_t Size) { - float a[4]; - for(size_t i = 0;i < 4;i++) - a[i] = getFloat(); - return Ogre::Quaternion(a); + typedef handler handler_t; + + if (FileCompatiblePlatform && handler_t::FileCompatibleLayout) + { + BOOST_STATIC_ASSERT_MSG (handler_t::FixedLength, "non-fixed length encoding not supported..."); + BOOST_STATIC_ASSERT_MSG (handler_t::EncodedLength == sizeof (element_type), "unexpected structure size"); + + inp->read (Array, handler_t::EncodedLength * Size); + } + else + { + for(size_t i = 0; i < Size; i++) + handler_t::extract (*this, Array[i]); + } } - Transformation getTrafo() + + template + void getStdVector (std::vector & Vector, size_t Size) { - Transformation t; - t.pos = getVector3(); - t.rotation = getMatrix3(); - t.scale = getFloat(); - return t; + Vector.resize(Size); + + getArray (&Vector.front (), Vector.size ()); } + template + void getStdVector (std::vector & Vector) + { + length_type Length; + + getValue (Length); + + getStdVector (Vector, Length); + } + + char getChar() { return getValue (); } + signed int getInt() { return getValue (); } + unsigned int getUInt() { return getValue (); } + signed short getShort() { return getValue (); } + unsigned short getUShort() { return getValue (); } + //signed long getLong() { return getValue (); } + //unsigned long getULong() { return getValue (); } + float getFloat() { return getValue (); } + + Ogre::Vector2 getVector2() { return getValue (); } + Ogre::Vector3 getVector3() { return getValue (); } + Ogre::Vector4 getVector4() { return getValue (); } + Ogre::Matrix3 getMatrix3() { return getValue (); } + Ogre::Quaternion getQuaternion() { return getValue (); } + + Transformation getTrafo() { return getValue (); } + + void getShorts(std::vector &vec, size_t size) { return getStdVector (vec, size); } + void getFloats(std::vector &vec, size_t size) { return getStdVector (vec, size); } + void getVector2s(std::vector &vec, size_t size) { return getStdVector (vec, size); } + void getVector3s(std::vector &vec, size_t size) { return getStdVector (vec, size); } + void getVector4s(std::vector &vec, size_t size) { return getStdVector (vec, size); } + void getQuaternions(std::vector &vec, size_t size) { return getStdVector (vec, size); } + std::string getString(size_t length) { std::vector str (length+1, 0); - if(inp->read(&str[0], length) != length) + if(read(&str[0], length) != length) throw std::runtime_error ("string length in NIF file does not match"); return &str[0]; } std::string getString() { - size_t size = read_le32(); + size_t size = getValue (); return getString(size); } +}; - void getShorts(std::vector &vec, size_t size) +/* + * generic type handlers + */ + +template +struct NIFStream::handler < type [Size] > +{ + typedef handler inner_handler; + + static const bool FixedLength = inner_handler::FixedLength; + static const size_t EncodedLength = inner_handler::EncodedLength * Size; + static const bool FileCompatibleLayout = inner_handler::FileCompatibleLayout; + + static void extract (NIFStream & Stream, type (&Value) [Size]) { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getShort(); + for (size_t i = 0; i < Size; ++i) + inner_handler::extract (Stream, Value [i]); } - void getFloats(std::vector &vec, size_t size) +}; + +template +struct NIFStream::le_handler +{ + static const bool FixedLength = true; + static const size_t EncodedLength = sizeof (backing_type); + static const bool FileCompatibleLayout = true; + + static void extract (NIFStream & Stream, presentation_type & Value) { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getFloat(); + BOOST_STATIC_ASSERT_MSG(sizeof (presentation_type) == sizeof (backing_type), "Invalid use of NIFile::le_handler template"); + + union { + + backing_type Backing; + presentation_type Presentation; + + } u; + + u.Backing = Stream.read_le (); + + Value = u.Presentation; } - void getVector2s(std::vector &vec, size_t size) +}; + +template +struct NIFStream::conversion_handler +{ + typedef handler store_handler; + + static const bool FixedLength = store_handler::FixedLength; + static const size_t EncodedLength = store_handler::EncodedLength; + static const bool FileCompatibleLayout = false; + + static void extract (NIFStream & Stream, final_type & Value) { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector2(); + store_type StoreValue; + store_handler::extract (Stream, StoreValue); + Value = final_type (StoreValue); } - void getVector3s(std::vector &vec, size_t size) +}; + +template +struct NIFStream::conversion_handler +{ + typedef handler store_handler; + + static const bool FixedLength = store_handler::FixedLength; + static const size_t EncodedLength = store_handler::EncodedLength; + static const bool FileCompatibleLayout = store_handler::FileCompatibleLayout; + + static void extract (NIFStream & Stream, final_type & FinalValue) { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector3(); + store_handler::extract (Stream, reinterpret_cast (FinalValue)); } - void getVector4s(std::vector &vec, size_t size) +}; + +template <> struct NIFStream::handler : NIFStream::le_handler {}; +template <> struct NIFStream::handler : NIFStream::le_handler {}; +template <> struct NIFStream::handler : NIFStream::le_handler {}; +template <> struct NIFStream::handler : NIFStream::le_handler {}; +template <> struct NIFStream::handler : NIFStream::le_handler {}; +template <> struct NIFStream::handler : NIFStream::le_handler {}; + +template <> struct NIFStream::handler : NIFStream::le_handler {}; +template <> struct NIFStream::handler : NIFStream::le_handler {}; + +template <> struct NIFStream::handler : NIFStream::conversion_handler {}; +template <> struct NIFStream::handler : NIFStream::conversion_handler {}; +template <> struct NIFStream::handler : NIFStream::conversion_handler {}; +template <> struct NIFStream::handler : NIFStream::conversion_handler {}; +template <> struct NIFStream::handler : NIFStream::conversion_handler {}; + +template <> struct NIFStream::handler +{ + static const bool FixedLength = true; + static const size_t EncodedLength = + handler ::EncodedLength + + handler ::EncodedLength + + handler ::EncodedLength; + static const bool FileCompatibleLayout = true; + + static void extract (NIFStream & stream, Transformation & value) { - vec.resize(size); - for(size_t i = 0;i < vec.size();i++) - vec[i] = getVector4(); - } - void getQuaternions(std::vector &quat, size_t size) - { - quat.resize(size); - for(size_t i = 0;i < quat.size();i++) - quat[i] = getQuaternion(); + stream.uncheckedRead (value.pos); + stream.uncheckedRead (value.rotation); + stream.uncheckedRead (value.scale); } }; From e7665582ad9b855522b37d955997df527254a01d Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sat, 5 Jan 2013 22:51:06 -0800 Subject: [PATCH 02/50] reworked Nif::KeyListT into Nif::CurveT Renamed Nif:KeyListT to Nif::CurveT, moved it into its own file and changed its implementation so that on compatible platforms, the entire array of key-frames can be read in a single read call. Added a helper class called Nif::CurveT::interpolator to allow other code to easily evaluate the curve. Reworked part of the skeletonLoader code to use the interpolator to simplify its own logic. --- components/nif/curve.hpp | 424 +++++++++++++++++++++++++++ components/nif/data.hpp | 17 +- components/nif/niffile.hpp | 76 ----- components/nifogre/ogrenifloader.cpp | 113 ++----- components/nifogre/skeleton.cpp | 107 ++----- 5 files changed, 481 insertions(+), 256 deletions(-) create mode 100644 components/nif/curve.hpp diff --git a/components/nif/curve.hpp b/components/nif/curve.hpp new file mode 100644 index 0000000000..9a71f0923d --- /dev/null +++ b/components/nif/curve.hpp @@ -0,0 +1,424 @@ +#ifndef _NIF_KEYLIST_H_ +#define _NIF_KEYLIST_H_ + +#include + +namespace Nif +{ + +template +void bubble_sort (iterator begin, iterator end, predicate const & in_order) +{ + if (end > begin) + { + for (iterator i = begin; i != end - 1; ++i) + { + if (in_order (*(i+0), *(i+1))) + continue; + + for (iterator j = i; j >= begin; --j) + { + std::swap (*(j+0), *(j+1)); + + if (in_order (*(j+0), *(j+1))) + break; + } + } + } +} + +template +value_type linear_interpolate (float amount, value_type prev, value_type next) +{ + return prev + (next - prev) * amount; +} + +inline +Ogre::Quaternion linear_interpolate (float amount, Ogre::Quaternion prev, Ogre::Quaternion next) +{ + return Ogre::Quaternion::nlerp (amount, prev, next); +} + +template +struct KeyT { + + static const size_t EncodedLength = + NIFStream::handler ::EncodedLength + + NIFStream::handler ::EncodedLength + ; + + float mTime; + value_type mValue; + + void extract (NIFStream &nif) + { + nif.uncheckedRead (mTime); + nif.uncheckedRead (mValue); + } + + static bool in_order (KeyT const & l, KeyT const & r) + { + return l.mTime < r.mTime; + } + + template + struct NIFStream_handler + { + static const bool FixedLength = true; + static const size_t EncodedLength = derived_type::EncodedLength; + static const bool FileCompatibleLayout = true; + + static void extract (NIFStream& Stream, KeyT & Value) + { + static_cast (Value).extract (Stream); + } + }; +}; + +template +struct LinearKeyT : KeyT +{ + static T interpolate (LinearKeyT * prev, LinearKeyT * next, float amount) + { + return linear_interpolate (amount, prev->mValue, next->mValue); + } +}; + +template +struct QuadraticKeyT : KeyT +{ + static const size_t EncodedLength = + KeyT ::EncodedLength + + NIFStream::handler ::EncodedLength * 2 + ; + + T mForwardValue; + T mBackwardValue; + + static T interpolate (QuadraticKeyT * prev, QuadraticKeyT * next, float amount) + { + return linear_interpolate (amount, prev->mValue, next->mValue); + } + + void extract (NIFStream &nif) + { + KeyT::extract (nif); + + nif.uncheckedRead (mForwardValue); + nif.uncheckedRead (mBackwardValue); + } +}; + +template +struct TbcKeyT : KeyT +{ + static const size_t EncodedLength = + KeyT ::EncodedLength + + NIFStream::handler ::EncodedLength * 3 + ; + + float mTension; + float mBias; + float mContinuity; + + static T interpolate (TbcKeyT * prev, TbcKeyT * next, float amount) + { + return linear_interpolate (amount, prev->mValue, next->mValue); + } + + void extract (NIFStream &nif) + { + KeyT::extract (nif); + + nif.uncheckedRead (mTension); + nif.uncheckedRead (mBias); + nif.uncheckedRead (mContinuity); + } +}; + +// register NIFStream extraction handlers for KeyT derivatives +template struct NIFStream::handler < LinearKeyT > : KeyT ::template NIFStream_handler < LinearKeyT > {}; +template struct NIFStream::handler < QuadraticKeyT > : KeyT ::template NIFStream_handler < QuadraticKeyT > {}; +template struct NIFStream::handler < TbcKeyT > : KeyT ::template NIFStream_handler < TbcKeyT > {}; + +struct Curve +{ + static const int sLinearInterpolation = 1; + static const int sQuadraticInterpolation = 2; + static const int sTBCInterpolation = 3; +}; + +template +struct CurveT : Curve { + + typedef KeyT BaseKey; + typedef TbcKeyT TcbKey; + typedef LinearKeyT LinearKey; + typedef QuadraticKeyT QuadraticKey; + + union keys { + LinearKey* Linear; + QuadraticKey* Quadratic; + TcbKey* Tcb; + }; + + class interpolator; + + int mInterpolationType; + size_t mSize; + keys mKeys; + + value_type sample (float time) const; + + KeyT const * const & keyAtIndex (size_t Index) const + { + switch (mInterpolationType) + { + case sLinearInterpolation: return mKeys.Linear + Index; + case sQuadraticInterpolation: return mKeys.Quadratic + Index; + case sTBCInterpolation: return mKeys.Tcb + Index; + } + } + + void read(NIFStream *nif, bool force=false) + { + size_t count = nif->getInt(); + + mSize = 0; + + if(count > 0 || force) + { + mInterpolationType = nif->getInt(); + + assert (mInterpolationType >= sLinearInterpolation && mInterpolationType <= sTBCInterpolation); + + if (count > 0) + { + if(mInterpolationType == sLinearInterpolation) + read_keys (nif, mKeys.Linear, count); + else if(mInterpolationType == sQuadraticInterpolation) + read_keys (nif, mKeys.Quadratic, count); + else if(mInterpolationType == sTBCInterpolation) + read_keys (nif, mKeys.Tcb, count); + else + nif->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); + } + } + else + mInterpolationType = sLinearInterpolation; + } + + CurveT () { init (); } + CurveT (CurveT const & k) { init (k); } + //CurveT (CurveT && k) { init (); swap (std::move (k)); } + ~CurveT () { dest (); } + + operator bool () const { return mSize > 0; } + + //void operator = (CurveT && k) { swap(k); } + void operator = (CurveT const & k) { dest (); init (k); } + + void swap (CurveT & k) + { + std::swap (mSize, k.mSize); + std::swap (mInterpolationType, k.mInterpolationType); + std::swap (mKeys, k.mKeys); + } + +private: + + void init () + { + mSize = 0; + } + + void init (CurveT const & k) + { + mInterpolationType = k.mInterpolationType; + switch (mInterpolationType) + { + default: + case sLinearInterpolation: + mKeys.Linear = new LinearKey [k.mSize]; + memcpy (mKeys.Linear, k.mKeys.Linear, sizeof (LinearKey) * k.mSize); + mSize = k.mSize; + break; + case sQuadraticInterpolation: + mKeys.Quadratic = new QuadraticKey [k.mSize]; + memcpy (mKeys.Quadratic, k.mKeys.Quadratic, sizeof (QuadraticKey) * k.mSize); + mSize = k.mSize; + break; + case sTBCInterpolation: + mKeys.Tcb = new TcbKey [k.mSize]; + memcpy (mKeys.Tcb, k.mKeys.Tcb, sizeof (TcbKey) * k.mSize); + mSize = k.mSize; + break; + } + } + + void dest () + { + if (mSize > 0) + { + switch (mInterpolationType) + { + case sLinearInterpolation: delete mKeys.Linear; break; + case sQuadraticInterpolation: delete mKeys.Quadratic; break; + case sTBCInterpolation: delete mKeys.Tcb; break; + } + } + } + + template + void read_keys (NIFStream *nif, T * & store, size_t count) + { + store = new T [count]; + + mSize = count; + + nif->getArray (store, count); + + //NOTE: Is this really necessary? It seems reasonable to assume that + // animation data is already sorted by time... + // verified no out of order frames in GOTY edition + bubble_sort (store, store+count, T::in_order); + } +}; + +template +class CurveT::interpolator +{ + template + struct impl + { + key_type *Cur, *End; + + void init (key_type * Beg, size_t Len) + { + if (Len > 0) + { + Cur = Beg; + End = Beg + Len - 1; + } + else + { + Cur = End = NULL; + } + } + + bool hasData () const + { + return Cur && Cur <= End; + } + + value_type valueAt (float time) + { + while ((Cur < End) && (time >= Cur [1].mTime)) + ++Cur; + + if (Cur < End) + { + if (time > Cur->mTime) + { + key_type * Nxt = Cur + 1; + + float offset = time - Cur->mTime; + float length = Nxt->mTime - Cur->mTime; + + return key_type::interpolate (Cur, Nxt, offset / length); + } + else + return Cur->mValue; + } + else + return End->mValue; + } + + float curTime () const + { + return (Cur != NULL) ? Cur->Time : FLT_MIN; + } + + float nextTime () const + { + return Cur < End ? (Cur + 1)->mTime : FLT_MAX; + } + }; + +public: + + int mInterpolationType; + union { + impl Linear; + impl Quadratic; + impl Tcb; + }; + + interpolator (CurveT const & Curve) + { + mInterpolationType = Curve.mInterpolationType; + + switch (mInterpolationType) + { + default: + case Curve::sLinearInterpolation: Linear .init (Curve.mKeys.Linear, Curve.mSize); break; + case Curve::sQuadraticInterpolation: Quadratic.init (Curve.mKeys.Quadratic, Curve.mSize); break; + case Curve::sTBCInterpolation: Tcb .init (Curve.mKeys.Tcb, Curve.mSize); break; + } + } + + // return true if there is any value(s) in this curve + float hasData () const + { + switch (mInterpolationType) + { + default: + case Curve::sLinearInterpolation: return Linear .hasData (); + case Curve::sQuadraticInterpolation: return Quadratic.hasData (); + case Curve::sTBCInterpolation: return Tcb .hasData (); + } + } + + // return the timestamp of the next key-frame, or FLT_MAX if + // there are no more key-frames, valid if hasData returns false + float nextTime () const + { + switch (mInterpolationType) + { + default: + case Curve::sLinearInterpolation: return Linear .nextTime (); + case Curve::sQuadraticInterpolation: return Quadratic.nextTime (); + case Curve::sTBCInterpolation: return Tcb .nextTime (); + } + } + + // return the value of the curve at the specified time + // the passed in time should never exceed the result of + // nextTime, not valid if hasData returns false + value_type valueAt (float time) + { + switch (mInterpolationType) + { + default: + case Curve::sLinearInterpolation: return Linear .valueAt (time); + case Curve::sQuadraticInterpolation: return Quadratic.valueAt (time); + case Curve::sTBCInterpolation: return Tcb .valueAt (time); + } + } +}; + +template +value_type CurveT::sample (float time) const +{ + interpolator i (*this); + return i.valueAt (time); +} + +typedef CurveT FloatCurve; +typedef CurveT Vector3Curve; +typedef CurveT Vector4Curve; +typedef CurveT QuaternionCurve; + +} + +#endif diff --git a/components/nif/data.hpp b/components/nif/data.hpp index f1f34184ba..d46f99abbc 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -25,6 +25,7 @@ #define OPENMW_COMPONENTS_NIF_DATA_HPP #include "controlled.hpp" +#include "curve.hpp" #include #include @@ -211,7 +212,7 @@ public: class NiPosData : public Record { public: - Vector3KeyList mKeyList; + Vector3Curve mKeyList; void read(NIFStream *nif) { @@ -222,7 +223,7 @@ public: class NiUVData : public Record { public: - FloatKeyList mKeyList[4]; + FloatCurve mKeyList[4]; void read(NIFStream *nif) { @@ -234,7 +235,7 @@ public: class NiFloatData : public Record { public: - FloatKeyList mKeyList; + FloatCurve mKeyList; void read(NIFStream *nif) { @@ -284,7 +285,7 @@ public: class NiColorData : public Record { public: - Vector4KeyList mKeyList; + Vector4Curve mKeyList; void read(NIFStream *nif) { @@ -389,7 +390,7 @@ public: struct NiMorphData : public Record { struct MorphData { - FloatKeyList mData; + FloatCurve mData; std::vector mVertices; }; std::vector mMorphs; @@ -412,9 +413,9 @@ struct NiMorphData : public Record struct NiKeyframeData : public Record { - QuaternionKeyList mRotations; - Vector3KeyList mTranslations; - FloatKeyList mScales; + QuaternionCurve mRotations; + Vector3Curve mTranslations; + FloatCurve mScales; void read(NIFStream *nif) { diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 6e629772e6..83cc6ac8fd 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -132,81 +132,5 @@ public: size_t numRoots() { return roots.size(); } }; - -template -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 Vector4Key; -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(NIFStream *nif, bool force=false) - { - size_t count = nif->getInt(); - if(count == 0 && !force) - 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->file->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); - } -}; -typedef KeyListT FloatKeyList; -typedef KeyListT Vector3KeyList; -typedef KeyListT Vector4KeyList; -typedef KeyListT QuaternionKeyList; - } // Namespace #endif diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index a26f431311..f381dc239f 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -166,9 +166,9 @@ public: class Value : public NodeTargetValue { private: - Nif::QuaternionKeyList mRotations; - Nif::Vector3KeyList mTranslations; - Nif::FloatKeyList mScales; + Nif::QuaternionCurve mRotations; + Nif::Vector3Curve mTranslations; + Nif::FloatCurve mScales; public: Value(Ogre::Node *target, const Nif::NiKeyframeData *data) @@ -186,68 +186,16 @@ public: virtual void setValue(Ogre::Real time) { - if(mRotations.mKeys.size() > 0) - { - if(time <= mRotations.mKeys.front().mTime) - mNode->setOrientation(mRotations.mKeys.front().mValue); - else if(time >= mRotations.mKeys.back().mTime) - mNode->setOrientation(mRotations.mKeys.back().mValue); - else - { - Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1); - for(;iter != mRotations.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; + if(mRotations) + mNode->setOrientation(mRotations.sample (time)); - Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue)); - break; - } - } - } - if(mTranslations.mKeys.size() > 0) - { - if(time <= mTranslations.mKeys.front().mTime) - mNode->setPosition(mTranslations.mKeys.front().mValue); - else if(time >= mTranslations.mKeys.back().mTime) - mNode->setPosition(mTranslations.mKeys.back().mValue); - else - { - Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1); - for(;iter != mTranslations.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; + if(mTranslations) + mNode->setPosition(mTranslations.sample (time)); - Nif::Vector3KeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a)); - break; - } - } - } - if(mScales.mKeys.size() > 0) + if(mScales) { - if(time <= mScales.mKeys.front().mTime) - mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue)); - else if(time >= mScales.mKeys.back().mTime) - mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue)); - else - { - Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1); - for(;iter != mScales.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::FloatKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a))); - break; - } - } + float s = mScales.sample (time); + mNode->setScale(s, s, s); } } }; @@ -262,30 +210,14 @@ public: { private: Ogre::MaterialPtr mMaterial; - Nif::FloatKeyList mUTrans; - Nif::FloatKeyList mVTrans; - Nif::FloatKeyList mUScale; - Nif::FloatKeyList mVScale; + Nif::FloatCurve mUTrans; + Nif::FloatCurve mVTrans; + Nif::FloatCurve mUScale; + Nif::FloatCurve mVScale; - static float lookupValue(const Nif::FloatKeyList &keys, float time, float def) + static float lookupValue(const Nif::FloatCurve &keys, float time, float def) { - if(keys.mKeys.size() == 0) - return def; - - if(time <= keys.mKeys.front().mTime) - return keys.mKeys.front().mValue; - - Nif::FloatKeyList::VecType::const_iterator iter(keys.mKeys.begin()+1); - for(;iter != keys.mKeys.end();iter++) - { - if(iter->mTime < time) - continue; - - Nif::FloatKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return last->mValue + ((iter->mValue - last->mValue)*a); - } - return keys.mKeys.back().mValue; + return keys ? keys.sample (time) : def; } public: @@ -392,18 +324,19 @@ class NIFObjectLoader const Nif::NiColorData *clrdata = cl->data.getPtr(); Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator"); - size_t num_colors = std::min(6, clrdata->mKeyList.mKeys.size()); + size_t num_colors = std::min(6, clrdata->mKeyList.mSize); for(size_t i = 0;i < num_colors;i++) { + Nif::Vector4Curve::BaseKey const * Key = clrdata->mKeyList.keyAtIndex (i); Ogre::ColourValue color; - color.r = clrdata->mKeyList.mKeys[i].mValue[0]; - color.g = clrdata->mKeyList.mKeys[i].mValue[1]; - color.b = clrdata->mKeyList.mKeys[i].mValue[2]; - color.a = clrdata->mKeyList.mKeys[i].mValue[3]; + color.r = Key->mValue[0]; + color.g = Key->mValue[1]; + color.b = Key->mValue[2]; + color.a = Key->mValue[3]; affector->setParameter("colour"+Ogre::StringConverter::toString(i), Ogre::StringConverter::toString(color)); affector->setParameter("time"+Ogre::StringConverter::toString(i), - Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime)); + Ogre::StringConverter::toString(Key->mTime)); } } else if(e->recType == Nif::RC_NiParticleRotation) diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index e97e91ef03..66060b9676 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -10,6 +10,11 @@ namespace NifOgre { +template +static value_type min (value_type V0, value_type V1, value_type V2, value_type V3) +{ + return std::min (std::min (V0, V1), std::min (V2, V3)); +} void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime) { @@ -22,15 +27,6 @@ void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string & continue; const Nif::NiKeyframeData *kf = kfc->data.getPtr(); - /* Get the keyframes and make sure they're sorted first to last */ - const Nif::QuaternionKeyList &quatkeys = kf->mRotations; - const Nif::Vector3KeyList &trankeys = kf->mTranslations; - const Nif::FloatKeyList &scalekeys = kf->mScales; - - Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); - Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); - Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); - Ogre::Bone *bone = skel->getBone(targets[i]); // NOTE: For some reason, Ogre doesn't like the node track ID being different from // the bone ID @@ -38,83 +34,30 @@ void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string & anim->getNodeTrack(bone->getHandle()) : anim->createNodeTrack(bone->getHandle(), bone); - 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 = quatiter->mValue; - if(traniter != trankeys.mKeys.end()) - lasttrans = curtrans = traniter->mValue; - if(scaleiter != scalekeys.mKeys.end()) - lastscale = curscale = Ogre::Vector3(scaleiter->mValue); + Nif::QuaternionCurve::interpolator rci (kf->mRotations); + Nif::Vector3Curve::interpolator tci (kf->mTranslations); + Nif::FloatCurve::interpolator sci (kf->mScales); - bool didlast = false; - while(!didlast) + float next_timestamp = startTime; + + for (;;) { - float curtime = std::numeric_limits::max(); - - //Get latest time - 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, startTime); - if(curtime >= stopTime) - { - didlast = true; - curtime = stopTime; - } - - // Get the latest quaternions, translations, and scales for the - // current time - while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) - { - lastquat = curquat; - if(++quatiter != quatkeys.mKeys.end()) - curquat = quatiter->mValue; - } - while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) - { - lasttrans = curtrans; - if(++traniter != trankeys.mKeys.end()) - curtrans = traniter->mValue; - } - while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) - { - lastscale = curscale; - if(++scaleiter != scalekeys.mKeys.end()) - curscale = Ogre::Vector3(scaleiter->mValue); - } + static const Ogre::Vector3 one (1,1,1); Ogre::TransformKeyFrame *kframe; - kframe = nodetrack->createNodeKeyFrame(curtime); - if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) - kframe->setRotation(curquat); - else - { - Nif::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 - { - Nif::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 - { - Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; - float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); - kframe->setScale(lastscale + ((curscale-lastscale)*diff)); - } + kframe = nodetrack->createNodeKeyFrame (next_timestamp); + + if (rci.hasData ()) kframe->setRotation (rci.valueAt (next_timestamp)); + if (tci.hasData ()) kframe->setTranslate (tci.valueAt (next_timestamp)); + if (sci.hasData ()) kframe->setScale (sci.valueAt (next_timestamp)*one); + + if (next_timestamp >= stopTime) + break; + + next_timestamp = min (stopTime, + rci.nextTime (), + tci.nextTime (), + sci.nextTime ()); } } anim->optimise(); From 6dbb53493b34c6430e8bdf0ea0d4f411facfa1b5 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Thu, 3 Jan 2013 23:39:00 -0800 Subject: [PATCH 03/50] enable move construction/assignment to key ESM data structure to reduce sorting costs --- components/esm/defs.hpp | 10 +++++++ components/esm/loaddial.hpp | 36 ++++++++++++++++++++++ components/esm/loadinfo.hpp | 60 +++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index bd86f9ba03..3dc498a639 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -1,6 +1,16 @@ #ifndef OPENMW_ESM_DEFS_H #define OPENMW_ESM_DEFS_H +#if _MSC_VER >= 1600 +#define OPENMW_ESM_ENABLE_CPP11_MOVE +#define OPENMW_ESM_DEFINE_CPP11_MOVE_OPS(Class) \ + Class () {} \ + Class (Class const & that) { copy (*this, that); } \ + Class (Class && that) { move (*this, that); } \ + Class & operator = (Class const & that) { copy (*this, that); return *this; } \ + Class & operator = (Class && that) { move (*this, that); return *this; } +#endif + #include namespace ESM diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 61f3f763de..bb726b8557 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "loadinfo.hpp" @@ -35,6 +36,41 @@ struct Dialogue void load(ESMReader &esm); void save(ESMWriter &esm); + +#ifdef OPENMW_ESM_ENABLE_CPP11_MOVE + OPENMW_ESM_DEFINE_CPP11_MOVE_OPS(Dialogue) + + static void copy (Dialogue & d, Dialogue const & s) + { + d.mId = s.mId; + d.mType = s.mType; + d.mInfo = s.mInfo; + } + static void move (Dialogue & d, Dialogue & s) + { + d.mId = std::move (s.mId); + d.mType = std::move (s.mType); + d.mInfo = std::move (s.mInfo); + } +#endif }; } + +/* + custom swap to prevent memory allocations and deallocations for mId and mInfo + while sorting +*/ +namespace std +{ + template <> inline + void swap (ESM::Dialogue & Left, ESM::Dialogue & Right) + { +#define _swp(id) std::swap (Left.id, Right.id); + _swp(mId); + _swp(mType); + _swp(mInfo); +#undef _swp + } +} + #endif diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 2361ed9eb5..c7608ca866 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -43,6 +43,22 @@ struct DialInfo { std::string mSelectRule; // This has a complicated format Variant mValue; + +#ifdef OPENMW_ESM_ENABLE_CPP11_MOVE + OPENMW_ESM_DEFINE_CPP11_MOVE_OPS(SelectStruct) + + static void copy (SelectStruct & d, SelectStruct const & s) + { + d.mSelectRule = s.mSelectRule; + d.mValue = s.mValue; + } + + static void move (SelectStruct & d, SelectStruct & s) + { + d.mSelectRule = std::move (s.mSelectRule); + d.mValue = std::move (s.mValue); + } +#endif // OPENMW_ESM_ENABLE_CPP11_MOVE }; // Journal quest indices (introduced with the quest system in Tribunal) @@ -100,6 +116,50 @@ struct DialInfo void load(ESMReader &esm); void save(ESMWriter &esm); + +#ifdef OPENMW_ESM_ENABLE_CPP11_MOVE + OPENMW_ESM_DEFINE_CPP11_MOVE_OPS(DialInfo) + + static void copy (DialInfo & d, DialInfo const & s) + { + d.mData = s.mData; + d.mSelects = s.mSelects; + d.mId = s.mId; + d.mPrev = s.mPrev; + d.mNext = s.mNext; + d.mActor = s.mActor; + d.mRace = s.mRace; + d.mClass = s.mClass; + d.mNpcFaction = s.mNpcFaction; + d.mPcFaction = s.mPcFaction; + d.mCell = s.mCell; + d.mSound = s.mSound; + d.mResponse = s.mResponse; + d.mResultScript = s.mResultScript; + d.mFactionLess = s.mFactionLess; + d.mQuestStatus = s.mQuestStatus; + } + + static void move (DialInfo & d, DialInfo & s) + { + d.mData = std::move (s.mData); + d.mSelects = std::move (s.mSelects); + d.mId = std::move (s.mId); + d.mPrev = std::move (s.mPrev); + d.mNext = std::move (s.mNext); + d.mActor = std::move (s.mActor); + d.mRace = std::move (s.mRace); + d.mClass = std::move (s.mClass); + d.mNpcFaction = std::move (s.mNpcFaction); + d.mPcFaction = std::move (s.mPcFaction); + d.mCell = std::move (s.mCell); + d.mSound = std::move (s.mSound); + d.mResponse = std::move (s.mResponse); + d.mResultScript = std::move (s.mResultScript); + d.mFactionLess = std::move (s.mFactionLess); + d.mQuestStatus = std::move (s.mQuestStatus); + } +#endif // OPENMW_ESM_ENABLE_CPP11_MOVE }; } From 8dad04eef19e7471787615348181d6c32ed6e82a Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 13 Jan 2013 13:03:11 -0800 Subject: [PATCH 04/50] hid JournalWindow behind IJournalWindow interface, and put its entire implementation, class definition and all, into journalwindow.cpp --- apps/openmw/mwgui/journalwindow.cpp | 223 ++++++++++++++----------- apps/openmw/mwgui/journalwindow.hpp | 34 +--- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.hpp | 4 +- 4 files changed, 137 insertions(+), 126 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 23588a4afa..9540e69868 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -5,6 +5,16 @@ #include "../mwbase/journal.hpp" #include "../mwbase/soundmanager.hpp" +#include +#include +#include +#include + +#include "windowbase.hpp" +#include "imagebutton.hpp" + +using namespace MWGui; + namespace { struct book @@ -77,116 +87,135 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) //std::string } - -MWGui::JournalWindow::JournalWindow () - : WindowBase("openmw_journal.layout") - , mPageNumber(0) +namespace { - mMainWidget->setVisible(false); - //setCoord(0,0,498, 342); - center(); - - getWidget(mLeftTextWidget, "LeftText"); - getWidget(mRightTextWidget, "RightText"); - getWidget(mPrevBtn, "PrevPageBTN"); - mPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyPrevPage); - getWidget(mNextBtn, "NextPageBTN"); - mNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyNextPage); - //MyGUI::ItemBox* list = new MyGUI::ItemBox(); - //list->addItem("qaq","aqzazaz"); - //mScrollerWidget->addChildItem(list); - //mScrollerWidget->addItem("dserzt",MyGUI::UString("fedgdfg")); - //mEditWidget->addText("ljblsxdvdsfvgedfvdfvdkjfghldfjgn sdv,nhsxl;vvn lklksbvlksb lbsdflkbdSLKJGBLskdhbvlshow(); - //mEditWidget->setEditStatic(true); - mLeftTextWidget->addText("left texxxt "); - mLeftTextWidget->setEditReadOnly(true); - mRightTextWidget->setEditReadOnly(true); - mRightTextWidget->setEditStatic(true); - mLeftTextWidget->setEditStatic(true); - mRightTextWidget->addText("Right texxt "); - - //std::list list = formatText("OpenMW rgh dsfg sqef srg ZT uzql n ZLIEHRF LQSJH GLOIjf qjfmj hslkdgn jlkdjhg qlr isgli shli uhs fiuh qksf cg ksjnf lkqsnbf ksbf sbfkl zbf kuyzflkj sbgdfkj zlfh ozhjfmo hzmfh lizuf rty qzt ezy tkyEZT RYYJ DG fgh is an open-source implementation of the game engine found in the game Morrowind. This is a dumb test text msodjbg smojg smoig fiiinnn"); - //std::list list = formatText(); - //displayLeftText(list.front()); -} - -void MWGui::JournalWindow::open() -{ - mPageNumber = 0; - if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end()) + struct JournalWindow : WindowBase, IJournalWindow { - book journal; - journal.endLine = 0; + MyGUI::EditPtr mLeftTextWidget; + MyGUI::EditPtr mRightTextWidget; + MWGui::ImageButton* mPrevBtn; + MWGui::ImageButton* mNextBtn; + std::vector mLeftPages; + std::vector mRightPages; + int mPageNumber; //store the number of the current left page - for(std::deque::const_iterator it = MWBase::Environment::get().getJournal()->begin();it!=MWBase::Environment::get().getJournal()->end();++it) + JournalWindow () + : WindowBase("openmw_journal.layout") + , mPageNumber(0) { - std::string a = it->getText(MWBase::Environment::get().getWorld()->getStore()); - journal = formatText(a,journal,10,17); - journal.endLine = journal.endLine +1; - journal.pages.back() = journal.pages.back() + std::string("\n"); + mMainWidget->setVisible(false); + //setCoord(0,0,498, 342); + center(); + + getWidget(mLeftTextWidget, "LeftText"); + getWidget(mRightTextWidget, "RightText"); + getWidget(mPrevBtn, "PrevPageBTN"); + mPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&JournalWindow::notifyPrevPage); + getWidget(mNextBtn, "NextPageBTN"); + mNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&JournalWindow::notifyNextPage); + + + + mLeftTextWidget->setEditReadOnly(true); + mRightTextWidget->setEditReadOnly(true); + mRightTextWidget->setEditStatic(true); + mLeftTextWidget->setEditStatic(true); } - //std::string a = MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); - //std::list journal = formatText(a,10,20,1); - bool left = true; - for(std::list::iterator it = journal.pages.begin(); it != journal.pages.end();++it) + + void close() { - if(left) + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); + } + + void open() + { + mPageNumber = 0; + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); + if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end()) { - mLeftPages.push_back(*it); + book journal; + journal.endLine = 0; + + for(std::deque::const_iterator it = MWBase::Environment::get().getJournal()->begin();it!=MWBase::Environment::get().getJournal()->end();++it) + { + std::string a = it->getText(MWBase::Environment::get().getWorld()->getStore()); + journal = formatText(a,journal,10,17); + journal.endLine = journal.endLine +1; + journal.pages.back() = journal.pages.back() + std::string("\n"); + } + //std::string a = MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); + //std::list journal = formatText(a,10,20,1); + bool left = true; + for(std::list::iterator it = journal.pages.begin(); it != journal.pages.end();++it) + { + if(left) + { + mLeftPages.push_back(*it); + } + else + { + mRightPages.push_back(*it); + } + left = !left; + } + if(!left) mRightPages.push_back(""); + + mPageNumber = mLeftPages.size()-1; + displayLeftText(mLeftPages[mPageNumber]); + displayRightText(mRightPages[mPageNumber]); + } else { - mRightPages.push_back(*it); + //std::cout << MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); } - left = !left; } - if(!left) mRightPages.push_back(""); - mPageNumber = mLeftPages.size()-1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); + void setVisible (bool newValue) + { + WindowBase::setVisible (newValue); + } - } - else - { - //std::cout << MWBase::Environment::get().getJournal()->begin()->getText(MWBase::Environment::get().getWorld()->getStore()); - } + + void displayLeftText(std::string text) + { + mLeftTextWidget->eraseText(0,mLeftTextWidget->getTextLength()); + mLeftTextWidget->addText(text); + } + + void displayRightText(std::string text) + { + mRightTextWidget->eraseText(0,mRightTextWidget->getTextLength()); + mRightTextWidget->addText(text); + } + + + + void notifyNextPage(MyGUI::Widget* _sender) + { + 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(mLeftPages[mPageNumber]); + displayRightText(mRightPages[mPageNumber]); + } + } + + void notifyPrevPage(MyGUI::Widget* _sender) + { + if(mPageNumber > 0) + { + std::string prevSound = "book page"; + MWBase::Environment::get().getSoundManager()->playSound (prevSound, 1.0, 1.0); + mPageNumber = mPageNumber - 1; + displayLeftText(mLeftPages[mPageNumber]); + displayRightText(mRightPages[mPageNumber]); + } + } + }; } -void MWGui::JournalWindow::displayLeftText(std::string text) -{ - mLeftTextWidget->eraseText(0,mLeftTextWidget->getTextLength()); - mLeftTextWidget->addText(text); -} - -void MWGui::JournalWindow::displayRightText(std::string text) -{ - mRightTextWidget->eraseText(0,mRightTextWidget->getTextLength()); - mRightTextWidget->addText(text); -} - - -void MWGui::JournalWindow::notifyNextPage(MyGUI::Widget* _sender) -{ - 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(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); - } -} - -void MWGui::JournalWindow::notifyPrevPage(MyGUI::Widget* _sender) -{ - if(mPageNumber > 0) - { - std::string prevSound = "book page"; - MWBase::Environment::get().getSoundManager()->playSound (prevSound, 1.0, 1.0); - mPageNumber = mPageNumber - 1; - displayLeftText(mLeftPages[mPageNumber]); - displayRightText(mRightPages[mPageNumber]); - } -} +// glue the implementation to the interface +IJournalWindow * MWGui::IJournalWindow::create () { return new JournalWindow (); } diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 27bf608e18..8f734dc244 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -1,39 +1,21 @@ #ifndef MWGUI_JOURNAL_H #define MWGUI_JOURNAL_H -#include - -#include "windowbase.hpp" -#include "imagebutton.hpp" +namespace MWBase { class WindowManager; } namespace MWGui { - class JournalWindow : public WindowBase + struct IJournalWindow { - public: - JournalWindow(); - virtual void open(); + /// construct a new instance of the one JournalWindow implementation + static IJournalWindow * create (); - private: - void displayLeftText(std::string text); - void displayRightText(std::string text); + /// destroy this instance of the JournalWindow implementation + virtual ~IJournalWindow () {}; - - /** - *Called when next/prev button is used. - */ - void notifyNextPage(MyGUI::Widget* _sender); - void notifyPrevPage(MyGUI::Widget* _sender); - - MyGUI::EditBox* mLeftTextWidget; - MyGUI::EditBox* mRightTextWidget; - MWGui::ImageButton* mPrevBtn; - MWGui::ImageButton* mNextBtn; - std::vector mLeftPages; - std::vector mRightPages; - int mPageNumber; //store the number of the current left page + /// show/hide the journal window + virtual void setVisible (bool newValue) = 0; }; - } #endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2171beaffb..6b38042a67 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -145,7 +145,7 @@ namespace MWGui mMap = new MapWindow(cacheDir); mStatsWindow = new StatsWindow(); mConsole = new Console(w,h, consoleOnlyScripts); - mJournal = new JournalWindow(); + mJournal = IJournalWindow::create(); mMessageBoxManager = new MessageBoxManager(); mInventoryWindow = new InventoryWindow(mDragAndDrop); mTradeWindow = new TradeWindow(); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 652ad870f3..43951d46a8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -51,7 +51,7 @@ namespace MWGui class MainMenu; class StatsWindow; class InventoryWindow; - class JournalWindow; + class IJournalWindow; class CharacterCreation; class DragAndDrop; class ToolTips; @@ -253,7 +253,7 @@ namespace MWGui StatsWindow *mStatsWindow; MessageBoxManager *mMessageBoxManager; Console *mConsole; - JournalWindow* mJournal; + IJournalWindow* mJournal; DialogueWindow *mDialogueWindow; ContainerWindow *mContainerWindow; DragAndDrop* mDragAndDrop; From 85595245abb777dc038785a42afbb3781e570238 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 27 Jan 2013 01:00:11 -0800 Subject: [PATCH 05/50] Updated journal layout to match Bethesda's version more closely --- files/mygui/openmw_journal.layout | 94 +++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index fdf82e4dee..addd4296d7 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -2,24 +2,84 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 75757cb675f91b3e010221cb9687842c70f89009 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 27 Jan 2013 10:43:41 -0800 Subject: [PATCH 06/50] Created a class to represent a stream of UTF8 characters. --- components/misc/utf8stream.hpp | 115 +++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 components/misc/utf8stream.hpp diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp new file mode 100644 index 0000000000..a491ed0824 --- /dev/null +++ b/components/misc/utf8stream.hpp @@ -0,0 +1,115 @@ +#ifndef MISC_UTF8ITER_HPP +#define MISC_UTF8ITER_HPP + +#include + +class utf8_stream +{ +public: + + typedef uint32_t unicode_char; + typedef unsigned char const * point; + + static const unicode_char sBadChar = 0xFFFFFFFF; + + utf8_stream (point begin, point end) : + cur (begin), nxt (begin), end (end) + { + } + + utf8_stream (std::pair range) : + cur (range.first), nxt (range.first), end (range.second) + { + } + + bool eof () const + { + return cur == end; + } + + point current () const + { + return cur; + } + + unicode_char peek () + { + if (cur == nxt) + next (); + return val; + } + + unicode_char consume () + { + if (cur == nxt) + next (); + cur = nxt; + return val; + } + + static std::pair decode (point cur, point end) + { + if ((*cur & 0x80) == 0) + { + unicode_char chr = *cur++; + + return std::make_pair (chr, cur); + } + + int octets; + unicode_char chr; + + boost::tie (octets, chr) = octet_count (*cur++); + + if (octets > 5) + return std::make_pair (sBadChar, cur); + + auto eoc = cur + octets; + + if (eoc > end) + return std::make_pair (sBadChar, cur); + + while (cur != eoc) + { + if ((*cur & 0xC0) != 0x80) // check continuation mark + return std::make_pair (sBadChar, cur);; + + chr = (chr << 6) | unicode_char ((*cur++) & 0x3F); + } + + return std::make_pair (chr, cur); + } + +private: + + static std::pair octet_count (unsigned char octet) + { + int octets; + + unsigned char mark = 0xC0; + unsigned char mask = 0xE0; + + for (octets = 1; octets <= 5; ++octets) + { + if ((octet & mask) == mark) + break; + + mark = (mark >> 1) | 0x80; + mask = (mask >> 1) | 0x80; + } + + return std::make_pair (octets, octet & ~mask); + } + + void next () + { + boost::tie (val, nxt) = decode (nxt, end); + } + + point cur; + point nxt; + point end; + unicode_char val; +}; + +#endif \ No newline at end of file From 55ca037411de9f669da282c1c86203862e74b029 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 27 Jan 2013 11:56:00 -0800 Subject: [PATCH 07/50] Created a MyGUI widget to present a page of formatted text. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/bookpage.cpp | 1204 ++++++++++++++++++++++++ apps/openmw/mwgui/bookpage.hpp | 120 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 2 + 4 files changed, 1327 insertions(+), 1 deletion(-) create mode 100644 apps/openmw/mwgui/bookpage.cpp create mode 100644 apps/openmw/mwgui/bookpage.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index f4fdcb390e..fcd2c14500 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -31,7 +31,7 @@ add_openmw_dir (mwgui confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons - merchantrepair repair soulgemdialog companionwindow + merchantrepair repair soulgemdialog companionwindow bookpage ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp new file mode 100644 index 0000000000..4007dc1f19 --- /dev/null +++ b/apps/openmw/mwgui/bookpage.cpp @@ -0,0 +1,1204 @@ +#include "bookpage.hpp" + +#include "MyGUI_FontManager.h" +#include "MyGUI_RenderItem.h" +#include "MyGUI_RenderManager.h" +#include "MyGUI_TextureUtility.h" +#include "MyGUI_FactoryManager.h" + +#include + +#include + +namespace MWGui +{ + struct TypesetBook; + struct PageDisplay; + struct BookPage; +} + +using namespace MyGUI; +using namespace MWGui; + +static bool ucs_space (int code_point); +static bool ucs_line_break (int code_point); +static bool ucs_breaking_space (int code_point); + +struct IBookTypesetter::IStyle { virtual ~IStyle () {} }; + +struct MWGui::TypesetBook : ITypesetBook +{ + typedef std::vector content; + typedef std::list contents; + typedef utf8_stream::point utf8_point; + typedef std::pair range; + + struct style : IBookTypesetter::IStyle + { + IFont* Font; + Colour HotColour; + Colour ActiveColour; + Colour NormalColour; + interactive_id InteractiveId; + + bool match (IFont* tstFont, Colour tstHotColour, Colour tstActiveColour, Colour tstNormalColour, intptr_t tstInteractiveId) + { + return (Font == tstFont) && + partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); + } + + bool match (char const * tstFont, Colour tstHotColour, Colour tstActiveColour, Colour tstNormalColour, intptr_t tstInteractiveId) + { + return (Font->getResourceName () == tstFont) && + partal_match (tstHotColour, tstActiveColour, tstNormalColour, tstInteractiveId); + } + + bool partal_match (Colour tstHotColour, Colour tstActiveColour, Colour tstNormalColour, intptr_t tstInteractiveId) + { + return + (HotColour == tstHotColour ) && + (ActiveColour == tstActiveColour ) && + (NormalColour == tstNormalColour ) && + (InteractiveId == tstInteractiveId ) ; + } + }; + + typedef std::list