1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-19 21:11:32 +00:00

Merge branch 'master' into globals

This commit is contained in:
Marc Zinnschlag 2013-01-08 11:32:53 +01:00
commit c0fb46384c
72 changed files with 3588 additions and 1220 deletions

View file

@ -37,8 +37,8 @@ option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF)
# Sound source selection # Sound source selection
option(USE_FFMPEG "use ffmpeg for sound" OFF) option(USE_FFMPEG "use ffmpeg for sound" ON)
option(USE_AUDIERE "use audiere for sound" OFF) option(USE_AUDIERE "use audiere for sound" ON)
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON) option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
# OS X deployment # OS X deployment
@ -137,30 +137,54 @@ set(OPENMW_LIBS ${OENGINE_ALL})
set(OPENMW_LIBS_HEADER) set(OPENMW_LIBS_HEADER)
# Sound setup # Sound setup
set(GOT_SOUND_INPUT 0)
set(SOUND_INPUT_INCLUDES "") set(SOUND_INPUT_INCLUDES "")
set(SOUND_INPUT_LIBRARY "") set(SOUND_INPUT_LIBRARY "")
set(SOUND_DEFINE "") set(SOUND_DEFINE "")
if (USE_FFMPEG) if (USE_FFMPEG)
find_package(FFMPEG REQUIRED) set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIR}) find_package(FFmpeg)
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) if (FFMPEG_FOUND)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS})
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES})
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG)
set(GOT_SOUND_INPUT 1)
endif (FFMPEG_FOUND)
endif (USE_FFMPEG) endif (USE_FFMPEG)
if (USE_AUDIERE) if (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
find_package(Audiere REQUIRED) find_package(Audiere)
if (AUDIERE_FOUND)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR}) set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY}) set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY})
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE)
endif (USE_AUDIERE) set(GOT_SOUND_INPUT 1)
endif (AUDIERE_FOUND)
endif (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
if (USE_MPG123) if (USE_MPG123 AND NOT GOT_SOUND_INPUT)
find_package(MPG123 REQUIRED) find_package(MPG123 REQUIRED)
find_package(SNDFILE REQUIRED) find_package(SNDFILE REQUIRED)
if (MPG123_FOUND AND SNDFILE_FOUND)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR}) set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY}) set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
endif (USE_MPG123) set(GOT_SOUND_INPUT 1)
endif (MPG123_FOUND AND SNDFILE_FOUND)
endif (USE_MPG123 AND NOT GOT_SOUND_INPUT)
if (NOT GOT_SOUND_INPUT)
message(WARNING "--------------------")
message(WARNING "Failed to find any sound input packages")
message(WARNING "--------------------")
endif (NOT GOT_SOUND_INPUT)
if (NOT FFMPEG_FOUND)
message(WARNING "--------------------")
message(WARNING "FFmpeg not found, video playback will be disabled")
message(WARNING "--------------------")
endif (NOT FFMPEG_FOUND)
# Platform specific # Platform specific
if (WIN32) if (WIN32)
@ -534,7 +558,9 @@ if (WIN32)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif (BUILD_LAUNCHER) endif (BUILD_LAUNCHER)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
if (BUILD_ESMTOOL)
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS})
endif (BUILD_ESMTOOL)
endif(MSVC) endif(MSVC)
# Same for MinGW # Same for MinGW

View file

@ -165,23 +165,12 @@ bool parseOptions (int argc, char** argv, Arguments &info)
// Font encoding settings // Font encoding settings
info.encoding = variables["encoding"].as<std::string>(); info.encoding = variables["encoding"].as<std::string>();
if (info.encoding == "win1250") if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252")
{
std::cout << "Using Central and Eastern European font encoding." << std::endl;
}
else if (info.encoding == "win1251")
{
std::cout << "Using Cyrillic font encoding." << std::endl;
}
else
{
if(info.encoding != "win1252")
{ {
std::cout << info.encoding << " is not a valid encoding option." << std::endl; std::cout << info.encoding << " is not a valid encoding option." << std::endl;
info.encoding = "win1252"; info.encoding = "win1252";
} }
std::cout << "Using default (English) font encoding." << std::endl; std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl;
}
return true; return true;
} }
@ -262,7 +251,8 @@ void printRaw(ESM::ESMReader &esm)
int load(Arguments& info) int load(Arguments& info)
{ {
ESM::ESMReader& esm = info.reader; ESM::ESMReader& esm = info.reader;
esm.setEncoding(ToUTF8::calculateEncoding(info.encoding)); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
esm.setEncoder(&encoder);
std::string filename = info.filename; std::string filename = info.filename;
std::cout << "Loading file: " << filename << std::endl; std::cout << "Loading file: " << filename << std::endl;
@ -432,7 +422,8 @@ int clone(Arguments& info)
std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl; std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl;
ESM::ESMWriter& esm = info.writer; ESM::ESMWriter& esm = info.writer;
esm.setEncoding(info.encoding); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
esm.setEncoder(&encoder);
esm.setAuthor(info.data.author); esm.setAuthor(info.data.author);
esm.setDescription(info.data.description); esm.setDescription(info.data.description);
esm.setVersion(info.data.version); esm.setVersion(info.data.version);

View file

@ -272,7 +272,8 @@ void DataFilesModel::addMasters(const QString &path)
foreach (const QString &path, dir.entryList()) { foreach (const QString &path, dir.entryList()) {
try { try {
ESM::ESMReader fileReader; ESM::ESMReader fileReader;
fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString()));
fileReader.setEncoder(&encoder);
fileReader.open(dir.absoluteFilePath(path).toStdString()); fileReader.open(dir.absoluteFilePath(path).toStdString());
ESM::ESMReader::MasterList mlist = fileReader.getMasters(); ESM::ESMReader::MasterList mlist = fileReader.getMasters();
@ -335,7 +336,8 @@ void DataFilesModel::addPlugins(const QString &path)
try { try {
ESM::ESMReader fileReader; ESM::ESMReader fileReader;
fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString()));
fileReader.setEncoder(&encoder);
fileReader.open(dir.absoluteFilePath(path).toStdString()); fileReader.open(dir.absoluteFilePath(path).toStdString());
ESM::ESMReader::MasterList mlist = fileReader.getMasters(); ESM::ESMReader::MasterList mlist = fileReader.getMasters();

View file

@ -649,11 +649,12 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) {
std::string section(""); std::string section("");
MwIniImporter::multistrmap map; MwIniImporter::multistrmap map;
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str()); boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
ToUTF8::Utf8Encoder encoder(mEncoding);
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
line = toUTF8(line); line = encoder.getUtf8(line);
// unify Unix-style and Windows file ending // unify Unix-style and Windows file ending
if (!(line.empty()) && (line[line.length()-1]) == '\r') { if (!(line.empty()) && (line[line.length()-1]) == '\r') {
@ -829,14 +830,6 @@ void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_
} }
} }
std::string MwIniImporter::toUTF8(const std::string &str) {
char *ptr = ToUTF8::getBuffer(str.length());
strncpy(ptr, str.c_str(), str.length());
// Convert to UTF8 and return
return ToUTF8::getUtf8(mEncoding);
}
void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding) void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)
{ {
mEncoding = encoding; mEncoding = encoding;

View file

@ -8,7 +8,7 @@
#include <vector> #include <vector>
#include <exception> #include <exception>
#include "../../components/to_utf8/to_utf8.hpp" #include <components/to_utf8/to_utf8.hpp>
class MwIniImporter { class MwIniImporter {
public: public:

View file

@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap occlusionquery terrain terrainmaterial water shadows renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
compositors characterpreview externalrendering globalmap compositors characterpreview externalrendering globalmap videoplayer
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

View file

@ -331,11 +331,15 @@ void OMW::Engine::go()
// cursor replacer (converts the cursor from the bsa so they can be used by mygui) // cursor replacer (converts the cursor from the bsa so they can be used by mygui)
MWGui::CursorReplace replacer; MWGui::CursorReplace replacer;
// Create encoder
ToUTF8::Utf8Encoder encoder (mEncoding);
// Create the world // Create the world
mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster,
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap));
//Load translation data //Load translation data
mTranslationDataStorage.setEncoder(&encoder);
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster); mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster);
// Create window manager - this manages all the MW-specific GUI windows // Create window manager - this manages all the MW-specific GUI windows
@ -494,7 +498,6 @@ void OMW::Engine::showFPS(int level)
void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
{ {
mEncoding = encoding; mEncoding = encoding;
mTranslationDataStorage.setEncoding (encoding);
} }
void OMW::Engine::setFallbackValues(std::map<std::string,std::string> fallbackMap) void OMW::Engine::setFallbackValues(std::map<std::string,std::string> fallbackMap)

View file

@ -128,12 +128,6 @@ float MWBase::Environment::getFrameDuration() const
void MWBase::Environment::cleanup() void MWBase::Environment::cleanup()
{ {
delete mInputManager;
mInputManager = 0;
delete mSoundManager;
mSoundManager = 0;
delete mMechanicsManager; delete mMechanicsManager;
mMechanicsManager = 0; mMechanicsManager = 0;
@ -146,11 +140,17 @@ void MWBase::Environment::cleanup()
delete mScriptManager; delete mScriptManager;
mScriptManager = 0; mScriptManager = 0;
delete mWorld;
mWorld = 0;
delete mSoundManager;
mSoundManager = 0;
delete mWindowManager; delete mWindowManager;
mWindowManager = 0; mWindowManager = 0;
delete mWorld; delete mInputManager;
mWorld = 0; mInputManager = 0;
} }
const MWBase::Environment& MWBase::Environment::get() const MWBase::Environment& MWBase::Environment::get()

View file

@ -22,6 +22,8 @@ namespace MWWorld
namespace MWSound namespace MWSound
{ {
class Sound; class Sound;
class Sound_Decoder;
typedef boost::shared_ptr<Sound_Decoder> DecoderPtr;
} }
namespace MWBase namespace MWBase
@ -32,7 +34,7 @@ namespace MWBase
class SoundManager class SoundManager
{ {
public: public:
/* These must all fit together */
enum PlayMode { enum PlayMode {
Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */
Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */
@ -41,6 +43,13 @@ namespace MWBase
* but do not keep it updated (the sound will not move with * but do not keep it updated (the sound will not move with
* the object and will not stop when the object is deleted. */ * the object and will not stop when the object is deleted. */
}; };
enum PlayType {
Play_TypeSfx = 1<<3, /* Normal SFX sound */
Play_TypeVoice = 1<<4, /* Voice sound */
Play_TypeMusic = 1<<5, /* Music track */
Play_TypeMovie = 1<<6, /* Movie audio track */
Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie
};
private: private:
@ -89,12 +98,15 @@ namespace MWBase
virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0;
///< Stop an actor speaking ///< Stop an actor speaking
virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0;
///< Play a 2D audio track, using a custom decoder
virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch,
int mode=Play_Normal) = 0; PlayMode mode=Play_Normal) = 0;
///< Play a sound, independently of 3D-position ///< Play a sound, independently of 3D-position
virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, int mode=Play_Normal) = 0; float volume, float pitch, PlayMode mode=Play_Normal) = 0;
///< Play a sound from an object ///< Play a sound from an object
virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0; virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0;
@ -112,15 +124,16 @@ namespace MWBase
virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0;
///< Is the given sound currently playing on the given object? ///< Is the given sound currently playing on the given object?
virtual void pauseSounds(int types=Play_TypeMask) = 0;
///< Pauses all currently playing sounds, including music.
virtual void resumeSounds(int types=Play_TypeMask) = 0;
///< Resumes all previously paused sounds.
virtual void update(float duration) = 0; virtual void update(float duration) = 0;
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0;
}; };
inline int operator|(SoundManager::PlayMode a, SoundManager::PlayMode b)
{ return static_cast<int> (a) | static_cast<int> (b); }
inline int operator&(SoundManager::PlayMode a, SoundManager::PlayMode b)
{ return static_cast<int> (a) & static_cast<int> (b); }
} }
#endif #endif

View file

@ -306,6 +306,11 @@ namespace MWBase
/// 1 - only waiting \n /// 1 - only waiting \n
/// 2 - player is underwater \n /// 2 - player is underwater \n
/// 3 - enemies are nearby (not implemented) /// 3 - enemies are nearby (not implemented)
/// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void stopVideo() = 0;
}; };
} }

View file

@ -213,7 +213,9 @@ namespace MWGui
void LoadingScreen::changeWallpaper () void LoadingScreen::changeWallpaper ()
{ {
std::vector<std::string> splash; if (mResources.isNull ())
{
mResources = Ogre::StringVectorPtr (new Ogre::StringVector);
Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false); Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false);
for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it) for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it)
@ -224,11 +226,13 @@ namespace MWGui
boost::to_lower(start); boost::to_lower(start);
if (start == "splash") if (start == "splash")
splash.push_back (*it); mResources->push_back (*it);
} }
if (splash.size()) }
if (mResources->size())
{ {
std::string randomSplash = splash[rand() % splash.size()]; std::string randomSplash = mResources->at (rand() % mResources->size());
Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General");
mBackgroundImage->setImageTexture (randomSplash); mBackgroundImage->setImageTexture (randomSplash);

View file

@ -42,6 +42,7 @@ namespace MWGui
Ogre::Rectangle2D* mRectangle; Ogre::Rectangle2D* mRectangle;
Ogre::MaterialPtr mBackgroundMaterial; Ogre::MaterialPtr mBackgroundMaterial;
Ogre::StringVectorPtr mResources;
bool mLoadingOn; bool mLoadingOn;

View file

@ -45,7 +45,9 @@ namespace MWGui
GM_Loading, GM_Loading,
GM_LoadingWallpaper, GM_LoadingWallpaper,
GM_QuickKeysMenu GM_QuickKeysMenu,
GM_Video
}; };
// Windows shown in inventory mode // Windows shown in inventory mode

View file

@ -230,6 +230,8 @@ WindowManager::~WindowManager()
delete mSpellCreationDialog; delete mSpellCreationDialog;
delete mEnchantingDialog; delete mEnchantingDialog;
delete mTrainingWindow; delete mTrainingWindow;
delete mCountDialog;
delete mQuickKeysMenu;
cleanupGarbage(); cleanupGarbage();
@ -407,6 +409,10 @@ void WindowManager::updateVisible()
case GM_Loading: case GM_Loading:
MyGUI::PointerManager::getInstance().setVisible(false); MyGUI::PointerManager::getInstance().setVisible(false);
break; break;
case GM_Video:
MyGUI::PointerManager::getInstance().setVisible(false);
mHud->setVisible(false);
break;
default: default:
// Unsupported mode, switch back to game // Unsupported mode, switch back to game
break; break;

View file

@ -515,6 +515,8 @@ namespace MWInput
{ {
if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings))
mWindows.popGuiMode(); mWindows.popGuiMode();
else if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video)
MWBase::Environment::get().getWorld ()->stopVideo ();
else else
mWindows.pushGuiMode (MWGui::GM_MainMenu); mWindows.pushGuiMode (MWGui::GM_MainMenu);
} }

View file

@ -17,7 +17,7 @@
using namespace MWRender; using namespace MWRender;
// These are the Morrowind.ini defaults /// \todo Replace these, once fallback values from the ini file are available.
float Objects::lightLinearValue = 3; float Objects::lightLinearValue = 3;
float Objects::lightLinearRadiusMult = 1; float Objects::lightLinearRadiusMult = 1;
@ -31,7 +31,6 @@ int Objects::uniqueID = 0;
void Objects::clearSceneNode (Ogre::SceneNode *node) void Objects::clearSceneNode (Ogre::SceneNode *node)
{ {
/// \todo This should probably be moved into OpenEngine at some point.
for (int i=node->numAttachedObjects()-1; i>=0; --i) for (int i=node->numAttachedObjects()-1; i>=0; --i)
{ {
Ogre::MovableObject *object = node->getAttachedObject (i); Ogre::MovableObject *object = node->getAttachedObject (i);
@ -235,8 +234,9 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f
else else
info.type = LT_Normal; info.type = LT_Normal;
// random starting phase for the animation // randomize lights animations
info.time = Ogre::Math::RangeRandom(0, 2 * Ogre::Math::PI); info.time = Ogre::Math::RangeRandom(-500, +500);
info.phase = Ogre::Math::RangeRandom(-500, +500);
// adjust the lights depending if we're in an interior or exterior cell // adjust the lights depending if we're in an interior or exterior cell
// quadratic means the light intensity falls off quite fast, resulting in a // quadratic means the light intensity falls off quite fast, resulting in a
@ -373,6 +373,46 @@ void Objects::disableLights()
} }
} }
namespace MWRender
{
namespace Pulse
{
static float amplitude (float phase)
{
return sin (phase);
}
}
namespace Flicker
{
static const float fa = 0.785398f;
static const float fb = 1.17024f;
static const float tdo = 0.94f;
static const float tdm = 2.48f;
static const float f [3] = { 1.5708f, 4.18774f, 5.19934f };
static const float o [3] = { 0.804248f, 2.11115f, 3.46832f };
static const float m [3] = { 1.0f, 0.785f, 0.876f };
static const float s = 0.394f;
static const float phase_wavelength = 120.0f * 3.14159265359f / fa;
static float frequency (float x)
{
return tdo + tdm * sin (fa * x);
}
static float amplitude (float x)
{
float v = 0.0f;
for (int i = 0; i < 3; ++i)
v += sin (fb*x*f[i] + o[1])*m[i];
return v * s;
}
}
}
void Objects::update(const float dt) void Objects::update(const float dt)
{ {
std::vector<LightInfo>::iterator it = mLights.begin(); std::vector<LightInfo>::iterator it = mLights.begin();
@ -382,77 +422,63 @@ void Objects::update(const float dt)
{ {
Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name); Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name);
// Light animation (pulse & flicker) float brightness;
it->time += dt; float cycle_time;
const float phase = std::fmod(static_cast<double> (it->time), static_cast<double>(32 * 2 * Ogre::Math::PI)) * 20; float time_distortion;
float pulseConstant;
if ((it->type == LT_Pulse) && (it->type == LT_PulseSlow))
{
cycle_time = 2 * Ogre::Math::PI;
time_distortion = 20.0f;
}
else
{
cycle_time = 500.0f;
it->phase = fmod (it->phase + dt, Flicker::phase_wavelength);
time_distortion = Flicker::frequency (it->phase);
}
it->time += it->dir*dt*time_distortion;
if (it->dir > 0 && it->time > +cycle_time)
{
it->dir = -1.0f;
it->time = +2*cycle_time - it->time;
}
if (it->dir < 0 && it->time < -cycle_time)
{
it->dir = +1.0f;
it->time = -2*cycle_time - it->time;
}
static const float fast = 4.0f/1.0f;
static const float slow = 1.0f/1.0f;
// These formulas are just guesswork, but they work pretty well // These formulas are just guesswork, but they work pretty well
if (it->type == LT_Normal) if (it->type == LT_Normal)
{ {
// Less than 1/255 light modifier for a constant light: // Less than 1/255 light modifier for a constant light:
pulseConstant = (const float)(1.0 + sin(phase) / 255.0 ); brightness = (const float)(1.0 + Flicker::amplitude(it->time*slow) / 255.0 );
} }
else if (it->type == LT_Flicker) else if (it->type == LT_Flicker)
{ {
// Let's do a 50% -> 100% sine wave pulse over 1 second: brightness = (const float)(0.75 + Flicker::amplitude(it->time*fast) * 0.25);
// This is 75% +/- 25%
pulseConstant = (const float)(0.75 + sin(phase) * 0.25);
// Then add a 25% flicker variation:
it->resetTime -= dt;
if (it->resetTime < 0)
{
it->flickerVariation = (rand() % 1000) / 1000 * 0.25;
it->resetTime = 0.5;
}
if (it->resetTime > 0.25)
{
pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime * 2.0f) + pulseConstant * it->resetTime * 2.0f;
}
else
{
pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime * 2.0f) + pulseConstant * (1-it->resetTime * 2.0f);
}
} }
else if (it->type == LT_FlickerSlow) else if (it->type == LT_FlickerSlow)
{ {
// Let's do a 50% -> 100% sine wave pulse over 1 second: brightness = (const float)(0.75 + Flicker::amplitude(it->time*slow) * 0.25);
// This is 75% +/- 25%
pulseConstant = (const float)(0.75 + sin(phase / 4.0) * 0.25);
// Then add a 25% flicker variation:
it->resetTime -= dt;
if (it->resetTime < 0)
{
it->flickerVariation = (rand() % 1000) / 1000 * 0.25;
it->resetTime = 0.5;
}
if (it->resetTime > 0.5)
{
pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime) + pulseConstant * it->resetTime;
}
else
{
pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime) + pulseConstant * (1-it->resetTime);
}
} }
else if (it->type == LT_Pulse) else if (it->type == LT_Pulse)
{ {
// Let's do a 75% -> 125% sine wave pulse over 1 second: brightness = (const float)(1.0 + Pulse::amplitude (it->time*fast) * 0.25);
// This is 100% +/- 25%
pulseConstant = (const float)(1.0 + sin(phase) * 0.25);
} }
else if (it->type == LT_PulseSlow) else if (it->type == LT_PulseSlow)
{ {
// Let's do a 75% -> 125% sine wave pulse over 1 second: brightness = (const float)(1.0 + Pulse::amplitude (it->time*slow) * 0.25);
// This is 100% +/- 25%
pulseConstant = (const float)(1.0 + sin(phase / 4.0) * 0.25);
} }
else else
assert(0 && "Invalid light type"); assert(0 && "Invalid light type");
light->setDiffuseColour( it->colour * pulseConstant ); light->setDiffuseColour(it->colour * brightness);
++it; ++it;
} }
@ -476,8 +502,7 @@ void Objects::rebuildStaticGeometry()
} }
} }
void void Objects::updateObjectCell(const MWWorld::Ptr &ptr)
Objects::updateObjectCell(const MWWorld::Ptr &ptr)
{ {
Ogre::SceneNode *node; Ogre::SceneNode *node;
MWWorld::CellStore *newCell = ptr.getCell(); MWWorld::CellStore *newCell = ptr.getCell();

View file

@ -34,16 +34,13 @@ struct LightInfo
LightType type; LightType type;
// Runtime variables // Runtime variables
float flickerVariation; // 25% flicker variation, reset once every 0.5 seconds float dir; // direction time is running...
float flickerSlowVariation; // 25% flicker variation, reset once every 1.0 seconds float time; // current time
float resetTime; float phase; // current phase
long double time;
LightInfo() : LightInfo() :
flickerVariation(0), resetTime(0.5), dir(1.0f), time(0.0f), phase (0.0f),
flickerSlowVariation(0), time(0), interior(true), interior(true), type(LT_Normal), radius(1.0)
type(LT_Normal), radius(1.0)
{ {
} }
}; };

View file

@ -37,6 +37,7 @@
#include "npcanimation.hpp" #include "npcanimation.hpp"
#include "externalrendering.hpp" #include "externalrendering.hpp"
#include "globalmap.hpp" #include "globalmap.hpp"
#include "videoplayer.hpp"
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -160,6 +161,9 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
mVideoPlayer = new VideoPlayer(mRendering.getScene ());
mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video"));
mSun = 0; mSun = 0;
mDebugging = new Debugging(mMwRoot, engine); mDebugging = new Debugging(mMwRoot, engine);
@ -181,7 +185,7 @@ RenderingManager::~RenderingManager ()
delete mOcclusionQuery; delete mOcclusionQuery;
delete mCompositors; delete mCompositors;
delete mWater; delete mWater;
delete mVideoPlayer;
delete mFactory; delete mFactory;
} }
@ -332,6 +336,8 @@ void RenderingManager::update (float duration, bool paused)
} }
mOcclusionQuery->update(duration); mOcclusionQuery->update(duration);
mVideoPlayer->update ();
mRendering.update(duration); mRendering.update(duration);
if(paused) if(paused)
@ -838,6 +844,8 @@ void RenderingManager::windowResized(Ogre::RenderWindow* rw)
mCompositors->recreate(); mCompositors->recreate();
mWater->assignTextures(); mWater->assignTextures();
mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight());
const Settings::CategorySettingVector& changed = Settings::Manager::apply(); const Settings::CategorySettingVector& changed = Settings::Manager::apply();
MWBase::Environment::get().getInputManager()->processChangedSettings(changed); //FIXME MWBase::Environment::get().getInputManager()->processChangedSettings(changed); //FIXME
MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); // FIXME MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); // FIXME
@ -921,4 +929,14 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend
rendering.setup (mRendering.getScene()); rendering.setup (mRendering.getScene());
} }
void RenderingManager::playVideo(const std::string& name, bool allowSkipping)
{
mVideoPlayer->playVideo ("video/" + name, allowSkipping);
}
void RenderingManager::stopVideo()
{
mVideoPlayer->stopVideo ();
}
} // namespace } // namespace

View file

@ -45,6 +45,7 @@ namespace MWRender
class Compositors; class Compositors;
class ExternalRendering; class ExternalRendering;
class GlobalMap; class GlobalMap;
class VideoPlayer;
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener {
@ -195,6 +196,9 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void setupExternalRendering (MWRender::ExternalRendering& rendering); void setupExternalRendering (MWRender::ExternalRendering& rendering);
void playVideo(const std::string& name, bool allowSkipping);
void stopVideo();
protected: protected:
virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowResized(Ogre::RenderWindow* rw);
virtual void windowClosed(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw);
@ -248,6 +252,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
MWRender::Shadows* mShadows; MWRender::Shadows* mShadows;
MWRender::Compositors* mCompositors; MWRender::Compositors* mCompositors;
VideoPlayer* mVideoPlayer;
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
#ifndef VIDEOPLAYER_H
#define VIDEOPLAYER_H
#include <OgreMaterial.h>
namespace Ogre
{
class SceneManager;
class SceneNode;
class Rectangle2D;
}
namespace MWRender
{
struct VideoState;
class VideoPlayer
{
public:
VideoPlayer(Ogre::SceneManager* sceneMgr);
~VideoPlayer();
void playVideo (const std::string& resourceName, bool allowSkipping);
void update();
void close();
void stopVideo();
bool isPlaying();
void setResolution (int w, int h) { mWidth = w; mHeight = h; }
private:
VideoState* mState;
bool mAllowSkipping;
Ogre::SceneManager* mSceneMgr;
Ogre::MaterialPtr mVideoMaterial;
Ogre::Rectangle2D* mRectangle;
Ogre::Rectangle2D* mBackgroundRectangle;
Ogre::SceneNode* mNode;
Ogre::SceneNode* mBackgroundNode;
int mWidth;
int mHeight;
};
}
#endif

View file

@ -301,10 +301,14 @@ op 0x20001ea: LowerRank
op 0x20001eb: LowerRank, explicit op 0x20001eb: LowerRank, explicit
op 0x20001ec: GetPCCrimeLevel op 0x20001ec: GetPCCrimeLevel
op 0x20001ed: SetPCCrimeLevel op 0x20001ed: SetPCCrimeLevel
op 0x20001ee: SetPCCrimeLevel op 0x20001ee: ModPCCrimeLevel
op 0x20001ef: GetCurrentAIPackage op 0x20001ef: GetCurrentAIPackage
op 0x20001f0: GetCurrentAIPackage, explicit reference op 0x20001f0: GetCurrentAIPackage, explicit reference
op 0x20001f1: GetDetected op 0x20001f1: GetDetected
op 0x20001f2: GetDetected, explicit reference op 0x20001f2: GetDetected, explicit reference
op 0x20001f3: AddSoulGem
opcodes 0x20001f3-0x3ffffff unused op 0x20001f4: AddSoulGem, explicit reference
op 0x20001f5: RemoveSoulGem
op 0x20001f6: RemoveSoulGem, explicit reference
op 0x20001f7: PlayBink
opcodes 0x20001f8-0x3ffffff unused

View file

@ -14,6 +14,8 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
@ -25,6 +27,22 @@ namespace MWScript
{ {
namespace Misc namespace Misc
{ {
class OpPlayBink : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
std::string name = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
bool allowSkipping = runtime[0].mInteger;
runtime.pop();
MWBase::Environment::get().getWorld ()->playVideo (name, allowSkipping);
}
};
class OpGetPcSleep : public Interpreter::Opcode0 class OpGetPcSleep : public Interpreter::Opcode0
{ {
public: public:
@ -306,6 +324,65 @@ namespace MWScript
} }
}; };
template<class R>
class OpAddSoulGem : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
std::string creature = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
std::string gem = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
store.get<ESM::Creature>().find(creature); // This line throws an exception if it can't find the creature
MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem);
ref.getPtr().getRefData().setCount (1);
ref.getPtr().getCellRef().mSoul = creature;
MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr());
}
};
template<class R>
class OpRemoveSoulGem : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
std::string soul = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr);
for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)
{
if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul))
{
if (iter->getRefData().getCount() <= 1)
iter->getRefData().setCount (0);
else
iter->getRefData().setCount (iter->getRefData().getCount() - 1);
break;
}
}
}
};
template <class R> template <class R>
class OpGetAttacked : public Interpreter::Opcode0 class OpGetAttacked : public Interpreter::Opcode0
{ {
@ -414,6 +491,10 @@ namespace MWScript
const int opcodeGetLockedExplicit = 0x20001c8; const int opcodeGetLockedExplicit = 0x20001c8;
const int opcodeGetEffect = 0x20001cf; const int opcodeGetEffect = 0x20001cf;
const int opcodeGetEffectExplicit = 0x20001d0; const int opcodeGetEffectExplicit = 0x20001d0;
const int opcodeAddSoulGem = 0x20001f3;
const int opcodeAddSoulGemExplicit = 0x20001f4;
const int opcodeRemoveSoulGem = 0x20001f5;
const int opcodeRemoveSoulGemExplicit = 0x20001f6;
const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttacked = 0x20001d3;
const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetAttackedExplicit = 0x20001d4;
const int opcodeGetWeaponDrawn = 0x20001d7; const int opcodeGetWeaponDrawn = 0x20001d7;
@ -425,6 +506,8 @@ namespace MWScript
const int opcodeSetDeleteExplicit = 0x20001e6; const int opcodeSetDeleteExplicit = 0x20001e6;
const int opcodeGetSquareRoot = 0x20001e7; const int opcodeGetSquareRoot = 0x20001e7;
const int opcodePlayBink = 0x20001f7;
void registerExtensions (Compiler::Extensions& extensions) void registerExtensions (Compiler::Extensions& extensions)
{ {
extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("xbox", 'l', "", opcodeXBox);
@ -450,8 +533,11 @@ namespace MWScript
extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode);
extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep);
extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc);
extensions.registerInstruction ("playbink", "Sl", opcodePlayBink);
extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit);
extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit);
extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit);
extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit);
extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit);
extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit);
extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit);
@ -481,10 +567,15 @@ namespace MWScript
interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode);
interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep); interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep);
interpreter.installSegment5 (opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (opcodeWakeUpPc, new OpWakeUpPc);
interpreter.installSegment5 (opcodePlayBink, new OpPlayBink);
interpreter.installSegment5 (opcodeGetLocked, new OpGetLocked<ImplicitRef>); interpreter.installSegment5 (opcodeGetLocked, new OpGetLocked<ImplicitRef>);
interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked<ExplicitRef>); interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked<ExplicitRef>);
interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect<ImplicitRef>); interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect<ImplicitRef>);
interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect<ExplicitRef>); interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect<ExplicitRef>);
interpreter.installSegment5 (opcodeAddSoulGem, new OpAddSoulGem<ImplicitRef>);
interpreter.installSegment5 (opcodeAddSoulGemExplicit, new OpAddSoulGem<ExplicitRef>);
interpreter.installSegment5 (opcodeRemoveSoulGem, new OpRemoveSoulGem<ImplicitRef>);
interpreter.installSegment5 (opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem<ExplicitRef>);
interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked<ImplicitRef>); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked<ImplicitRef>);
interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked<ExplicitRef>); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked<ExplicitRef>);
interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn<ImplicitRef>); interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn<ImplicitRef>);

View file

@ -118,7 +118,8 @@ namespace MWScript
std::string sound = runtime.getStringLiteral (runtime[0].mInteger); std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : 0); MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop :
MWBase::SoundManager::Play_Normal);
} }
}; };
@ -144,7 +145,8 @@ namespace MWScript
Interpreter::Type_Float pitch = runtime[0].mFloat; Interpreter::Type_Float pitch = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : 0); MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop :
MWBase::SoundManager::Play_Normal);
} }
}; };

View file

@ -53,6 +53,9 @@ public:
: mStream(stream), refs(1) : mStream(stream), refs(1)
{ } { }
virtual ~OgreFile() { } virtual ~OgreFile() { }
Ogre::String getName()
{ return mStream->getName(); }
}; };
@ -60,7 +63,7 @@ void Audiere_Decoder::open(const std::string &fname)
{ {
close(); close();
audiere::FilePtr file(new OgreFile(mResourceMgr.openResource(fname))); mSoundFile = audiere::FilePtr(new OgreFile(mResourceMgr.openResource(fname)));
mSoundSource = audiere::OpenSampleSource(file); mSoundSource = audiere::OpenSampleSource(file);
int channels, srate; int channels, srate;
@ -86,9 +89,15 @@ void Audiere_Decoder::open(const std::string &fname)
void Audiere_Decoder::close() void Audiere_Decoder::close()
{ {
mSoundFile = NULL;
mSoundSource = NULL; mSoundSource = NULL;
} }
std::string Audiere_Decoder::getName()
{
return mSoundFile->getName();
}
void Audiere_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) void Audiere_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{ {
*samplerate = mSampleRate; *samplerate = mSampleRate;
@ -108,6 +117,11 @@ void Audiere_Decoder::rewind()
mSoundSource->reset(); mSoundSource->reset();
} }
size_t Audiere_Decoder::getSampleOffset()
{
return 0;
}
Audiere_Decoder::Audiere_Decoder() Audiere_Decoder::Audiere_Decoder()
{ {
} }

View file

@ -12,6 +12,7 @@ namespace MWSound
{ {
class Audiere_Decoder : public Sound_Decoder class Audiere_Decoder : public Sound_Decoder
{ {
audiere::FilePtr mSoundFile;
audiere::SampleSourcePtr mSoundSource; audiere::SampleSourcePtr mSoundSource;
int mSampleRate; int mSampleRate;
SampleType mSampleType; SampleType mSampleType;
@ -20,10 +21,12 @@ namespace MWSound
virtual void open(const std::string &fname); virtual void open(const std::string &fname);
virtual void close(); virtual void close();
virtual std::string getName();
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes); virtual size_t read(char *buffer, size_t bytes);
virtual void rewind(); virtual void rewind();
virtual size_t getSampleOffset();
Audiere_Decoder& operator=(const Audiere_Decoder &rhs); Audiere_Decoder& operator=(const Audiere_Decoder &rhs);
Audiere_Decoder(const Audiere_Decoder &rhs); Audiere_Decoder(const Audiere_Decoder &rhs);

View file

@ -15,28 +15,6 @@ static void fail(const std::string &msg)
{ throw std::runtime_error("FFmpeg exception: "+msg); } { throw std::runtime_error("FFmpeg exception: "+msg); }
struct PacketList {
AVPacket pkt;
PacketList *next;
};
struct FFmpeg_Decoder::MyStream {
AVCodecContext *mCodecCtx;
int mStreamIdx;
PacketList *mPackets;
char *mDecodedData;
size_t mDecodedDataSize;
FFmpeg_Decoder *mParent;
void clearPackets();
void *getAVAudioData(size_t *length);
size_t readAVAudioData(void *data, size_t length);
};
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
{ {
Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream; Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;
@ -72,166 +50,84 @@ int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)
/* Used by getAV*Data to search for more compressed data, and buffer it in the /* Used by getAV*Data to search for more compressed data, and buffer it in the
* correct stream. It won't buffer data for streams that the app doesn't have a * correct stream. It won't buffer data for streams that the app doesn't have a
* handle for. */ * handle for. */
bool FFmpeg_Decoder::getNextPacket(int streamidx) bool FFmpeg_Decoder::getNextPacket()
{ {
PacketList *packet; if(!mStream)
return false;
packet = (PacketList*)av_malloc(sizeof(*packet)); int stream_idx = mStream - mFormatCtx->streams;
packet->next = NULL; while(av_read_frame(mFormatCtx, &mPacket) >= 0)
next_packet:
while(av_read_frame(mFormatCtx, &packet->pkt) >= 0)
{ {
std::vector<MyStream*>::iterator iter = mStreams.begin(); /* Check if the packet belongs to this stream */
if(stream_idx == mPacket.stream_index)
/* Check each stream the user has a handle for, looking for the one
* this packet belongs to */
while(iter != mStreams.end())
{ {
if((*iter)->mStreamIdx == packet->pkt.stream_index) if((uint64_t)mPacket.pts != AV_NOPTS_VALUE)
{ mNextPts = av_q2d((*mStream)->time_base)*mPacket.pts;
PacketList **last;
last = &(*iter)->mPackets;
while(*last != NULL)
last = &(*last)->next;
*last = packet;
if((*iter)->mStreamIdx == streamidx)
return true; return true;
}
packet = (PacketList*)av_malloc(sizeof(*packet));
packet->next = NULL;
goto next_packet;
}
iter++;
}
/* Free the packet and look for another */ /* Free the packet and look for another */
av_free_packet(&packet->pkt); av_free_packet(&mPacket);
} }
av_free(packet);
return false; return false;
} }
void FFmpeg_Decoder::MyStream::clearPackets() bool FFmpeg_Decoder::getAVAudioData()
{ {
while(mPackets) int got_frame, len;
{
PacketList *self = mPackets;
mPackets = self->next;
av_free_packet(&self->pkt); if((*mStream)->codec->codec_type != AVMEDIA_TYPE_AUDIO)
av_free(self); return false;
}
}
void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) do {
{ if(mPacket.size == 0 && !getNextPacket())
int size; return false;
int len;
if(length) *length = 0;
if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
return NULL;
mDecodedDataSize = 0;
next_packet:
if(!mPackets && !mParent->getNextPacket(mStreamIdx))
return NULL;
/* Decode some data, and check for errors */ /* Decode some data, and check for errors */
size = AVCODEC_MAX_AUDIO_FRAME_SIZE; if((len=avcodec_decode_audio4((*mStream)->codec, mFrame, &got_frame, &mPacket)) < 0)
while((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size, return false;
&mPackets->pkt)) == 0)
{
PacketList *self;
if(size > 0)
break;
/* Packet went unread and no data was given? Drop it and try the next,
* I guess... */
self = mPackets;
mPackets = self->next;
av_free_packet(&self->pkt);
av_free(self);
if(!mPackets)
goto next_packet;
size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
}
if(len < 0)
return NULL;
if(len < mPackets->pkt.size)
{
/* Move the unread data to the front and clear the end bits */ /* Move the unread data to the front and clear the end bits */
int remaining = mPackets->pkt.size - len; int remaining = mPacket.size - len;
memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining); if(remaining <= 0)
memset(&mPackets->pkt.data[remaining], 0, mPackets->pkt.size - remaining); av_free_packet(&mPacket);
mPackets->pkt.size -= len;
}
else else
{ {
PacketList *self; memmove(mPacket.data, &mPacket.data[len], remaining);
av_shrink_packet(&mPacket, remaining);
self = mPackets;
mPackets = self->next;
av_free_packet(&self->pkt);
av_free(self);
} }
} while(got_frame == 0 || mFrame->nb_samples == 0);
mNextPts += (double)mFrame->nb_samples / (double)(*mStream)->codec->sample_rate;
if(size == 0) return true;
goto next_packet;
/* Set the output buffer size */
mDecodedDataSize = size;
if(length) *length = mDecodedDataSize;
return mDecodedData;
} }
size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length)
{ {
size_t dec = 0; size_t dec = 0;
while(dec < length) while(dec < length)
{ {
/* If there's no decoded data, find some */ /* If there's no decoded data, find some */
if(mDecodedDataSize == 0) if(mFramePos >= mFrameSize)
{ {
if(getAVAudioData(NULL) == NULL) if(!getAVAudioData())
break; break;
mFramePos = 0;
mFrameSize = mFrame->nb_samples * (*mStream)->codec->channels *
av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
} }
if(mDecodedDataSize > 0)
{
/* Get the amount of bytes remaining to be written, and clamp to /* Get the amount of bytes remaining to be written, and clamp to
* the amount of decoded data we have */ * the amount of decoded data we have */
size_t rem = length-dec; size_t rem = std::min<size_t>(length-dec, mFrameSize-mFramePos);
if(rem > mDecodedDataSize)
rem = mDecodedDataSize;
/* Copy the data to the app's buffer and increment */ /* Copy the data to the app's buffer and increment */
if(data != NULL) memcpy(data, mFrame->data[0]+mFramePos, rem);
{
memcpy(data, mDecodedData, rem);
data = (char*)data + rem; data = (char*)data + rem;
}
dec += rem; dec += rem;
mFramePos += rem;
/* If there's any decoded data left, move it to the front of the
* buffer for next time */
if(rem < mDecodedDataSize)
memmove(mDecodedData, &mDecodedData[rem], mDecodedDataSize - rem);
mDecodedDataSize -= rem;
}
} }
/* Return the number of bytes we were able to get */ /* Return the number of bytes we were able to get */
@ -239,7 +135,6 @@ size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length)
} }
void FFmpeg_Decoder::open(const std::string &fname) void FFmpeg_Decoder::open(const std::string &fname)
{ {
close(); close();
@ -265,140 +160,155 @@ void FFmpeg_Decoder::open(const std::string &fname)
{ {
if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{ {
std::auto_ptr<MyStream> stream(new MyStream); mStream = &mFormatCtx->streams[j];
stream->mCodecCtx = mFormatCtx->streams[j]->codec;
stream->mStreamIdx = j;
stream->mPackets = NULL;
AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id);
if(!codec)
{
std::stringstream ss("No codec found for id ");
ss << stream->mCodecCtx->codec_id;
fail(ss.str());
}
if(avcodec_open(stream->mCodecCtx, codec) < 0)
fail("Failed to open audio codec " + std::string(codec->long_name));
stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
stream->mDecodedDataSize = 0;
stream->mParent = this;
mStreams.push_back(stream.release());
break; break;
} }
} }
if(mStreams.empty()) if(!mStream)
fail("No audio streams in "+fname); fail("No audio streams in "+fname);
AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id);
if(!codec)
{
std::stringstream ss("No codec found for id ");
ss << (*mStream)->codec->codec_id;
fail(ss.str());
}
if(avcodec_open2((*mStream)->codec, codec, NULL) < 0)
fail("Failed to open audio codec " + std::string(codec->long_name));
mFrame = avcodec_alloc_frame();
} }
catch(std::exception &e) catch(std::exception &e)
{ {
av_close_input_file(mFormatCtx); avformat_close_input(&mFormatCtx);
mFormatCtx = NULL;
throw; throw;
} }
} }
void FFmpeg_Decoder::close() void FFmpeg_Decoder::close()
{ {
while(!mStreams.empty()) if(mStream)
{ avcodec_close((*mStream)->codec);
MyStream *stream = mStreams.front(); mStream = NULL;
stream->clearPackets(); av_free_packet(&mPacket);
avcodec_close(stream->mCodecCtx); av_freep(&mFrame);
av_free(stream->mDecodedData);
delete stream;
mStreams.erase(mStreams.begin());
}
if(mFormatCtx) if(mFormatCtx)
{ {
AVIOContext* context = mFormatCtx->pb; AVIOContext* context = mFormatCtx->pb;
avformat_close_input(&mFormatCtx);
av_free(context); av_free(context);
mFormatCtx->pb = NULL;
av_close_input_file(mFormatCtx);
} }
mFormatCtx = NULL;
mDataStream.setNull(); mDataStream.setNull();
} }
std::string FFmpeg_Decoder::getName()
{
return mFormatCtx->filename;
}
void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{ {
if(mStreams.empty()) if(!mStream)
fail("No audio stream info"); fail("No audio stream info");
MyStream *stream = mStreams[0]; if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8)
if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8)
*type = SampleType_UInt8; *type = SampleType_UInt8;
else if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16)
*type = SampleType_Int16; *type = SampleType_Int16;
else else
fail(std::string("Unsupported sample format: ")+ fail(std::string("Unsupported sample format: ")+
av_get_sample_fmt_name(stream->mCodecCtx->sample_fmt)); av_get_sample_fmt_name((*mStream)->codec->sample_fmt));
if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_MONO)
*chans = ChannelConfig_Mono; *chans = ChannelConfig_Mono;
else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_STEREO)
*chans = ChannelConfig_Stereo; *chans = ChannelConfig_Stereo;
else if(stream->mCodecCtx->channel_layout == 0) else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_QUAD)
*chans = ChannelConfig_Quad;
else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_5POINT1)
*chans = ChannelConfig_5point1;
else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_7POINT1)
*chans = ChannelConfig_7point1;
else if((*mStream)->codec->channel_layout == 0)
{ {
/* Unknown channel layout. Try to guess. */ /* Unknown channel layout. Try to guess. */
if(stream->mCodecCtx->channels == 1) if((*mStream)->codec->channels == 1)
*chans = ChannelConfig_Mono; *chans = ChannelConfig_Mono;
else if(stream->mCodecCtx->channels == 2) else if((*mStream)->codec->channels == 2)
*chans = ChannelConfig_Stereo; *chans = ChannelConfig_Stereo;
else else
{ {
std::stringstream sstr("Unsupported raw channel count: "); std::stringstream sstr("Unsupported raw channel count: ");
sstr << stream->mCodecCtx->channels; sstr << (*mStream)->codec->channels;
fail(sstr.str()); fail(sstr.str());
} }
} }
else else
{ {
char str[1024]; char str[1024];
av_get_channel_layout_string(str, sizeof(str), stream->mCodecCtx->channels, av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels,
stream->mCodecCtx->channel_layout); (*mStream)->codec->channel_layout);
fail(std::string("Unsupported channel layout: ")+str); fail(std::string("Unsupported channel layout: ")+str);
} }
*samplerate = stream->mCodecCtx->sample_rate; *samplerate = (*mStream)->codec->sample_rate;
} }
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
{ {
if(mStreams.empty()) if(!mStream)
fail("No audio streams"); fail("No audio stream");
return readAVAudioData(buffer, bytes);
return mStreams.front()->readAVAudioData(buffer, bytes);
} }
void FFmpeg_Decoder::readAll(std::vector<char> &output) void FFmpeg_Decoder::readAll(std::vector<char> &output)
{ {
if(mStreams.empty()) if(!mStream)
fail("No audio streams"); fail("No audio stream");
MyStream *stream = mStreams.front();
char *inbuf;
size_t got;
while((inbuf=(char*)stream->getAVAudioData(&got)) != NULL && got > 0) while(getAVAudioData())
{
size_t got = mFrame->nb_samples * (*mStream)->codec->channels *
av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
const char *inbuf = reinterpret_cast<char*>(mFrame->data[0]);
output.insert(output.end(), inbuf, inbuf+got); output.insert(output.end(), inbuf, inbuf+got);
}
} }
void FFmpeg_Decoder::rewind() void FFmpeg_Decoder::rewind()
{ {
av_seek_frame(mFormatCtx, -1, 0, 0); int stream_idx = mStream - mFormatCtx->streams;
std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets)); if(av_seek_frame(mFormatCtx, stream_idx, 0, 0) < 0)
fail("Failed to seek in audio stream");
av_free_packet(&mPacket);
mFrameSize = mFramePos = 0;
mNextPts = 0.0;
} }
FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL) size_t FFmpeg_Decoder::getSampleOffset()
{ {
static bool done_init = false; int delay = (mFrameSize-mFramePos) / (*mStream)->codec->channels /
av_get_bytes_per_sample((*mStream)->codec->sample_fmt);
return (int)(mNextPts*(*mStream)->codec->sample_rate) - delay;
}
FFmpeg_Decoder::FFmpeg_Decoder()
: mFormatCtx(NULL)
, mStream(NULL)
, mFrame(NULL)
, mFrameSize(0)
, mFramePos(0)
, mNextPts(0.0)
{
memset(&mPacket, 0, sizeof(mPacket));
/* We need to make sure ffmpeg is initialized. Optionally silence warning /* We need to make sure ffmpeg is initialized. Optionally silence warning
* output from the lib */ * output from the lib */
static bool done_init = false;
if(!done_init) if(!done_init)
{ {
av_register_all(); av_register_all();

View file

@ -10,8 +10,8 @@
#include <stdint.h> #include <stdint.h>
extern "C" extern "C"
{ {
#include <avcodec.h> #include <libavcodec/avcodec.h>
#include <avformat.h> #include <libavformat/avformat.h>
} }
#include "sound_decoder.hpp" #include "sound_decoder.hpp"
@ -22,25 +22,36 @@ namespace MWSound
class FFmpeg_Decoder : public Sound_Decoder class FFmpeg_Decoder : public Sound_Decoder
{ {
AVFormatContext *mFormatCtx; AVFormatContext *mFormatCtx;
AVStream **mStream;
struct MyStream; AVPacket mPacket;
std::vector<MyStream*> mStreams; AVFrame *mFrame;
bool getNextPacket(int streamidx); int mFrameSize;
int mFramePos;
double mNextPts;
bool getNextPacket();
Ogre::DataStreamPtr mDataStream; Ogre::DataStreamPtr mDataStream;
static int readPacket(void *user_data, uint8_t *buf, int buf_size); static int readPacket(void *user_data, uint8_t *buf, int buf_size);
static int writePacket(void *user_data, uint8_t *buf, int buf_size); static int writePacket(void *user_data, uint8_t *buf, int buf_size);
static int64_t seek(void *user_data, int64_t offset, int whence); static int64_t seek(void *user_data, int64_t offset, int whence);
bool getAVAudioData();
size_t readAVAudioData(void *data, size_t length);
virtual void open(const std::string &fname); virtual void open(const std::string &fname);
virtual void close(); virtual void close();
virtual std::string getName();
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes); virtual size_t read(char *buffer, size_t bytes);
virtual void readAll(std::vector<char> &output); virtual void readAll(std::vector<char> &output);
virtual void rewind(); virtual void rewind();
virtual size_t getSampleOffset();
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
FFmpeg_Decoder(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs);

View file

@ -155,6 +155,11 @@ void MpgSnd_Decoder::close()
mDataStream.setNull(); mDataStream.setNull();
} }
std::string MpgSnd_Decoder::getName()
{
return mDataStream->getName();
}
void MpgSnd_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) void MpgSnd_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{ {
if(!mSndFile && !mMpgFile) if(!mSndFile && !mMpgFile)
@ -213,6 +218,11 @@ void MpgSnd_Decoder::rewind()
} }
} }
size_t MpgSnd_Decoder::getSampleOffset()
{
return 0;
}
MpgSnd_Decoder::MpgSnd_Decoder() MpgSnd_Decoder::MpgSnd_Decoder()
: mSndInfo() : mSndInfo()
, mSndFile(NULL) , mSndFile(NULL)

View file

@ -34,11 +34,13 @@ namespace MWSound
virtual void open(const std::string &fname); virtual void open(const std::string &fname);
virtual void close(); virtual void close();
virtual std::string getName();
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
virtual size_t read(char *buffer, size_t bytes); virtual size_t read(char *buffer, size_t bytes);
virtual void readAll(std::vector<char> &output); virtual void readAll(std::vector<char> &output);
virtual void rewind(); virtual void rewind();
virtual size_t getSampleOffset();
MpgSnd_Decoder& operator=(const MpgSnd_Decoder &rhs); MpgSnd_Decoder& operator=(const MpgSnd_Decoder &rhs);
MpgSnd_Decoder(const MpgSnd_Decoder &rhs); MpgSnd_Decoder(const MpgSnd_Decoder &rhs);

View file

@ -61,10 +61,50 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
if(fmtlist[i].chans == chans && fmtlist[i].type == type) if(fmtlist[i].chans == chans && fmtlist[i].type == type)
return fmtlist[i].format; return fmtlist[i].format;
} }
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
static const struct {
char name[32];
ChannelConfig chans;
SampleType type;
} mcfmtlist[] = {
{ "AL_FORMAT_QUAD16", ChannelConfig_Quad, SampleType_Int16 },
{ "AL_FORMAT_QUAD8", ChannelConfig_Quad, SampleType_UInt8 },
{ "AL_FORMAT_51CHN16", ChannelConfig_5point1, SampleType_Int16 },
{ "AL_FORMAT_51CHN8", ChannelConfig_5point1, SampleType_UInt8 },
{ "AL_FORMAT_71CHN16", ChannelConfig_7point1, SampleType_Int16 },
{ "AL_FORMAT_71CHN8", ChannelConfig_7point1, SampleType_UInt8 },
};
static const size_t mcfmtlistsize = sizeof(mcfmtlist)/sizeof(mcfmtlist[0]);
for(size_t i = 0;i < mcfmtlistsize;i++)
{
if(mcfmtlist[i].chans == chans && mcfmtlist[i].type == type)
{
ALenum format = alGetEnumValue(mcfmtlist[i].name);
if(format != 0 && format != -1)
return format;
}
}
}
fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")"); fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")");
return AL_NONE; return AL_NONE;
} }
static ALint getBufferSampleCount(ALuint buf)
{
ALint size, bits, channels;
alGetBufferi(buf, AL_SIZE, &size);
alGetBufferi(buf, AL_BITS, &bits);
alGetBufferi(buf, AL_CHANNELS, &channels);
throwALerror();
return size / channels * 8 / bits;
}
// //
// A streaming OpenAL sound. // A streaming OpenAL sound.
// //
@ -82,19 +122,26 @@ class OpenAL_SoundStream : public Sound
ALsizei mSampleRate; ALsizei mSampleRate;
ALuint mBufferSize; ALuint mBufferSize;
ALuint mSamplesQueued;
DecoderPtr mDecoder; DecoderPtr mDecoder;
volatile bool mIsFinished; volatile bool mIsFinished;
void updateAll(bool local);
OpenAL_SoundStream(const OpenAL_SoundStream &rhs); OpenAL_SoundStream(const OpenAL_SoundStream &rhs);
OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs); OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs);
friend class OpenAL_Output;
public: public:
OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder); OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags);
virtual ~OpenAL_SoundStream(); virtual ~OpenAL_SoundStream();
virtual void stop(); virtual void stop();
virtual bool isPlaying(); virtual bool isPlaying();
virtual double getTimeOffset();
virtual void update(); virtual void update();
void play(); void play();
@ -109,7 +156,7 @@ const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f;
struct OpenAL_Output::StreamThread { struct OpenAL_Output::StreamThread {
typedef std::vector<OpenAL_SoundStream*> StreamVec; typedef std::vector<OpenAL_SoundStream*> StreamVec;
StreamVec mStreams; StreamVec mStreams;
boost::mutex mMutex; boost::recursive_mutex mMutex;
boost::thread mThread; boost::thread mThread;
StreamThread() StreamThread()
@ -170,8 +217,9 @@ private:
}; };
OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder) OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags)
: mOutput(output), mSource(src), mDecoder(decoder), mIsFinished(true) : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags)
, mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true)
{ {
throwALerror(); throwALerror();
@ -189,6 +237,8 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode
mBufferSize = static_cast<ALuint>(sBufferLength*srate); mBufferSize = static_cast<ALuint>(sBufferLength*srate);
mBufferSize = framesToBytes(mBufferSize, chans, type); mBufferSize = framesToBytes(mBufferSize, chans, type);
mOutput.mActiveSounds.push_back(this);
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -209,22 +259,20 @@ OpenAL_SoundStream::~OpenAL_SoundStream()
alGetError(); alGetError();
mDecoder->close(); mDecoder->close();
mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(),
mOutput.mActiveSounds.end(), this));
} }
void OpenAL_SoundStream::play() void OpenAL_SoundStream::play()
{ {
std::vector<char> data(mBufferSize);
alSourceStop(mSource); alSourceStop(mSource);
alSourcei(mSource, AL_BUFFER, 0); alSourcei(mSource, AL_BUFFER, 0);
throwALerror(); throwALerror();
mSamplesQueued = 0;
for(ALuint i = 0;i < sNumBuffers;i++) for(ALuint i = 0;i < sNumBuffers;i++)
{ alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate);
size_t got;
got = mDecoder->read(&data[0], data.size());
alBufferData(mBuffers[i], mFormat, &data[0], got, mSampleRate);
}
throwALerror(); throwALerror();
alSourceQueueBuffers(mSource, sNumBuffers, mBuffers); alSourceQueueBuffers(mSource, sNumBuffers, mBuffers);
@ -243,6 +291,7 @@ void OpenAL_SoundStream::stop()
alSourceStop(mSource); alSourceStop(mSource);
alSourcei(mSource, AL_BUFFER, 0); alSourcei(mSource, AL_BUFFER, 0);
throwALerror(); throwALerror();
mSamplesQueued = 0;
mDecoder->rewind(); mDecoder->rewind();
} }
@ -254,11 +303,49 @@ bool OpenAL_SoundStream::isPlaying()
alGetSourcei(mSource, AL_SOURCE_STATE, &state); alGetSourcei(mSource, AL_SOURCE_STATE, &state);
throwALerror(); throwALerror();
if(state == AL_PLAYING) if(state == AL_PLAYING || state == AL_PAUSED)
return true; return true;
return !mIsFinished; return !mIsFinished;
} }
double OpenAL_SoundStream::getTimeOffset()
{
ALint state = AL_STOPPED;
ALfloat offset = 0.0f;
double t;
mOutput.mStreamThread->mMutex.lock();
alGetSourcef(mSource, AL_SEC_OFFSET, &offset);
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
if(state == AL_PLAYING || state == AL_PAUSED)
t = (double)(mDecoder->getSampleOffset() - mSamplesQueued)/(double)mSampleRate + offset;
else
t = (double)mDecoder->getSampleOffset() / (double)mSampleRate;
mOutput.mStreamThread->mMutex.unlock();
throwALerror();
return t;
}
void OpenAL_SoundStream::updateAll(bool local)
{
alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance);
alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance);
if(local)
{
alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f);
alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE);
}
else
{
alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f);
alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE);
}
alSourcei(mSource, AL_LOOPING, AL_FALSE);
update();
}
void OpenAL_SoundStream::update() void OpenAL_SoundStream::update()
{ {
ALfloat gain = mVolume*mBaseVolume; ALfloat gain = mVolume*mBaseVolume;
@ -279,6 +366,7 @@ void OpenAL_SoundStream::update()
bool OpenAL_SoundStream::process() bool OpenAL_SoundStream::process()
{ {
try {
bool finished = mIsFinished; bool finished = mIsFinished;
ALint processed, state; ALint processed, state;
@ -290,10 +378,11 @@ bool OpenAL_SoundStream::process()
{ {
std::vector<char> data(mBufferSize); std::vector<char> data(mBufferSize);
do { do {
ALuint bufid; ALuint bufid = 0;
size_t got; size_t got;
alSourceUnqueueBuffers(mSource, 1, &bufid); alSourceUnqueueBuffers(mSource, 1, &bufid);
mSamplesQueued -= getBufferSampleCount(bufid);
processed--; processed--;
if(finished) if(finished)
@ -305,6 +394,7 @@ bool OpenAL_SoundStream::process()
{ {
alBufferData(bufid, mFormat, &data[0], got, mSampleRate); alBufferData(bufid, mFormat, &data[0], got, mSampleRate);
alSourceQueueBuffers(mSource, 1, &bufid); alSourceQueueBuffers(mSource, 1, &bufid);
mSamplesQueued += getBufferSampleCount(bufid);
} }
} while(processed > 0); } while(processed > 0);
throwALerror(); throwALerror();
@ -312,19 +402,22 @@ bool OpenAL_SoundStream::process()
if(state != AL_PLAYING && state != AL_PAUSED) if(state != AL_PLAYING && state != AL_PAUSED)
{ {
ALint queued; ALint queued = 0;
alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
throwALerror();
if(queued > 0) if(queued > 0)
{
alSourcePlay(mSource); alSourcePlay(mSource);
throwALerror(); throwALerror();
} }
}
mIsFinished = finished; mIsFinished = finished;
return !finished; }
catch(std::exception &e) {
std::cout<< "Error updating stream \""<<mDecoder->getName()<<"\"" <<std::endl;
mSamplesQueued = 0;
mIsFinished = true;
}
return !mIsFinished;
} }
// //
@ -338,16 +431,21 @@ protected:
ALuint mSource; ALuint mSource;
ALuint mBuffer; ALuint mBuffer;
friend class OpenAL_Output;
void updateAll(bool local);
private: private:
OpenAL_Sound(const OpenAL_Sound &rhs); OpenAL_Sound(const OpenAL_Sound &rhs);
OpenAL_Sound& operator=(const OpenAL_Sound &rhs); OpenAL_Sound& operator=(const OpenAL_Sound &rhs);
public: public:
OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf); OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags);
virtual ~OpenAL_Sound(); virtual ~OpenAL_Sound();
virtual void stop(); virtual void stop();
virtual bool isPlaying(); virtual bool isPlaying();
virtual double getTimeOffset();
virtual void update(); virtual void update();
}; };
@ -360,16 +458,18 @@ class OpenAL_Sound3D : public OpenAL_Sound
OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs); OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs);
public: public:
OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf) OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags)
: OpenAL_Sound(output, src, buf) : OpenAL_Sound(output, src, buf, pos, vol, basevol, pitch, mindist, maxdist, flags)
{ } { }
virtual void update(); virtual void update();
}; };
OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf) OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags)
: mOutput(output), mSource(src), mBuffer(buf) : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags)
, mOutput(output), mSource(src), mBuffer(buf)
{ {
mOutput.mActiveSounds.push_back(this);
} }
OpenAL_Sound::~OpenAL_Sound() OpenAL_Sound::~OpenAL_Sound()
{ {
@ -378,6 +478,9 @@ OpenAL_Sound::~OpenAL_Sound()
mOutput.mFreeSources.push_back(mSource); mOutput.mFreeSources.push_back(mSource);
mOutput.bufferFinished(mBuffer); mOutput.bufferFinished(mBuffer);
mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(),
mOutput.mActiveSounds.end(), this));
} }
void OpenAL_Sound::stop() void OpenAL_Sound::stop()
@ -393,13 +496,43 @@ bool OpenAL_Sound::isPlaying()
alGetSourcei(mSource, AL_SOURCE_STATE, &state); alGetSourcei(mSource, AL_SOURCE_STATE, &state);
throwALerror(); throwALerror();
return state==AL_PLAYING; return state==AL_PLAYING || state==AL_PAUSED;
}
double OpenAL_Sound::getTimeOffset()
{
ALfloat t;
alGetSourcef(mSource, AL_SEC_OFFSET, &t);
throwALerror();
return t;
}
void OpenAL_Sound::updateAll(bool local)
{
alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance);
alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance);
if(local)
{
alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f);
alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE);
}
else
{
alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f);
alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE);
}
alSourcei(mSource, AL_LOOPING, (mFlags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE);
update();
} }
void OpenAL_Sound::update() void OpenAL_Sound::update()
{ {
ALfloat gain = mVolume*mBaseVolume; ALfloat gain = mVolume*mBaseVolume;
ALfloat pitch = mPitch; ALfloat pitch = mPitch;
if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
{ {
gain *= 0.9f; gain *= 0.9f;
@ -521,11 +654,9 @@ void OpenAL_Output::deinit()
{ {
mStreamThread->removeAll(); mStreamThread->removeAll();
while(!mFreeSources.empty()) for(size_t i = 0;i < mFreeSources.size();i++)
{ alDeleteSources(1, &mFreeSources[i]);
alDeleteSources(1, &mFreeSources.front()); mFreeSources.clear();
mFreeSources.pop_front();
}
mBufferRefs.clear(); mBufferRefs.clear();
mUnusedBuffers.clear(); mUnusedBuffers.clear();
@ -642,7 +773,7 @@ void OpenAL_Output::bufferFinished(ALuint buf)
} }
MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, float basevol, float pitch, int flags)
{ {
boost::shared_ptr<OpenAL_Sound> sound; boost::shared_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0; ALuint src=0, buf=0;
@ -655,7 +786,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume
try try
{ {
buf = getBuffer(fname); buf = getBuffer(fname);
sound.reset(new OpenAL_Sound(*this, src, buf)); sound.reset(new OpenAL_Sound(*this, src, buf, Ogre::Vector3(0.0f), vol, basevol, pitch, 1.0f, 1000.0f, flags));
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -666,25 +797,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume
throw; throw;
} }
alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f); sound->updateAll(true);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f);
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater)
{
volume *= 0.9f;
pitch *= 0.7f;
}
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE);
throwALerror();
alSourcei(src, AL_BUFFER, buf); alSourcei(src, AL_BUFFER, buf);
alSourcePlay(src); alSourcePlay(src);
@ -693,7 +806,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume
return sound; return sound;
} }
MWBase::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 vol, float basevol, float pitch,
float min, float max, int flags) float min, float max, int flags)
{ {
boost::shared_ptr<OpenAL_Sound> sound; boost::shared_ptr<OpenAL_Sound> sound;
@ -707,7 +820,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre
try try
{ {
buf = getBuffer(fname); buf = getBuffer(fname);
sound.reset(new OpenAL_Sound3D(*this, src, buf)); sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags));
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -718,26 +831,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre
throw; throw;
} }
alSource3f(src, AL_POSITION, pos.x, pos.z, -pos.y); sound->updateAll(false);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, min);
alSourcef(src, AL_MAX_DISTANCE, max);
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater)
{
volume *= 0.9f;
pitch *= 0.7f;
}
alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ?
0.0f : volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE);
throwALerror();
alSourcei(src, AL_BUFFER, buf); alSourcei(src, AL_BUFFER, buf);
alSourcePlay(src); alSourcePlay(src);
@ -747,7 +841,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre
} }
MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags) MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, float pitch, int flags)
{ {
boost::shared_ptr<OpenAL_SoundStream> sound; boost::shared_ptr<OpenAL_SoundStream> sound;
ALuint src; ALuint src;
@ -757,13 +851,11 @@ MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volu
src = mFreeSources.front(); src = mFreeSources.front();
mFreeSources.pop_front(); mFreeSources.pop_front();
if((flags&MWBase::SoundManager::Play_Loop))
std::cout <<"Warning: cannot loop stream \""<<decoder->getName()<<"\""<< std::endl;
try try
{ {
if((flags&MWBase::SoundManager::Play_Loop)) sound.reset(new OpenAL_SoundStream(*this, src, decoder, volume, pitch, flags));
std::cout <<"Warning: cannot loop stream "<<fname<< std::endl;
DecoderPtr decoder = mManager.getDecoder();
decoder->open(fname);
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -771,25 +863,7 @@ MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volu
throw; throw;
} }
alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f); sound->updateAll(true);
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f);
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater)
{
volume *= 0.9f;
pitch *= 0.7f;
}
alSourcef(src, AL_GAIN, volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
alSourcei(src, AL_LOOPING, AL_FALSE);
throwALerror();
sound->play(); sound->play();
return sound; return sound;
@ -814,6 +888,61 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3
} }
void OpenAL_Output::pauseSounds(int types)
{
std::vector<ALuint> sources;
SoundVec::const_iterator iter = mActiveSounds.begin();
while(iter != mActiveSounds.end())
{
const OpenAL_SoundStream *stream = dynamic_cast<OpenAL_SoundStream*>(*iter);
if(stream)
{
if(stream->mSource && (stream->getPlayType()&types))
sources.push_back(stream->mSource);
}
else
{
const OpenAL_Sound *sound = dynamic_cast<OpenAL_Sound*>(*iter);
if(sound && sound->mSource && (sound->getPlayType()&types))
sources.push_back(sound->mSource);
}
iter++;
}
if(sources.size() > 0)
{
alSourcePausev(sources.size(), &sources[0]);
throwALerror();
}
}
void OpenAL_Output::resumeSounds(int types)
{
std::vector<ALuint> sources;
SoundVec::const_iterator iter = mActiveSounds.begin();
while(iter != mActiveSounds.end())
{
const OpenAL_SoundStream *stream = dynamic_cast<OpenAL_SoundStream*>(*iter);
if(stream)
{
if(stream->mSource && (stream->getPlayType()&types))
sources.push_back(stream->mSource);
}
else
{
const OpenAL_Sound *sound = dynamic_cast<OpenAL_Sound*>(*iter);
if(sound && sound->mSource && (sound->getPlayType()&types))
sources.push_back(sound->mSource);
}
iter++;
}
if(sources.size() > 0)
{
alSourcePlayv(sources.size(), &sources[0]);
throwALerror();
}
}
OpenAL_Output::OpenAL_Output(SoundManager &mgr) OpenAL_Output::OpenAL_Output(SoundManager &mgr)
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0), : Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0),
mLastEnvironment(Env_Normal), mStreamThread(new StreamThread) mLastEnvironment(Env_Normal), mStreamThread(new StreamThread)

View file

@ -33,6 +33,9 @@ namespace MWSound
uint64_t mBufferCacheMemSize; uint64_t mBufferCacheMemSize;
typedef std::vector<Sound*> SoundVec;
SoundVec mActiveSounds;
ALuint getBuffer(const std::string &fname); ALuint getBuffer(const std::string &fname);
void bufferFinished(ALuint buffer); void bufferFinished(ALuint buffer);
@ -42,13 +45,16 @@ namespace MWSound
virtual void init(const std::string &devname=""); virtual void init(const std::string &devname="");
virtual void deinit(); virtual void deinit();
virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags);
virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags); float vol, float basevol, float pitch, float min, float max, int flags);
virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags); virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags);
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env);
virtual void pauseSounds(int types);
virtual void resumeSounds(int types);
OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output& operator=(const OpenAL_Output &rhs);
OpenAL_Output(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs);
@ -64,7 +70,7 @@ namespace MWSound
friend class SoundManager; friend class SoundManager;
}; };
#ifndef DEFAULT_OUTPUT #ifndef DEFAULT_OUTPUT
#define DEFAULT_OUTPUT (::MWSound::OpenAL_Output) #define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x))
#endif #endif
} }

View file

@ -26,16 +26,22 @@ namespace MWSound
public: public:
virtual void stop() = 0; virtual void stop() = 0;
virtual bool isPlaying() = 0; virtual bool isPlaying() = 0;
virtual double getTimeOffset() = 0;
void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setPosition(const Ogre::Vector3 &pos) { mPos = pos; }
void setVolume(float volume) { mVolume = volume; } void setVolume(float volume) { mVolume = volume; }
Sound() : mPos(0.0f, 0.0f, 0.0f) MWBase::SoundManager::PlayType getPlayType() const
, mVolume(1.0f) { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); }
, mBaseVolume(1.0f)
, mPitch(1.0f)
, mMinDistance(20.0f) /* 1 * min_range_scale */ Sound(const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags)
, mMaxDistance(12750.0f) /* 255 * max_range_scale */ : mPos(pos)
, mFlags(MWBase::SoundManager::Play_Normal) , mVolume(vol)
, mBaseVolume(basevol)
, mPitch(pitch)
, mMinDistance(mindist)
, mMaxDistance(maxdist)
, mFlags(flags)
{ } { }
virtual ~Sound() { } virtual ~Sound() { }

View file

@ -15,7 +15,10 @@ namespace MWSound
enum ChannelConfig { enum ChannelConfig {
ChannelConfig_Mono, ChannelConfig_Mono,
ChannelConfig_Stereo ChannelConfig_Stereo,
ChannelConfig_Quad,
ChannelConfig_5point1,
ChannelConfig_7point1
}; };
const char *getChannelConfigName(ChannelConfig config); const char *getChannelConfigName(ChannelConfig config);
@ -29,11 +32,13 @@ namespace MWSound
virtual void open(const std::string &fname) = 0; virtual void open(const std::string &fname) = 0;
virtual void close() = 0; virtual void close() = 0;
virtual std::string getName() = 0;
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0;
virtual size_t read(char *buffer, size_t bytes) = 0; virtual size_t read(char *buffer, size_t bytes) = 0;
virtual void readAll(std::vector<char> &output); virtual void readAll(std::vector<char> &output);
virtual void rewind() = 0; virtual void rewind() = 0;
virtual size_t getSampleOffset() = 0;
Sound_Decoder() : mResourceMgr(Ogre::ResourceGroupManager::getSingleton()) Sound_Decoder() : mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
{ } { }

View file

@ -24,13 +24,16 @@ namespace MWSound
virtual void init(const std::string &devname="") = 0; virtual void init(const std::string &devname="") = 0;
virtual void deinit() = 0; virtual void deinit() = 0;
virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags) = 0;
virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags) = 0; float vol, float basevol, float pitch, float min, float max, int flags) = 0;
virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0; virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, 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; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0;
virtual void pauseSounds(int types) = 0;
virtual void resumeSounds(int types) = 0;
Sound_Output& operator=(const Sound_Output &rhs); Sound_Output& operator=(const Sound_Output &rhs);
Sound_Output(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs);

View file

@ -52,6 +52,7 @@ namespace MWSound
, mMusicVolume(1.0f) , mMusicVolume(1.0f)
, mFootstepsVolume(1.0f) , mFootstepsVolume(1.0f)
, mVoiceVolume(1.0f) , mVoiceVolume(1.0f)
, mPausedSoundTypes(0)
{ {
if(!useSound) if(!useSound)
return; return;
@ -86,7 +87,7 @@ namespace MWSound
{ {
if(devname.empty()) if(devname.empty())
throw; throw;
std::cout <<"Failed to open device \""<<devname<<"\", trying default."<< std::endl << "The error given was: " << e.what() << std::endl; std::cerr <<"Failed to open device \""<<devname<<"\": " << e.what() << std::endl;
mOutput->init(); mOutput->init();
Settings::Manager::setString("device", "Sound", ""); Settings::Manager::setString("device", "Sound", "");
} }
@ -137,6 +138,27 @@ namespace MWSound
return "Sound/"+snd->mSound; return "Sound/"+snd->mSound;
} }
// Gets the combined volume settings for the given sound type
float SoundManager::volumeFromType(PlayType type) const
{
float volume = mMasterVolume;
switch(type)
{
case Play_TypeSfx:
volume *= mSFXVolume;
break;
case Play_TypeVoice:
volume *= mVoiceVolume;
break;
case Play_TypeMusic:
case Play_TypeMovie:
volume *= mMusicVolume;
break;
case Play_TypeMask:
break;
}
return volume;
}
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
{ {
@ -165,11 +187,13 @@ namespace MWSound
std::cout <<"Playing "<<filename<< std::endl; std::cout <<"Playing "<<filename<< std::endl;
try try
{ {
float basevol = mMasterVolume * mMusicVolume;
stopMusic(); stopMusic();
mMusic = mOutput->streamSound(filename, basevol, 1.0f, Play_NoEnv);
mMusic->mBaseVolume = basevol; DecoderPtr decoder = getDecoder();
mMusic->mFlags = Play_NoEnv; decoder->open(filename);
mMusic = mOutput->streamSound(decoder, volumeFromType(Play_TypeMusic),
1.0f, Play_NoEnv|Play_TypeMusic);
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -212,16 +236,13 @@ namespace MWSound
try try
{ {
// The range values are not tested // The range values are not tested
float basevol = mMasterVolume * mVoiceVolume; float basevol = volumeFromType(Play_TypeVoice);
std::string filePath = "Sound/"+filename; std::string filePath = "Sound/"+filename;
const ESM::Position &pos = ptr.getRefData().getPosition(); const ESM::Position &pos = ptr.getRefData().getPosition();
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f,
20.0f, 12750.0f, Play_Normal); 20.0f, 12750.0f, Play_Normal|Play_TypeVoice);
sound->mPos = objpos;
sound->mBaseVolume = basevol;
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
} }
catch(std::exception &e) catch(std::exception &e)
@ -236,12 +257,10 @@ namespace MWSound
return; return;
try try
{ {
float basevol = mMasterVolume * mVoiceVolume; float basevol = volumeFromType(Play_TypeVoice);
std::string filePath = "Sound/"+filename; std::string filePath = "Sound/"+filename;
MWBase::SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice);
sound->mBaseVolume = basevol;
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound"));
} }
catch(std::exception &e) catch(std::exception &e)
@ -271,26 +290,35 @@ namespace MWSound
} }
MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type)
{
MWBase::SoundPtr track;
if(!mOutput->isInitialized())
return track;
try
{
track = mOutput->streamSound(decoder, volumeFromType(type), 1.0f, Play_NoEnv|type);
}
catch(std::exception &e)
{
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
return track;
}
MWBase::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, PlayMode mode)
{ {
MWBase::SoundPtr sound; MWBase::SoundPtr sound;
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return sound; return sound;
try try
{ {
float basevol = mMasterVolume * mSFXVolume; float basevol = volumeFromType(Play_TypeSfx);
float min, max; float min, max;
std::string file = lookup(soundId, basevol, min, max); std::string file = lookup(soundId, volume, min, max);
sound = mOutput->playSound(file, volume*basevol, pitch, mode);
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mPitch = pitch;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
sound = mOutput->playSound(file, volume, basevol, pitch, mode|Play_TypeSfx);
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
} }
catch(std::exception &e) catch(std::exception &e)
@ -301,7 +329,7 @@ namespace MWSound
} }
MWBase::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) float volume, float pitch, PlayMode mode)
{ {
MWBase::SoundPtr sound; MWBase::SoundPtr sound;
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
@ -309,21 +337,13 @@ namespace MWSound
try try
{ {
// Look up the sound in the ESM data // Look up the sound in the ESM data
float basevol = mMasterVolume * mSFXVolume; float basevol = volumeFromType(Play_TypeSfx);
float min, max; float min, max;
std::string file = lookup(soundId, basevol, min, max); std::string file = lookup(soundId, volume, min, max);
const ESM::Position &pos = ptr.getRefData().getPosition();; const ESM::Position &pos = ptr.getRefData().getPosition();;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode); sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|Play_TypeSfx);
sound->mPos = objpos;
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mPitch = pitch;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
if((mode&Play_NoTrack)) if((mode&Play_NoTrack))
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
else else
@ -404,6 +424,27 @@ namespace MWSound
} }
void SoundManager::pauseSounds(int types)
{
if(mOutput->isInitialized())
{
types &= Play_TypeMask;
mOutput->pauseSounds(types);
mPausedSoundTypes |= types;
}
}
void SoundManager::resumeSounds(int types)
{
if(mOutput->isInitialized())
{
types &= types&Play_TypeMask&mPausedSoundTypes;
mOutput->resumeSounds(types);
mPausedSoundTypes &= ~types;
}
}
void SoundManager::updateRegionSound(float duration) void SoundManager::updateRegionSound(float duration)
{ {
MWWorld::Ptr::CellStore *current = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); MWWorld::Ptr::CellStore *current = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell();
@ -525,24 +566,13 @@ namespace MWSound
SoundMap::iterator snditer = mActiveSounds.begin(); SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end()) while(snditer != mActiveSounds.end())
{ {
if(snditer->second.second != "_say_sound") snditer->first->mBaseVolume = volumeFromType(snditer->first->getPlayType());
{
float basevol = mMasterVolume * mSFXVolume;
float min, max;
lookup(snditer->second.second, basevol, min, max);
snditer->first->mBaseVolume = basevol;
}
else
{
float basevol = mMasterVolume * mVoiceVolume;
snditer->first->mBaseVolume = basevol;
}
snditer->first->update(); snditer->first->update();
snditer++; snditer++;
} }
if(mMusic) if(mMusic)
{ {
mMusic->mBaseVolume = mMasterVolume * mMusicVolume; mMusic->mBaseVolume = volumeFromType(mMusic->getPlayType());
mMusic->update(); mMusic->update();
} }
} }
@ -587,6 +617,9 @@ namespace MWSound
{ {
case ChannelConfig_Mono: return "Mono"; case ChannelConfig_Mono: return "Mono";
case ChannelConfig_Stereo: return "Stereo"; case ChannelConfig_Stereo: return "Stereo";
case ChannelConfig_Quad: return "Quad";
case ChannelConfig_5point1: return "5.1 Surround";
case ChannelConfig_7point1: return "7.1 Surround";
} }
return "(unknown channel config)"; return "(unknown channel config)";
} }
@ -597,6 +630,9 @@ namespace MWSound
{ {
case ChannelConfig_Mono: frames *= 1; break; case ChannelConfig_Mono: frames *= 1; break;
case ChannelConfig_Stereo: frames *= 2; break; case ChannelConfig_Stereo: frames *= 2; break;
case ChannelConfig_Quad: frames *= 4; break;
case ChannelConfig_5point1: frames *= 6; break;
case ChannelConfig_7point1: frames *= 8; break;
} }
switch(type) switch(type)
{ {

View file

@ -22,8 +22,6 @@ namespace MWSound
struct Sound_Decoder; struct Sound_Decoder;
class Sound; class Sound;
typedef boost::shared_ptr<Sound_Decoder> DecoderPtr;
enum Environment { enum Environment {
Env_Normal, Env_Normal,
Env_Underwater Env_Underwater
@ -54,6 +52,8 @@ namespace MWSound
Ogre::Vector3 mListenerDir; Ogre::Vector3 mListenerDir;
Ogre::Vector3 mListenerUp; Ogre::Vector3 mListenerUp;
int mPausedSoundTypes;
std::string lookup(const std::string &soundId, std::string lookup(const std::string &soundId,
float &volume, float &min, float &max); float &volume, float &min, float &max);
void streamMusicFull(const std::string& filename); void streamMusicFull(const std::string& filename);
@ -61,6 +61,8 @@ namespace MWSound
void updateSounds(float duration); void updateSounds(float duration);
void updateRegionSound(float duration); void updateRegionSound(float duration);
float volumeFromType(PlayType type) const;
SoundManager(const SoundManager &rhs); SoundManager(const SoundManager &rhs);
SoundManager& operator=(const SoundManager &rhs); SoundManager& operator=(const SoundManager &rhs);
@ -105,11 +107,14 @@ namespace MWSound
virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr());
///< Stop an actor speaking ///< Stop an actor speaking
virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type);
///< Play a 2D audio track, using a custom decoder
virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal);
///< Play a sound, independently of 3D-position ///< Play a sound, independently of 3D-position
virtual MWBase::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); float volume, float pitch, PlayMode mode=Play_Normal);
///< Play a sound from an object ///< Play a sound from an object
virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId);
@ -127,6 +132,12 @@ namespace MWSound
virtual 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? ///< Is the given sound currently playing on the given object?
virtual void pauseSounds(int types=Play_TypeMask);
///< Pauses all currently playing sounds, including music.
virtual void resumeSounds(int types=Play_TypeMask);
///< Resumes all previously paused sounds.
virtual void update(float duration); virtual void update(float duration);
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up); virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up);

View file

@ -738,7 +738,7 @@ void WeatherManager::update(float duration)
if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end())
{ {
mSoundsPlaying.push_back(ambientSnd); mSoundsPlaying.push_back(ambientSnd);
MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, true); MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop);
} }
} }
@ -749,7 +749,7 @@ void WeatherManager::update(float duration)
if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end())
{ {
mSoundsPlaying.push_back(rainSnd); mSoundsPlaying.push_back(rainSnd);
MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, true); MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop);
} }
} }

View file

@ -170,7 +170,7 @@ namespace MWWorld
World::World (OEngine::Render::OgreRenderer& renderer, World::World (OEngine::Render::OgreRenderer& renderer,
const Files::Collections& fileCollections, const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
const ToUTF8::FromType& encoding, std::map<std::string,std::string> fallbackMap) ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mCells (mStore, mEsm), mSky (true), mCells (mStore, mEsm),
mNumFacing(0) mNumFacing(0)
@ -187,7 +187,7 @@ namespace MWWorld
std::cout << "Loading ESM " << masterPath.string() << "\n"; std::cout << "Loading ESM " << masterPath.string() << "\n";
// This parses the ESM file and loads a sample cell // This parses the ESM file and loads a sample cell
mEsm.setEncoding(encoding); mEsm.setEncoder(encoder);
mEsm.open (masterPath.string()); mEsm.open (masterPath.string());
mStore.load (mEsm); mStore.load (mEsm);
@ -1300,4 +1300,14 @@ namespace MWWorld
return 0; return 0;
} }
void World::playVideo (const std::string &name, bool allowSkipping)
{
mRendering->playVideo(name, allowSkipping);
}
void World::stopVideo ()
{
mRendering->stopVideo();
}
} }

View file

@ -95,7 +95,7 @@ namespace MWWorld
World (OEngine::Render::OgreRenderer& renderer, World (OEngine::Render::OgreRenderer& renderer,
const Files::Collections& fileCollections, const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
const ToUTF8::FromType& encoding, std::map<std::string,std::string> fallbackMap); ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap);
virtual ~World(); virtual ~World();
@ -336,6 +336,10 @@ namespace MWWorld
/// 1 - only waiting \n /// 1 - only waiting \n
/// 2 - player is underwater \n /// 2 - player is underwater \n
/// 3 - enemies are nearby (not implemented) /// 3 - enemies are nearby (not implemented)
/// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping);
virtual void stopVideo();
}; };
} }

View file

@ -1,105 +0,0 @@
# Find the FFmpeg library
#
# Sets
# FFMPEG_FOUND. If false, don't try to use ffmpeg
# FFMPEG_INCLUDE_DIR
# FFMPEG_LIBRARIES
#
# Modified by Nicolay Korslund for OpenMW
SET( FFMPEG_FOUND "NO" )
FIND_PATH( FFMPEG_general_INCLUDE_DIR libavcodec/avcodec.h libavformat/avformat.h
HINTS
PATHS
/usr/include
/usr/local/include
/usr/include/ffmpeg
/usr/local/include/ffmpeg
/usr/include/ffmpeg/libavcodec
/usr/local/include/ffmpeg/libavcodec
/usr/include/libavcodec
/usr/local/include/libavcodec
)
FIND_PATH( FFMPEG_avcodec_INCLUDE_DIR avcodec.h
HINTS
PATHS
${FFMPEG_general_INCLUDE_DIR}/libavcodec
/usr/include
/usr/local/include
/usr/include/ffmpeg
/usr/local/include/ffmpeg
/usr/include/ffmpeg/libavcodec
/usr/local/include/ffmpeg/libavcodec
/usr/include/libavcodec
/usr/local/include/libavcodec
)
FIND_PATH( FFMPEG_avformat_INCLUDE_DIR avformat.h
HINTS
PATHS
${FFMPEG_general_INCLUDE_DIR}/libavformat
/usr/include
/usr/local/include
/usr/include/ffmpeg
/usr/local/include/ffmpeg
/usr/include/ffmpeg/libavformat
/usr/local/include/ffmpeg/libavformat
/usr/include/libavformat
/usr/local/include/libavformat
)
set(FFMPEG_INCLUDE_DIR ${FFMPEG_general_INCLUDE_DIR} ${FFMPEG_avcodec_INCLUDE_DIR} ${FFMPEG_avformat_INCLUDE_DIR})
IF( FFMPEG_INCLUDE_DIR )
FIND_PROGRAM( FFMPEG_CONFIG ffmpeg-config
/usr/bin
/usr/local/bin
${HOME}/bin
)
IF( FFMPEG_CONFIG )
EXEC_PROGRAM( ${FFMPEG_CONFIG} ARGS "--libs avformat" OUTPUT_VARIABLE FFMPEG_LIBS )
SET( FFMPEG_FOUND "YES" )
SET( FFMPEG_LIBRARIES "${FFMPEG_LIBS}" )
ELSE( FFMPEG_CONFIG )
FIND_LIBRARY( FFMPEG_avcodec_LIBRARY avcodec
/usr/lib
/usr/local/lib
/usr/lib64
/usr/local/lib64
)
FIND_LIBRARY( FFMPEG_avformat_LIBRARY avformat
/usr/lib
/usr/local/lib
/usr/lib64
/usr/local/lib64
)
FIND_LIBRARY( FFMPEG_avutil_LIBRARY avutil
/usr/lib
/usr/local/lib
/usr/lib64
/usr/local/lib64
)
IF( FFMPEG_avcodec_LIBRARY )
IF( FFMPEG_avformat_LIBRARY )
SET( FFMPEG_FOUND "YES" )
SET( FFMPEG_LIBRARIES ${FFMPEG_avformat_LIBRARY} ${FFMPEG_avcodec_LIBRARY} )
IF( FFMPEG_avutil_LIBRARY )
SET( FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_avutil_LIBRARY} )
ENDIF( FFMPEG_avutil_LIBRARY )
ENDIF( FFMPEG_avformat_LIBRARY )
ENDIF( FFMPEG_avcodec_LIBRARY )
ENDIF( FFMPEG_CONFIG )
ENDIF( FFMPEG_INCLUDE_DIR )

148
cmake/FindFFmpeg.cmake Normal file
View file

@ -0,0 +1,148 @@
# vim: ts=2 sw=2
# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC)
#
# Once done this will define
# FFMPEG_FOUND - System has the all required components.
# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers.
# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components.
# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components.
#
# For each of the components it will additionaly set.
# - AVCODEC
# - AVDEVICE
# - AVFORMAT
# - AVUTIL
# - POSTPROCESS
# - SWSCALE
# the following variables will be defined
# <component>_FOUND - System has <component>
# <component>_INCLUDE_DIRS - Include directory necessary for using the <component> headers
# <component>_LIBRARIES - Link these to use <component>
# <component>_DEFINITIONS - Compiler switches required for using <component>
# <component>_VERSION - The components version
#
# Copyright (c) 2006, Matthias Kretz, <kretz@kde.org>
# Copyright (c) 2008, Alexander Neundorf, <neundorf@kde.org>
# Copyright (c) 2011, Michael Jansen, <kde@michael-jansen.biz>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
include(FindPackageHandleStandardArgs)
# The default components were taken from a survey over other FindFFMPEG.cmake files
if (NOT FFmpeg_FIND_COMPONENTS)
set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE)
endif ()
#
### Macro: set_component_found
#
# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present.
#
macro(set_component_found _component )
if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS)
# message(STATUS " - ${_component} found.")
set(${_component}_FOUND TRUE)
else ()
# message(STATUS " - ${_component} not found.")
endif ()
endmacro()
#
### Macro: find_component
#
# Checks for the given component by invoking pkgconfig and then looking up the libraries and
# include directories.
#
macro(find_component _component _pkgconfig _library _header)
if (NOT WIN32)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(PC_${_component} ${_pkgconfig})
endif ()
endif (NOT WIN32)
find_path(${_component}_INCLUDE_DIRS ${_header}
HINTS
${PC_LIB${_component}_INCLUDEDIR}
${PC_LIB${_component}_INCLUDE_DIRS}
PATH_SUFFIXES
ffmpeg
)
find_library(${_component}_LIBRARIES NAMES ${_library}
HINTS
${PC_LIB${_component}_LIBDIR}
${PC_LIB${_component}_LIBRARY_DIRS}
)
set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.")
set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.")
set_component_found(${_component})
mark_as_advanced(
${_component}_INCLUDE_DIRS
${_component}_LIBRARIES
${_component}_DEFINITIONS
${_component}_VERSION)
endmacro()
# Check for cached results. If there are skip the costly part.
if (NOT FFMPEG_LIBRARIES)
# Check for all possible component.
find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h)
find_component(AVFORMAT libavformat avformat libavformat/avformat.h)
find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h)
find_component(AVUTIL libavutil avutil libavutil/avutil.h)
find_component(SWSCALE libswscale swscale libswscale/swscale.h)
find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h)
# Check if the required components were found and add their stuff to the FFMPEG_* vars.
foreach (_component ${FFmpeg_FIND_COMPONENTS})
if (${_component}_FOUND)
# message(STATUS "Required component ${_component} present.")
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES})
set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS})
list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS})
else ()
# message(STATUS "Required component ${_component} missing.")
endif ()
endforeach ()
# Build the include path with duplicates removed.
if (FFMPEG_INCLUDE_DIRS)
list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
endif ()
# cache the vars.
set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE)
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE)
set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE)
mark_as_advanced(FFMPEG_INCLUDE_DIRS
FFMPEG_LIBRARIES
FFMPEG_DEFINITIONS)
endif ()
# Now set the noncached _FOUND vars for the components.
foreach (_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWSCALE)
set_component_found(${_component})
endforeach ()
# Compile the list of required vars
set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS)
foreach (_component ${FFmpeg_FIND_COMPONENTS})
list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS)
endforeach ()
# Give a nice error message if some of the required vars are missing.
find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS})

177
cmake/FindSDL.cmake Normal file
View file

@ -0,0 +1,177 @@
# Locate SDL library
# This module defines
# SDL_LIBRARY, the name of the library to link against
# SDL_FOUND, if false, do not try to link to SDL
# SDL_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL_BUILDING_LIBRARY
# If this is defined, then no SDL_main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDLmain which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL_LIBRARY_TEMP in your configuration
# and no SDL_LIBRARY, it means CMake did not find your SDL library
# (SDL.dll, libsdl.so, SDL.framework, etc).
# Set SDL_LIBRARY_TEMP to point to your SDL library, and configure again.
# Similarly, if you see an empty SDLMAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL_LIBRARY
# variable, but when these values are unset, SDL_LIBRARY does not get created.
#
#
# $SDLDIR is an environment variable that would
# correspond to the ./configure --prefix=$SDLDIR
# used in building SDL.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDLmain which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
FIND_PATH(SDL_INCLUDE_DIR SDL.h
HINTS
$ENV{SDLDIR}
PATH_SUFFIXES include/SDL include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local/include/SDL12
/usr/local/include/SDL11 # FreeBSD ports
/usr/include/SDL12
/usr/include/SDL11
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
#MESSAGE("SDL_INCLUDE_DIR is ${SDL_INCLUDE_DIR}")
# SDL-1.1 is the name used by FreeBSD ports...
# don't confuse it for the version number.
FIND_LIBRARY(SDL_LIBRARY_TEMP
NAMES SDL SDL-1.1
HINTS
$ENV{SDLDIR}
PATH_SUFFIXES lib64 lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
#MESSAGE("SDL_LIBRARY_TEMP is ${SDL_LIBRARY_TEMP}")
IF(NOT SDL_BUILDING_LIBRARY)
IF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDLmain for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDLMAIN_LIBRARY
NAMES SDLmain SDLmain-1.1
HINTS
$ENV{SDLDIR}
PATH_SUFFIXES lib64 lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL_BUILDING_LIBRARY)
# SDL may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
SET(SDL_FOUND "NO")
IF(SDL_LIBRARY_TEMP)
# For SDLmain
IF(NOT SDL_BUILDING_LIBRARY)
IF(SDLMAIN_LIBRARY)
SET(SDL_LIBRARY_TEMP ${SDLMAIN_LIBRARY} ${SDL_LIBRARY_TEMP})
ENDIF(SDLMAIN_LIBRARY)
ENDIF(NOT SDL_BUILDING_LIBRARY)
# For OS X, SDL uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "")
SET(SDL_FOUND "YES")
ENDIF(SDL_LIBRARY_TEMP)
#MESSAGE("SDL_LIBRARY is ${SDL_LIBRARY}")

View file

@ -48,7 +48,7 @@ add_component_dir (misc
add_component_dir (files add_component_dir (files
linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager
filelibrary ogreplugin filelibrary ogreplugin constrainedfiledatastream lowlevelfile
) )
add_component_dir (compiler add_component_dir (compiler

View file

@ -23,94 +23,15 @@
#include "bsa_file.hpp" #include "bsa_file.hpp"
#include <stdexcept> //#include <stdexcept>
#include <cstdlib> //#include <cstdlib>
#include <cassert> //#include <cassert>
#include <OgreDataStream.h> #include "../files/constrainedfiledatastream.hpp"
using namespace std; using namespace std;
using namespace Bsa; 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<char*>(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 /// Error handling
void BSAFile::fail(const string &msg) void BSAFile::fail(const string &msg)
@ -253,5 +174,5 @@ Ogre::DataStreamPtr BSAFile::getFile(const char *file)
fail("File not found: " + string(file)); fail("File not found: " + string(file));
const FileStruct &fs = files[i]; const FileStruct &fs = files[i];
return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, fs.offset, fs.fileSize)); return openConstrainedFileDataStream (filename.c_str (), fs.offset, fs.fileSize);
} }

View file

@ -1,6 +1,8 @@
#include "esmreader.hpp" #include "esmreader.hpp"
#include <stdexcept> #include <stdexcept>
#include "../files/constrainedfiledatastream.hpp"
namespace ESM namespace ESM
{ {
@ -13,6 +15,11 @@ ESM_Context ESMReader::getContext()
return mCtx; return mCtx;
} }
ESMReader::ESMReader(void):
mBuffer(50*1024)
{
}
void ESMReader::restoreContext(const ESM_Context &rc) void ESMReader::restoreContext(const ESM_Context &rc)
{ {
// Reopen the file if necessary // Reopen the file if necessary
@ -108,16 +115,12 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name)
void ESMReader::open(const std::string &file) void ESMReader::open(const std::string &file)
{ {
std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); open (openConstrainedFileDataStream (file.c_str ()), file);
// Ogre will delete the stream for us
open(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file);
} }
void ESMReader::openRaw(const std::string &file) void ESMReader::openRaw(const std::string &file)
{ {
std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); openRaw (openConstrainedFileDataStream (file.c_str ()), file);
// Ogre will delete the stream for us
openRaw(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file);
} }
int64_t ESMReader::getHNLong(const char *name) int64_t ESMReader::getHNLong(const char *name)
@ -325,11 +328,21 @@ void ESMReader::getExact(void*x, int size)
std::string ESMReader::getString(int size) std::string ESMReader::getString(int size)
{ {
char *ptr = ToUTF8::getBuffer(size); size_t s = size;
mEsm->read(ptr, size); if (mBuffer.size() <= s)
// Add some extra padding to reduce the chance of having to resize
// again later.
mBuffer.resize(3*s);
// And make sure the string is zero terminated
mBuffer[s] = 0;
// read ESM data
char *ptr = &mBuffer[0];
getExact(ptr, size);
// Convert to UTF8 and return // Convert to UTF8 and return
return ToUTF8::getUtf8(mEncoding); return mEncoder->getUtf8(ptr, size);
} }
void ESMReader::fail(const std::string &msg) void ESMReader::fail(const std::string &msg)
@ -347,9 +360,9 @@ void ESMReader::fail(const std::string &msg)
throw std::runtime_error(ss.str()); throw std::runtime_error(ss.str());
} }
void ESMReader::setEncoding(const ToUTF8::FromType& encoding) void ESMReader::setEncoder(ToUTF8::Utf8Encoder* encoder)
{ {
mEncoding = encoding; mEncoder = encoder;
} }
} }

View file

@ -20,6 +20,8 @@ class ESMReader
{ {
public: public:
ESMReader(void);
/************************************************************************* /*************************************************************************
* *
* Public type definitions * Public type definitions
@ -233,8 +235,8 @@ public:
/// Used for error handling /// Used for error handling
void fail(const std::string &msg); void fail(const std::string &msg);
/// Sets font encoding for ESM strings /// Sets font encoder for ESM strings
void setEncoding(const ToUTF8::FromType& encoding); void setEncoder(ToUTF8::Utf8Encoder* encoder);
private: private:
Ogre::DataStreamPtr mEsm; Ogre::DataStreamPtr mEsm;
@ -244,9 +246,12 @@ private:
// Special file signifier (see SpecialFile enum above) // Special file signifier (see SpecialFile enum above)
int mSpf; int mSpf;
// Buffer for ESM strings
std::vector<char> mBuffer;
SaveData mSaveData; SaveData mSaveData;
MasterList mMasters; MasterList mMasters;
ToUTF8::FromType mEncoding; ToUTF8::Utf8Encoder* mEncoder;
}; };
} }
#endif #endif

View file

@ -157,12 +157,8 @@ void ESMWriter::writeHString(const std::string& data)
write("\0", 1); write("\0", 1);
else else
{ {
char *ptr = ToUTF8::getBuffer(data.size()+1);
strncpy(ptr, &data[0], data.size());
ptr[data.size()] = '\0';
// Convert to UTF8 and return // Convert to UTF8 and return
std::string ascii = ToUTF8::getLegacyEnc(m_encoding); std::string ascii = m_encoder->getLegacyEnc(data);
write(ascii.c_str(), ascii.size()); write(ascii.c_str(), ascii.size());
} }
@ -192,21 +188,9 @@ void ESMWriter::write(const char* data, int size)
m_stream->write(data, size); m_stream->write(data, size);
} }
void ESMWriter::setEncoding(const std::string& encoding) void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
{ {
if (encoding == "win1250") m_encoder = encoder;
{
m_encoding = ToUTF8::WINDOWS_1250;
}
else if (encoding == "win1251")
{
m_encoding = ToUTF8::WINDOWS_1251;
}
else
{
// Default Latin encoding
m_encoding = ToUTF8::WINDOWS_1252;
}
} }
} }

View file

@ -6,7 +6,7 @@
#include <assert.h> #include <assert.h>
#include "esmcommon.hpp" #include "esmcommon.hpp"
#include "../to_utf8/to_utf8.hpp" #include <components/to_utf8/to_utf8.hpp>
namespace ESM { namespace ESM {
@ -24,7 +24,7 @@ public:
void setVersion(int ver); void setVersion(int ver);
int getType(); int getType();
void setType(int type); void setType(int type);
void setEncoding(const std::string& encoding); // Write strings as UTF-8? void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8?
void setAuthor(const std::string& author); void setAuthor(const std::string& author);
void setDescription(const std::string& desc); void setDescription(const std::string& desc);
@ -94,11 +94,10 @@ private:
std::list<RecordData> m_records; std::list<RecordData> m_records;
std::ostream* m_stream; std::ostream* m_stream;
std::streampos m_headerPos; std::streampos m_headerPos;
ToUTF8::FromType m_encoding; ToUTF8::Utf8Encoder* m_encoder;
int m_recordCount; int m_recordCount;
HEDRstruct m_header; HEDRstruct m_header;
SaveData m_saveData;
}; };
} }

View file

@ -0,0 +1,175 @@
#include "constrainedfiledatastream.hpp"
#include "lowlevelfile.hpp"
#include <stdexcept>
#include <cassert>
#ifndef __clang__
#include <cstdint>
#else
#include <tr1/cstdint>
#endif
namespace {
class ConstrainedDataStream : public Ogre::DataStream {
public:
static const size_t sBufferSize = 4096; // somewhat arbitrary though 64KB buffers didn't seem to improve performance any
static const size_t sBufferThreshold = 1024; // reads larger than this bypass buffering as cost of memcpy outweighs cost of system call
ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length)
{
mFile.open (fname.c_str ());
mSize = length != 0xFFFFFFFF ? length : mFile.size () - start;
mPos = 0;
mOrigin = start;
mExtent = start + mSize;
mBufferOrigin = 0;
mBufferExtent = 0;
}
size_t read(void* buf, size_t count)
{
assert (mPos <= mSize);
uint8_t * out = reinterpret_cast <uint8_t *> (buf);
size_t posBeg = mOrigin + mPos;
size_t posEnd = posBeg + count;
if (posEnd > mExtent)
posEnd = mExtent;
size_t posCur = posBeg;
while (posCur != posEnd)
{
size_t readLeft = posEnd - posCur;
if (posCur < mBufferOrigin || posCur >= mBufferExtent)
{
if (readLeft >= sBufferThreshold || (posCur == mOrigin && posEnd == mExtent))
{
assert (mFile.tell () == mBufferExtent);
if (posCur != mBufferExtent)
mFile.seek (posCur);
posCur += mFile.read (out, readLeft);
mBufferOrigin = mBufferExtent = posCur;
mPos = posCur - mOrigin;
return posCur - posBeg;
}
else
{
size_t newBufferOrigin;
if ((posCur < mBufferOrigin) && (mBufferOrigin - posCur < sBufferSize))
newBufferOrigin = std::max (mOrigin, mBufferOrigin > sBufferSize ? mBufferOrigin - sBufferSize : 0);
else
newBufferOrigin = posCur;
fill (newBufferOrigin);
}
}
size_t xfer = std::min (readLeft, mBufferExtent - posCur);
memcpy (out, mBuffer + (posCur - mBufferOrigin), xfer);
posCur += xfer;
out += xfer;
}
count = posEnd - posBeg;
mPos += count;
return count;
}
void skip(long count)
{
assert (mPos <= mSize);
if((count >= 0 && (size_t)count <= mSize-mPos) ||
(count < 0 && (size_t)-count <= mPos))
mPos += count;
}
void seek(size_t pos)
{
assert (mPos <= mSize);
if (pos < mSize)
mPos = pos;
}
virtual size_t tell() const
{
assert (mPos <= mSize);
return mPos;
}
virtual bool eof() const
{
assert (mPos <= mSize);
return mPos == mSize;
}
virtual void close()
{
mFile.close();
}
private:
void fill (size_t newOrigin)
{
assert (mFile.tell () == mBufferExtent);
size_t newExtent = newOrigin + sBufferSize;
if (newExtent > mExtent)
newExtent = mExtent;
size_t oldExtent = mBufferExtent;
if (newOrigin != oldExtent)
mFile.seek (newOrigin);
mBufferOrigin = mBufferExtent = newOrigin;
size_t amountRequested = newExtent - newOrigin;
size_t amountRead = mFile.read (mBuffer, amountRequested);
if (amountRead != amountRequested)
throw std::runtime_error ("An unexpected condition occurred while reading from a file.");
mBufferExtent = newExtent;
}
LowLevelFile mFile;
size_t mOrigin;
size_t mExtent;
size_t mPos;
uint8_t mBuffer [sBufferSize];
size_t mBufferOrigin;
size_t mBufferExtent;
};
} // end of unnamed namespace
Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset, size_t length)
{
return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, offset, length));
}

View file

@ -0,0 +1,8 @@
#ifndef COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP
#define COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP
#include <OgreDataStream.h>
Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset = 0, size_t length = 0xFFFFFFFF);
#endif // COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP

View file

@ -0,0 +1,299 @@
#include "lowlevelfile.hpp"
#include <stdexcept>
#include <sstream>
#include <cassert>
#if FILE_API == FILE_API_POSIX
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#endif
#if FILE_API == FILE_API_STDIO
/*
*
* Implementation of LowLevelFile methods using c stdio
*
*/
LowLevelFile::LowLevelFile ()
{
mHandle = NULL;
}
LowLevelFile::~LowLevelFile ()
{
if (mHandle != NULL)
fclose (mHandle);
}
void LowLevelFile::open (char const * filename)
{
assert (mHandle == NULL);
mHandle = fopen (filename, "rb");
if (mHandle == NULL)
{
std::ostringstream os;
os << "Failed to open '" << filename << "' for reading.";
throw std::runtime_error (os.str ());
}
}
void LowLevelFile::close ()
{
assert (mHandle != NULL);
fclose (mHandle);
mHandle = NULL;
}
size_t LowLevelFile::size ()
{
assert (mHandle != NULL);
long oldPosition = ftell (mHandle);
if (oldPosition == -1)
throw std::runtime_error ("A query operation on a file failed.");
if (fseek (mHandle, 0, SEEK_END) != 0)
throw std::runtime_error ("A query operation on a file failed.");
long Size = ftell (mHandle);
if (Size == -1)
throw std::runtime_error ("A query operation on a file failed.");
if (fseek (mHandle, oldPosition, SEEK_SET) != 0)
throw std::runtime_error ("A query operation on a file failed.");
return size_t (Size);
}
void LowLevelFile::seek (size_t Position)
{
assert (mHandle != NULL);
if (fseek (mHandle, Position, SEEK_SET) != 0)
throw std::runtime_error ("A seek operation on a file failed.");
}
size_t LowLevelFile::tell ()
{
assert (mHandle != NULL);
long Position = ftell (mHandle);
if (Position == -1)
throw std::runtime_error ("A query operation on a file failed.");
return size_t (Position);
}
size_t LowLevelFile::read (void * data, size_t size)
{
assert (mHandle != NULL);
int amount = fread (data, 1, size, mHandle);
if (amount == 0 && ferror (mHandle))
throw std::runtime_error ("A read operation on a file failed.");
return amount;
}
#elif FILE_API == FILE_API_POSIX
/*
*
* Implementation of LowLevelFile methods using posix IO calls
*
*/
LowLevelFile::LowLevelFile ()
{
mHandle = -1;
}
LowLevelFile::~LowLevelFile ()
{
if (mHandle != -1)
::close (mHandle);
}
void LowLevelFile::open (char const * filename)
{
assert (mHandle == -1);
#ifdef O_BINARY
static const int openFlags = O_RDONLY | O_BINARY;
#else
static const int openFlags = O_RDONLY;
#endif
mHandle = ::open (filename, openFlags, 0);
if (mHandle == -1)
{
std::ostringstream os;
os << "Failed to open '" << filename << "' for reading.";
throw std::runtime_error (os.str ());
}
}
void LowLevelFile::close ()
{
assert (mHandle != -1);
::close (mHandle);
mHandle = -1;
}
size_t LowLevelFile::size ()
{
assert (mHandle != -1);
size_t oldPosition = ::lseek (mHandle, 0, SEEK_CUR);
if (oldPosition == size_t (-1))
throw std::runtime_error ("A query operation on a file failed.");
size_t Size = ::lseek (mHandle, 0, SEEK_END);
if (Size == size_t (-1))
throw std::runtime_error ("A query operation on a file failed.");
if (lseek (mHandle, oldPosition, SEEK_SET) == -1)
throw std::runtime_error ("A query operation on a file failed.");
return Size;
}
void LowLevelFile::seek (size_t Position)
{
assert (mHandle != -1);
if (::lseek (mHandle, Position, SEEK_SET) == -1)
throw std::runtime_error ("A seek operation on a file failed.");
}
size_t LowLevelFile::tell ()
{
assert (mHandle != -1);
size_t Position = ::lseek (mHandle, 0, SEEK_CUR);
if (Position == size_t (-1))
throw std::runtime_error ("A query operation on a file failed.");
return Position;
}
size_t LowLevelFile::read (void * data, size_t size)
{
assert (mHandle != -1);
int amount = ::read (mHandle, data, size);
if (amount == -1)
throw std::runtime_error ("A read operation on a file failed.");
return amount;
}
#elif FILE_API == FILE_API_WIN32
/*
*
* Implementation of LowLevelFile methods using Win32 API calls
*
*/
LowLevelFile::LowLevelFile ()
{
mHandle = INVALID_HANDLE_VALUE;
}
LowLevelFile::~LowLevelFile ()
{
if (mHandle == INVALID_HANDLE_VALUE)
CloseHandle (mHandle);
}
void LowLevelFile::open (char const * filename)
{
assert (mHandle == INVALID_HANDLE_VALUE);
HANDLE handle = CreateFileA (filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (handle == NULL)
{
std::ostringstream os;
os << "Failed to open '" << filename << "' for reading.";
throw std::runtime_error (os.str ());
}
mHandle = handle;
}
void LowLevelFile::close ()
{
assert (mHandle != INVALID_HANDLE_VALUE);
CloseHandle (mHandle);
mHandle = INVALID_HANDLE_VALUE;
}
size_t LowLevelFile::size ()
{
assert (mHandle != INVALID_HANDLE_VALUE);
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle (mHandle, &info))
throw std::runtime_error ("A query operation on a file failed.");
if (info.nFileSizeHigh != 0)
throw std::runtime_error ("Files greater that 4GB are not supported.");
return info.nFileSizeLow;
}
void LowLevelFile::seek (size_t Position)
{
assert (mHandle != INVALID_HANDLE_VALUE);
if (SetFilePointer (mHandle, Position, NULL, SEEK_SET) == INVALID_SET_FILE_POINTER)
if (GetLastError () != NO_ERROR)
throw std::runtime_error ("A seek operation on a file failed.");
}
size_t LowLevelFile::tell ()
{
assert (mHandle != INVALID_HANDLE_VALUE);
DWORD value = SetFilePointer (mHandle, 0, NULL, SEEK_CUR);
if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)
throw std::runtime_error ("A query operation on a file failed.");
return value;
}
size_t LowLevelFile::read (void * data, size_t size)
{
assert (mHandle != INVALID_HANDLE_VALUE);
DWORD read;
if (!ReadFile (mHandle, data, size, &read, NULL))
throw std::runtime_error ("A read operation on a file failed.");
return read;
}
#endif

View file

@ -0,0 +1,56 @@
#ifndef COMPONENTS_FILES_LOWLEVELFILE_HPP
#define COMPONENTS_FILES_LOWLEVELFILE_HPP
#include <OgrePlatform.h>
#include <cstdlib>
#define FILE_API_STDIO 0
#define FILE_API_POSIX 1
#define FILE_API_WIN32 2
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
#define FILE_API FILE_API_POSIX
#elif OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define FILE_API FILE_API_WIN32
#else
#define FILE_API FILE_API_STDIO
#endif
#if FILE_API == FILE_API_STDIO
#include <cstdio>
#elif FILE_API == FILE_API_POSIX
#elif FILE_API == FILE_API_WIN32
#include <windows.h>
#else
#error Unsupported File API
#endif
class LowLevelFile
{
public:
LowLevelFile ();
~LowLevelFile ();
void open (char const * filename);
void close ();
size_t size ();
void seek (size_t Position);
size_t tell ();
size_t read (void * data, size_t size);
private:
#if FILE_API == FILE_API_STDIO
FILE* mHandle;
#elif FILE_API == FILE_API_POSIX
int mHandle;
#elif FILE_API == FILE_API_WIN32
HANDLE mHandle;
#endif
};
#endif

View file

@ -43,16 +43,26 @@
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>
#include <components/nif/node.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/nifoverrides/nifoverrides.hpp> #include <components/nifoverrides/nifoverrides.hpp>
typedef unsigned char ubyte; typedef unsigned char ubyte;
using namespace std; namespace std
using namespace Nif; {
using namespace NifOgre;
// These operators allow extra data types to be stored in an Ogre::Any
// object, which can then be stored in user object bindings on the nodes
// TODO: Do something useful
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
{ return o; }
}
namespace NifOgre
{
// Helper class that computes the bounding box and of a mesh // Helper class that computes the bounding box and of a mesh
class BoundsFinder class BoundsFinder
{ {
@ -62,7 +72,7 @@ class BoundsFinder
MaxMinFinder() MaxMinFinder()
{ {
min = numeric_limits<float>::infinity(); min = std::numeric_limits<float>::infinity();
max = -min; max = -min;
} }
@ -152,8 +162,9 @@ static void fail(const std::string &msg)
} }
static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textkeys) static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk)
{ {
TextKeyMap textkeys;
for(size_t i = 0;i < tk->list.size();i++) for(size_t i = 0;i < tk->list.size();i++)
{ {
const std::string &str = tk->list[i].text; const std::string &str = tk->list[i].text;
@ -166,11 +177,12 @@ static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textke
break; break;
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); 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))); textkeys.insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos)));
pos = nextpos; pos = nextpos;
} }
} }
return textkeys;
} }
@ -197,6 +209,17 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector<Nif::Ni
ctrl = ctrl->next; ctrl = ctrl->next;
} }
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiTextKeyExtraData)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk)));
}
e = e->extra;
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode) if(ninode)
{ {
@ -219,7 +242,7 @@ struct KeyTimeSort
}; };
typedef std::map<std::string,NIFSkeletonLoader,ciLessBoost> LoaderMap; typedef std::map<std::string,NIFSkeletonLoader> LoaderMap;
static LoaderMap sLoaders; static LoaderMap sLoaders;
public: public:
@ -269,16 +292,16 @@ void loadResource(Ogre::Resource *resource)
Nif::NiKeyframeData *kf = kfc->data.getPtr(); Nif::NiKeyframeData *kf = kfc->data.getPtr();
/* Get the keyframes and make sure they're sorted first to last */ /* Get the keyframes and make sure they're sorted first to last */
QuaternionKeyList quatkeys = kf->mRotations; Nif::QuaternionKeyList quatkeys = kf->mRotations;
Vector3KeyList trankeys = kf->mTranslations; Nif::Vector3KeyList trankeys = kf->mTranslations;
FloatKeyList scalekeys = kf->mScales; Nif::FloatKeyList scalekeys = kf->mScales;
std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort<Ogre::Quaternion>()); std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort<Ogre::Quaternion>());
std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort<Ogre::Vector3>()); std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort<Ogre::Vector3>());
std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort<float>()); std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort<float>());
QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
Ogre::Bone *bone = skel->getBone(targets[i]); Ogre::Bone *bone = skel->getBone(targets[i]);
const Ogre::Quaternion startquat = bone->getInitialOrientation(); const Ogre::Quaternion startquat = bone->getInitialOrientation();
@ -346,7 +369,7 @@ void loadResource(Ogre::Resource *resource)
kframe->setRotation(curquat); kframe->setRotation(curquat);
else else
{ {
QuaternionKeyList::VecType::const_iterator last = quatiter-1; Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1;
float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
} }
@ -354,7 +377,7 @@ void loadResource(Ogre::Resource *resource)
kframe->setTranslate(curtrans); kframe->setTranslate(curtrans);
else else
{ {
Vector3KeyList::VecType::const_iterator last = traniter-1; Nif::Vector3KeyList::VecType::const_iterator last = traniter-1;
float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
} }
@ -362,7 +385,7 @@ void loadResource(Ogre::Resource *resource)
kframe->setScale(curscale); kframe->setScale(curscale);
else else
{ {
FloatKeyList::VecType::const_iterator last = scaleiter-1; Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1;
float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
kframe->setScale(lastscale + ((curscale-lastscale)*diff)); kframe->setScale(lastscale + ((curscale-lastscale)*diff));
} }
@ -371,34 +394,17 @@ void loadResource(Ogre::Resource *resource)
anim->optimise(); anim->optimise();
} }
bool createSkeleton(const std::string &name, const std::string &group, TextKeyMap *textkeys, const Nif::Node *node) bool createSkeleton(const std::string &name, const std::string &group, 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<const Nif::NiTextKeyExtraData*>(e.getPtr());
insertTextKeys(tk, textkeys);
}
e = e->extra;
}
}
if(node->boneTrafo != NULL) if(node->boneTrafo != NULL)
{ {
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
Ogre::SkeletonPtr skel = skelMgr.getByName(name); Ogre::SkeletonPtr skel = skelMgr.getByName(name);
if(skel.isNull()) if(skel.isNull())
{ {
NIFSkeletonLoader *loader = &sLoaders[name]; NIFSkeletonLoader *loader = &sLoaders[name];
skel = skelMgr.create(name, group, true, loader); skel = skelMgr.create(name, group, true, loader);
} }
if(!textkeys || textkeys->size() > 0)
return true; return true;
} }
@ -410,7 +416,7 @@ bool createSkeleton(const std::string &name, const std::string &group, TextKeyMa
{ {
if(!children[i].empty()) if(!children[i].empty())
{ {
if(createSkeleton(name, group, textkeys, children[i].getPtr())) if(createSkeleton(name, group, children[i].getPtr()))
return true; return true;
} }
} }
@ -484,7 +490,7 @@ static void fail(const std::string &msg)
public: public:
static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String &name, const Ogre::String &group)
{ {
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
Ogre::MaterialPtr material = matMgr.getByName(name); Ogre::MaterialPtr material = matMgr.getByName(name);
@ -504,24 +510,24 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam
bool vertexColour = (shape->data->colors.size() != 0); bool vertexColour = (shape->data->colors.size() != 0);
// These are set below if present // These are set below if present
const NiTexturingProperty *t = NULL; const Nif::NiTexturingProperty *t = NULL;
const NiMaterialProperty *m = NULL; const Nif::NiMaterialProperty *m = NULL;
const NiAlphaProperty *a = NULL; const Nif::NiAlphaProperty *a = NULL;
// Scan the property list for material information // Scan the property list for material information
const PropertyList &list = shape->props; const Nif::PropertyList &list = shape->props;
for (size_t i = 0;i < list.length();i++) for (size_t i = 0;i < list.length();i++)
{ {
// Entries may be empty // Entries may be empty
if (list[i].empty()) continue; if (list[i].empty()) continue;
const Property *pr = list[i].getPtr(); const Nif::Property *pr = list[i].getPtr();
if (pr->recType == RC_NiTexturingProperty) if (pr->recType == Nif::RC_NiTexturingProperty)
t = static_cast<const NiTexturingProperty*>(pr); t = static_cast<const Nif::NiTexturingProperty*>(pr);
else if (pr->recType == RC_NiMaterialProperty) else if (pr->recType == Nif::RC_NiMaterialProperty)
m = static_cast<const NiMaterialProperty*>(pr); m = static_cast<const Nif::NiMaterialProperty*>(pr);
else if (pr->recType == RC_NiAlphaProperty) else if (pr->recType == Nif::RC_NiAlphaProperty)
a = static_cast<const NiAlphaProperty*>(pr); a = static_cast<const Nif::NiAlphaProperty*>(pr);
else else
warn("Skipped property type: "+pr->recName); warn("Skipped property type: "+pr->recName);
} }
@ -529,18 +535,27 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam
// Texture // Texture
if (t && t->textures[0].inUse) if (t && t->textures[0].inUse)
{ {
NiSourceTexture *st = t->textures[0].texture.getPtr(); Nif::NiSourceTexture *st = t->textures[0].texture.getPtr();
if (st->external) if (st->external)
{ {
/* Bethesda at some at some point converted all their BSA /* Bethesda at some at some point converted all their BSA
* textures from tga to dds for increased load speed, but all * textures from tga to dds for increased load speed, but all
* texture file name references were kept as .tga. * texture file name references were kept as .tga.
*/ */
texName = "textures\\" + st->filename; static const char path[] = "textures\\";
if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName))
{ texName = path + st->filename;
Ogre::String::size_type pos = texName.rfind('.'); Ogre::String::size_type pos = texName.rfind('.');
if(pos != Ogre::String::npos && texName.compare(pos, texName.length() - pos, ".dds") != 0)
{
// since we know all (GOTY edition or less) textures end
// in .dds, we change the extension
texName.replace(pos, texName.length(), ".dds"); texName.replace(pos, texName.length(), ".dds");
// if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods)
// verify, and revert if false (this call succeeds quickly, but fails slowly)
if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName))
texName = path + st->filename;
} }
} }
else warn("Found internal texture, ignoring."); else warn("Found internal texture, ignoring.");
@ -918,7 +933,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
} }
typedef std::map<std::string,NIFMeshLoader,ciLessBoost> LoaderMap; typedef std::map<std::string,NIFMeshLoader> LoaderMap;
static LoaderMap sLoaders; static LoaderMap sLoaders;
public: public:
@ -981,7 +996,7 @@ public:
if(node->recType == Nif::RC_NiTriShape) if(node->recType == Nif::RC_NiTriShape)
{ {
const NiTriShape *shape = dynamic_cast<const NiTriShape*>(node); const Nif::NiTriShape *shape = dynamic_cast<const Nif::NiTriShape*>(node);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
std::string fullname = mName+"@shape="+shape->name; std::string fullname = mName+"@shape="+shape->name;
@ -1004,7 +1019,7 @@ public:
mesh->setAutoBuildEdgeLists(false); mesh->setAutoBuildEdgeLists(false);
} }
meshes.push_back(std::make_pair(mesh, shape->name)); meshes.push_back(std::make_pair(mesh->getName(), shape->name));
} }
else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode &&
node->recType != Nif::RC_NiRotatingParticles) node->recType != Nif::RC_NiRotatingParticles)
@ -1025,13 +1040,19 @@ public:
NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group) typedef std::map<std::string,MeshPairList> MeshPairMap;
{ static MeshPairMap sMeshPairMap;
MeshPairList meshes;
MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group)
{
std::transform(name.begin(), name.end(), name.begin(), ::tolower); std::transform(name.begin(), name.end(), name.begin(), ::tolower);
std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower); std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower);
MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName);
if(meshiter != sMeshPairMap.end())
return meshiter->second;
MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName];
Nif::NIFFile nif(name); Nif::NIFFile nif(name);
if (nif.numRecords() < 1) if (nif.numRecords() < 1)
{ {
@ -1052,7 +1073,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap
} }
NIFSkeletonLoader skelldr; NIFSkeletonLoader skelldr;
bool hasSkel = skelldr.createSkeleton(skelName, group, textkeys, node); bool hasSkel = skelldr.createSkeleton(name, group, node);
NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string()));
meshldr.createMeshes(node, meshes); meshldr.createMeshes(node, meshes);
@ -1064,19 +1085,37 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke
{ {
EntityList entitylist; EntityList entitylist;
MeshPairList meshes = load(name, name, textkeys, group); MeshPairList meshes = load(name, name, group);
if(meshes.size() == 0) if(meshes.size() == 0)
return entitylist; return entitylist;
Ogre::SceneManager *sceneMgr = parent->getCreator(); Ogre::SceneManager *sceneMgr = parent->getCreator();
for(size_t i = 0;i < meshes.size();i++) for(size_t i = 0;i < meshes.size();i++)
{ {
entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first));
Ogre::Entity *entity = entitylist.mEntities.back(); Ogre::Entity *entity = entitylist.mEntities.back();
if(!entitylist.mSkelBase && entity->hasSkeleton()) if(!entitylist.mSkelBase && entity->hasSkeleton())
entitylist.mSkelBase = entity; entitylist.mSkelBase = entity;
} }
if(entitylist.mSkelBase && textkeys)
{
// Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr.
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
Ogre::SkeletonPtr skel = skelMgr.getByName(entitylist.mSkelBase->getSkeleton()->getName());
Ogre::Skeleton::BoneIterator iter = skel->getBoneIterator();
while(iter.hasMoreElements())
{
Ogre::Bone *bone = iter.getNext();
const Ogre::Any &data = bone->getUserObjectBindings().getUserAny("TextKeyExtraData");
if(!data.isEmpty())
{
*textkeys = Ogre::any_cast<TextKeyMap>(data);
break;
}
}
}
if(entitylist.mSkelBase) if(entitylist.mSkelBase)
{ {
parent->attachObject(entitylist.mSkelBase); parent->attachObject(entitylist.mSkelBase);
@ -1101,13 +1140,6 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke
return entitylist; return entitylist;
} }
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, EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode, Ogre::SceneNode *parentNode,
const std::string &name, const std::string &name,
@ -1115,23 +1147,24 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo
{ {
EntityList entitylist; EntityList entitylist;
MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), NULL, group); MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group);
if(meshes.size() == 0) if(meshes.size() == 0)
return entitylist; return entitylist;
Ogre::SceneManager *sceneMgr = parentNode->getCreator(); Ogre::SceneManager *sceneMgr = parentNode->getCreator();
std::string filter = "Tri "+bonename; std::string filter = "tri "+bonename;
std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower);
for(size_t i = 0;i < meshes.size();i++) for(size_t i = 0;i < meshes.size();i++)
{ {
Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first);
if(ent->hasSkeleton()) if(ent->hasSkeleton())
{ {
std::transform(meshes[i].second.begin(), meshes[i].second.end(), meshes[i].second.begin(), ::tolower);
if(meshes[i].second.length() < filter.length() || if(meshes[i].second.length() < filter.length() ||
std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow()).first != filter.end()) meshes[i].second.compare(0, filter.length(), filter) != 0)
{ {
sceneMgr->destroyEntity(ent); sceneMgr->destroyEntity(ent);
meshes.erase(meshes.begin()+i);
i--;
continue; continue;
} }
if(!entitylist.mSkelBase) if(!entitylist.mSkelBase)
@ -1216,3 +1249,5 @@ extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height,
*/ */
} // nsmaepace NifOgre

View file

@ -30,23 +30,6 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <cassert>
#include <boost/algorithm/string.hpp>
#include "../nif/node.hpp"
#include <libs/platform/strings.h>
class BoundsFinder;
struct ciLessBoost : std::binary_function<std::string, std::string, bool>
{
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());
}
};
namespace Nif namespace Nif
{ {
@ -69,9 +52,8 @@ struct EntityList {
}; };
/** This holds a list of meshes along with the names of their parent nodes /** This holds a list of mesh names along with the names of their parent nodes */
*/ typedef std::vector< std::pair<std::string,std::string> > MeshPairList;
typedef std::vector< std::pair<Ogre::MeshPtr,std::string> > MeshPairList;
/** Manual resource loader for NIF meshes. This is the main class /** Manual resource loader for NIF meshes. This is the main class
responsible for translating the internal NIF mesh structure into responsible for translating the internal NIF mesh structure into
@ -87,7 +69,7 @@ typedef std::vector< std::pair<Ogre::MeshPtr,std::string> > MeshPairList;
*/ */
class NIFLoader class NIFLoader
{ {
static MeshPairList load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group); static MeshPairList load(std::string name, std::string skelName, const std::string &group);
public: public:
static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename,

1
components/to_utf8/tests/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*_test

View file

@ -0,0 +1,4 @@
original: Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?
converted: Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?
original: Vous lui donnez le gâteau sans protester avant daller chercher tous vos amis et de revenir vous venger.
converted: Vous lui donnez le gâteau sans protester avant daller chercher tous vos amis et de revenir vous venger.

View file

@ -0,0 +1,18 @@
#!/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

View file

@ -0,0 +1 @@
Vous lui donnez le gâteau sans protester avant daller chercher tous vos amis et de revenir vous venger.

View file

@ -0,0 +1 @@
Vous lui donnez le gâteau sans protester avant daller chercher tous vos amis et de revenir vous venger.

View file

@ -0,0 +1 @@
Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?

View file

@ -0,0 +1 @@
Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам?

View file

@ -0,0 +1,59 @@
#include <iostream>
#include <fstream>
#include <cassert>
#include <stdexcept>
#include "../to_utf8.hpp"
std::string getFirstLine(const std::string &filename);
void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile,
const std::string &utf8File);
/// Test character encoding conversion to and from UTF-8
void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile,
const std::string &utf8File)
{
// get some test data
std::string legacyEncLine = getFirstLine(legacyEncFile);
std::string utf8Line = getFirstLine(utf8File);
// create an encoder for specified character encoding
ToUTF8::Utf8Encoder encoder (encoding);
// convert text to UTF-8
std::string convertedUtf8Line = encoder.getUtf8(legacyEncLine);
std::cout << "original: " << utf8Line << std::endl;
std::cout << "converted: " << convertedUtf8Line << std::endl;
// check correctness
assert(convertedUtf8Line == utf8Line);
// convert UTF-8 text to legacy encoding
std::string convertedLegacyEncLine = encoder.getLegacyEnc(utf8Line);
// check correctness
assert(convertedLegacyEncLine == legacyEncLine);
}
std::string getFirstLine(const std::string &filename)
{
std::string line;
std::ifstream text (filename.c_str());
if (!text.is_open())
{
throw std::runtime_error("Unable to open file " + filename);
}
std::getline(text, line);
text.close();
return line;
}
int main()
{
testEncoder(ToUTF8::WINDOWS_1251, "test_data/russian-win1251.txt", "test_data/russian-utf8.txt");
testEncoder(ToUTF8::WINDOWS_1252, "test_data/french-win1252.txt", "test_data/french-utf8.txt");
return 0;
}

View file

@ -2,6 +2,8 @@
#include <vector> #include <vector>
#include <cassert> #include <cassert>
#include <iostream>
#include <iomanip>
/* This file contains the code to translate from WINDOWS-1252 (native /* This file contains the code to translate from WINDOWS-1252 (native
charset used in English version of Morrowind) to UTF-8. The library charset used in English version of Morrowind) to UTF-8. The library
@ -39,34 +41,128 @@
// Generated tables // Generated tables
#include "tables_gen.hpp" #include "tables_gen.hpp"
// Shared global buffers, we love you. These initial sizes are large using namespace ToUTF8;
// enough to hold the largest books in Morrowind.esm, but we will
// resize automaticall if necessary.
static std::vector<char> buf (50*1024);
static std::vector<char> output (50*1024);
static int size;
// Make sure the given vector is large enough for 'size' bytes, Utf8Encoder::Utf8Encoder(const FromType sourceEncoding):
// including a terminating zero after it. mOutput(50*1024)
static void resize(std::vector<char> &buf, size_t size)
{ {
if(buf.size() <= size) switch (sourceEncoding)
// Add some extra padding to reduce the chance of having to resize {
// again later. case ToUTF8::WINDOWS_1252:
buf.resize(3*size); {
translationArray = ToUTF8::windows_1252;
// And make sure the string is zero terminated break;
buf[size] = 0; }
case ToUTF8::WINDOWS_1250:
{
translationArray = ToUTF8::windows_1250;
break;
}
case ToUTF8::WINDOWS_1251:
{
translationArray = ToUTF8::windows_1251;
break;
}
default:
{
assert(0);
}
}
} }
// This is just used to spew out a reusable input buffer for the std::string Utf8Encoder::getUtf8(const char* input, int size)
// conversion process.
char *ToUTF8::getBuffer(int s)
{ {
// Remember the requested size // Double check that the input string stops at some point (it might
size = s; // contain zero terminators before this, inside its own data, which
resize(buf, size); // is also ok.)
return &buf[0]; assert(input[size] == 0);
// TODO: The rest of this function is designed for single-character
// input encodings only. It also assumes that the input the input
// encoding shares its first 128 values (0-127) with ASCII. These
// conditions must be checked again if you add more input encodings
// later.
// Compute output length, and check for pure ascii input at the same
// time.
bool ascii;
size_t outlen = getLength(input, ascii);
// If we're pure ascii, then don't bother converting anything.
if(ascii)
return std::string(input, outlen);
// Make sure the output is large enough
resize(outlen);
char *out = &mOutput[0];
// Translate
while (*input)
copyFromArray(*(input++), out);
// Make sure that we wrote the correct number of bytes
assert((out-&mOutput[0]) == (int)outlen);
// And make extra sure the output is null terminated
assert(mOutput.size() > outlen);
assert(mOutput[outlen] == 0);
// Return a string
return std::string(&mOutput[0], outlen);
}
std::string Utf8Encoder::getLegacyEnc(const char *input, int size)
{
// Double check that the input string stops at some point (it might
// contain zero terminators before this, inside its own data, which
// is also ok.)
assert(input[size] == 0);
// TODO: The rest of this function is designed for single-character
// input encodings only. It also assumes that the input the input
// encoding shares its first 128 values (0-127) with ASCII. These
// conditions must be checked again if you add more input encodings
// later.
// Compute output length, and check for pure ascii input at the same
// time.
bool ascii;
size_t outlen = getLength2(input, ascii);
// If we're pure ascii, then don't bother converting anything.
if(ascii)
return std::string(input, outlen);
// Make sure the output is large enough
resize(outlen);
char *out = &mOutput[0];
// Translate
while(*input)
copyFromArray2(input, out);
// Make sure that we wrote the correct number of bytes
assert((out-&mOutput[0]) == (int)outlen);
// And make extra sure the output is null terminated
assert(mOutput.size() > outlen);
assert(mOutput[outlen] == 0);
// Return a string
return std::string(&mOutput[0], outlen);
}
// Make sure the output vector is large enough for 'size' bytes,
// including a terminating zero after it.
void Utf8Encoder::resize(size_t size)
{
if (mOutput.size() <= size)
// Add some extra padding to reduce the chance of having to resize
// again later.
mOutput.resize(3*size);
// And make sure the string is zero terminated
mOutput[size] = 0;
} }
/** Get the total length length needed to decode the given string with /** Get the total length length needed to decode the given string with
@ -79,7 +175,7 @@ char *ToUTF8::getBuffer(int s)
is the case, then the ascii parameter is set to true, and the is the case, then the ascii parameter is set to true, and the
caller can optimize for this case. caller can optimize for this case.
*/ */
static size_t getLength(const char *arr, const char* input, bool &ascii) size_t Utf8Encoder::getLength(const char* input, bool &ascii)
{ {
ascii = true; ascii = true;
size_t len = 0; size_t len = 0;
@ -88,21 +184,21 @@ static size_t getLength(const char *arr, const char* input, bool &ascii)
// Do away with the ascii part of the string first (this is almost // Do away with the ascii part of the string first (this is almost
// always the entire string.) // always the entire string.)
while(inp && inp < 128) while (inp && inp < 128)
inp = *(++ptr); inp = *(++ptr);
len += (ptr-input); len += (ptr-input);
// If we're not at the null terminator at this point, then there // If we're not at the null terminator at this point, then there
// were some non-ascii characters to deal with. Go to slow-mode for // were some non-ascii characters to deal with. Go to slow-mode for
// the rest of the string. // the rest of the string.
if(inp) if (inp)
{ {
ascii = false; ascii = false;
while(inp) while (inp)
{ {
// Find the translated length of this character in the // Find the translated length of this character in the
// lookup table. // lookup table.
len += arr[inp*6]; len += translationArray[inp*6];
inp = *(++ptr); inp = *(++ptr);
} }
} }
@ -111,89 +207,22 @@ static size_t getLength(const char *arr, const char* input, bool &ascii)
// Translate one character 'ch' using the translation array 'arr', and // Translate one character 'ch' using the translation array 'arr', and
// advance the output pointer accordingly. // advance the output pointer accordingly.
static void copyFromArray(const char *arr, unsigned char ch, char* &out) void Utf8Encoder::copyFromArray(unsigned char ch, char* &out)
{ {
// Optimize for ASCII values // Optimize for ASCII values
if(ch < 128) if (ch < 128)
{ {
*(out++) = ch; *(out++) = ch;
return; return;
} }
const char *in = arr + ch*6; const char *in = translationArray + ch*6;
int len = *(in++); int len = *(in++);
for(int i=0; i<len; i++) for (int i=0; i<len; i++)
*(out++) = *(in++); *(out++) = *(in++);
} }
std::string ToUTF8::getUtf8(ToUTF8::FromType from) size_t Utf8Encoder::getLength2(const char* input, bool &ascii)
{
// Pick translation array
const char *arr;
switch (from)
{
case ToUTF8::WINDOWS_1252:
{
arr = ToUTF8::windows_1252;
break;
}
case ToUTF8::WINDOWS_1250:
{
arr = ToUTF8::windows_1250;
break;
}
case ToUTF8::WINDOWS_1251:
{
arr = ToUTF8::windows_1251;
break;
}
default:
{
assert(0);
}
}
// Double check that the input string stops at some point (it might
// contain zero terminators before this, inside its own data, which
// is also ok.)
const char* input = &buf[0];
assert(input[size] == 0);
// TODO: The rest of this function is designed for single-character
// input encodings only. It also assumes that the input the input
// encoding shares its first 128 values (0-127) with ASCII. These
// conditions must be checked again if you add more input encodings
// later.
// Compute output length, and check for pure ascii input at the same
// time.
bool ascii;
size_t outlen = getLength(arr, input, ascii);
// If we're pure ascii, then don't bother converting anything.
if(ascii)
return std::string(input, outlen);
// Make sure the output is large enough
resize(output, outlen);
char *out = &output[0];
// Translate
while(*input)
copyFromArray(arr, *(input++), out);
// Make sure that we wrote the correct number of bytes
assert((out-&output[0]) == (int)outlen);
// And make extra sure the output is null terminated
assert(output.size() > outlen);
assert(output[outlen] == 0);
// Return a string
return std::string(&output[0], outlen);
}
static size_t getLength2(const char *arr, const char* input, bool &ascii)
{ {
ascii = true; ascii = true;
size_t len = 0; size_t len = 0;
@ -202,14 +231,14 @@ static size_t getLength2(const char *arr, const char* input, bool &ascii)
// Do away with the ascii part of the string first (this is almost // Do away with the ascii part of the string first (this is almost
// always the entire string.) // always the entire string.)
while(inp && inp < 128) while (inp && inp < 128)
inp = *(++ptr); inp = *(++ptr);
len += (ptr-input); len += (ptr-input);
// If we're not at the null terminator at this point, then there // If we're not at the null terminator at this point, then there
// were some non-ascii characters to deal with. Go to slow-mode for // were some non-ascii characters to deal with. Go to slow-mode for
// the rest of the string. // the rest of the string.
if(inp) if (inp)
{ {
ascii = false; ascii = false;
while(inp) while(inp)
@ -237,14 +266,11 @@ static size_t getLength2(const char *arr, const char* input, bool &ascii)
return len; return len;
} }
#include <iostream> void Utf8Encoder::copyFromArray2(const char*& chp, char* &out)
#include <iomanip>
static void copyFromArray2(const char *arr, char*& chp, char* &out)
{ {
unsigned char ch = *(chp++); unsigned char ch = *(chp++);
// Optimize for ASCII values // Optimize for ASCII values
if(ch < 128) if (ch < 128)
{ {
*(out++) = ch; *(out++) = ch;
return; return;
@ -278,7 +304,7 @@ static void copyFromArray2(const char *arr, char*& chp, char* &out)
for (int i = 128; i < 256; i++) for (int i = 128; i < 256; i++)
{ {
unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; unsigned char b1 = translationArray[i*6 + 1], b2 = translationArray[i*6 + 2], b3 = translationArray[i*6 + 3];
if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3))
{ {
*(out++) = (char)i; *(out++) = (char)i;
@ -291,73 +317,6 @@ static void copyFromArray2(const char *arr, char*& chp, char* &out)
*(out++) = ch; // Could not find glyph, just put whatever *(out++) = ch; // Could not find glyph, just put whatever
} }
std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to)
{
// Pick translation array
const char *arr;
switch (to)
{
case ToUTF8::WINDOWS_1252:
{
arr = ToUTF8::windows_1252;
break;
}
case ToUTF8::WINDOWS_1250:
{
arr = ToUTF8::windows_1250;
break;
}
case ToUTF8::WINDOWS_1251:
{
arr = ToUTF8::windows_1251;
break;
}
default:
{
assert(0);
}
}
// Double check that the input string stops at some point (it might
// contain zero terminators before this, inside its own data, which
// is also ok.)
char* input = &buf[0];
assert(input[size] == 0);
// TODO: The rest of this function is designed for single-character
// input encodings only. It also assumes that the input the input
// encoding shares its first 128 values (0-127) with ASCII. These
// conditions must be checked again if you add more input encodings
// later.
// Compute output length, and check for pure ascii input at the same
// time.
bool ascii;
size_t outlen = getLength2(arr, input, ascii);
// If we're pure ascii, then don't bother converting anything.
if(ascii)
return std::string(input, outlen);
// Make sure the output is large enough
resize(output, outlen);
char *out = &output[0];
// Translate
while(*input)
copyFromArray2(arr, input, out);
// Make sure that we wrote the correct number of bytes
assert((out-&output[0]) == (int)outlen);
// And make extra sure the output is null terminated
assert(output.size() > outlen);
assert(output[outlen] == 0);
// Return a string
return std::string(&output[0], outlen);
}
ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName)
{ {
if (encodingName == "win1250") if (encodingName == "win1250")

View file

@ -2,6 +2,8 @@
#define COMPONENTS_TOUTF8_H #define COMPONENTS_TOUTF8_H
#include <string> #include <string>
#include <cstring>
#include <vector>
namespace ToUTF8 namespace ToUTF8
{ {
@ -14,17 +16,39 @@ namespace ToUTF8
// probably others) // probably others)
}; };
// Return a writable buffer of at least 'size' bytes. The buffer
// does not have to be freed.
char* getBuffer(int size);
// Convert the previously written buffer to UTF8 from the given code
// page.
std::string getUtf8(FromType from);
std::string getLegacyEnc(FromType to);
FromType calculateEncoding(const std::string& encodingName); FromType calculateEncoding(const std::string& encodingName);
std::string encodingUsingMessage(const std::string& encodingName); std::string encodingUsingMessage(const std::string& encodingName);
// class
class Utf8Encoder
{
public:
Utf8Encoder(FromType sourceEncoding);
// Convert to UTF8 from the previously given code page.
std::string getUtf8(const char *input, int size);
inline std::string getUtf8(const std::string &str)
{
return getUtf8(str.c_str(), str.size());
}
std::string getLegacyEnc(const char *input, int size);
inline std::string getLegacyEnc(const std::string &str)
{
return getLegacyEnc(str.c_str(), str.size());
}
private:
void resize(size_t size);
size_t getLength(const char* input, bool &ascii);
void copyFromArray(unsigned char chp, char* &out);
size_t getLength2(const char* input, bool &ascii);
void copyFromArray2(const char*& chp, char* &out);
std::vector<char> mOutput;
char* translationArray;
};
} }
#endif #endif

View file

@ -30,7 +30,7 @@ namespace Translation
{ {
std::string path = dataFileCollections.getCollection (extension).getPath (fileName).string(); std::string path = dataFileCollections.getCollection (extension).getPath (fileName).string();
std::ifstream stream (path); std::ifstream stream (path.c_str());
if (!stream.is_open()) if (!stream.is_open())
throw std::runtime_error ("failed to open translation file: " + fileName); throw std::runtime_error ("failed to open translation file: " + fileName);
@ -50,10 +50,7 @@ namespace Translation
if (!line.empty()) if (!line.empty())
{ {
char* buffer = ToUTF8::getBuffer(line.size() + 1); line = mEncoder->getUtf8(line);
//buffer has at least line.size() + 1 bytes, so it must be safe
strcpy(buffer, line.c_str());
line = ToUTF8::getUtf8(mEncoding);
size_t tab_pos = line.find('\t'); size_t tab_pos = line.find('\t');
if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1)
@ -104,9 +101,9 @@ namespace Translation
return phrase; return phrase;
} }
void Storage::setEncoding (const ToUTF8::FromType& encoding) void Storage::setEncoder(ToUTF8::Utf8Encoder* encoder)
{ {
mEncoding = encoding; mEncoder = encoder;
} }
bool Storage::hasTranslation() const bool Storage::hasTranslation() const

View file

@ -19,7 +19,7 @@ namespace Translation
// Standard form usually means nominative case // Standard form usually means nominative case
std::string topicStandardForm(const std::string& phrase) const; std::string topicStandardForm(const std::string& phrase) const;
void setEncoding (const ToUTF8::FromType& encoding); void setEncoder(ToUTF8::Utf8Encoder* encoder);
bool hasTranslation() const; bool hasTranslation() const;
@ -34,7 +34,7 @@ namespace Translation
void loadDataFromStream(ContainerType& container, std::istream& stream); void loadDataFromStream(ContainerType& container, std::istream& stream);
ToUTF8::FromType mEncoding; ToUTF8::Utf8Encoder* mEncoder;
ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms; ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms;
}; };
} }

View file

@ -72,7 +72,7 @@ compositor gbufferFinalizer
pass render_scene pass render_scene
{ {
first_render_queue 51 first_render_queue 51
last_render_queue 100 last_render_queue 105
} }
} }
target_output target_output

View file

@ -6,6 +6,19 @@
using namespace OEngine::GUI; using namespace OEngine::GUI;
/*
* As of MyGUI 3.2.0, MyGUI::OgreDataManager::isDataExist is unnecessarily complex
* this override fixes the resulting performance issue.
*/
class FixedOgreDataManager : public MyGUI::OgreDataManager
{
public:
bool isDataExist(const std::string& _name)
{
return Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup (_name);
}
};
void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir)
{ {
assert(wnd); assert(wnd);
@ -25,11 +38,18 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
if(!logDir.empty()) if(!logDir.empty())
theLogFile.insert(0, logDir); theLogFile.insert(0, logDir);
// Set up OGRE platform. We might make this more generic later. // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later.
mPlatform = new OgrePlatform(); mLogManager = new LogManager();
LogManager::getInstance().setSTDOutputEnabled(logging); mRenderManager = new OgreRenderManager();
mPlatform->initialise(wnd, mgr, "General", theLogFile); mDataManager = new FixedOgreDataManager();
LogManager::getInstance().setSTDOutputEnabled(logging);
if (!theLogFile.empty())
LogManager::getInstance().createDefaultSource(theLogFile);
mRenderManager->initialise(wnd, mgr);
mDataManager->initialise("General");
// Create GUI // Create GUI
mGui = new Gui(); mGui = new Gui();
@ -40,11 +60,22 @@ void MyGUIManager::shutdown()
{ {
mGui->shutdown (); mGui->shutdown ();
delete mGui; delete mGui;
if(mPlatform) if(mRenderManager)
{ {
mPlatform->shutdown(); mRenderManager->shutdown();
delete mPlatform; delete mRenderManager;
mRenderManager = NULL;
}
if(mDataManager)
{
mDataManager->shutdown();
delete mDataManager;
mDataManager = NULL;
}
if (mLogManager)
{
delete mLogManager;
mLogManager = NULL;
} }
mGui = NULL; mGui = NULL;
mPlatform = NULL;
} }

View file

@ -3,8 +3,10 @@
namespace MyGUI namespace MyGUI
{ {
class OgrePlatform;
class Gui; class Gui;
class LogManager;
class OgreDataManager;
class OgreRenderManager;
} }
namespace Ogre namespace Ogre
@ -18,12 +20,15 @@ namespace GUI
{ {
class MyGUIManager class MyGUIManager
{ {
MyGUI::OgrePlatform *mPlatform;
MyGUI::Gui *mGui; MyGUI::Gui *mGui;
MyGUI::LogManager* mLogManager;
MyGUI::OgreDataManager* mDataManager;
MyGUI::OgreRenderManager* mRenderManager;
Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mSceneMgr;
public: public:
MyGUIManager() : mPlatform(NULL), mGui(NULL) {} MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {}
MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""))
{ {
setup(wnd,mgr,logging, logDir); setup(wnd,mgr,logging, logDir);

View file

@ -16,6 +16,8 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest
{ {
Root* root = Ogre::Root::getSingletonPtr(); Root* root = Ogre::Root::getSingletonPtr();
std::string destImageRot = std::string(destImage) + std::string("_rot");
SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC);
Camera* camera = sceneMgr->createCamera("ImageRotateCamera"); Camera* camera = sceneMgr->createCamera("ImageRotateCamera");
@ -48,8 +50,8 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest
unsigned int width = sourceTexture->getWidth(); unsigned int width = sourceTexture->getWidth();
unsigned int height = sourceTexture->getHeight(); unsigned int height = sourceTexture->getHeight();
TexturePtr destTexture = TextureManager::getSingleton().createManual( TexturePtr destTextureRot = TextureManager::getSingleton().createManual(
destImage, destImageRot,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D, TEX_TYPE_2D,
width, height, width, height,
@ -57,7 +59,7 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest
PF_FLOAT16_RGBA, PF_FLOAT16_RGBA,
TU_RENDERTARGET); TU_RENDERTARGET);
RenderTarget* rtt = destTexture->getBuffer()->getRenderTarget(); RenderTarget* rtt = destTextureRot->getBuffer()->getRenderTarget();
rtt->setAutoUpdated(false); rtt->setAutoUpdated(false);
Viewport* vp = rtt->addViewport(camera); Viewport* vp = rtt->addViewport(camera);
vp->setOverlaysEnabled(false); vp->setOverlaysEnabled(false);
@ -66,7 +68,20 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest
rtt->update(); rtt->update();
//copy the rotated image to a static texture
TexturePtr destTexture = TextureManager::getSingleton().createManual(
destImage,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D,
width, height,
0,
PF_FLOAT16_RGBA,
Ogre::TU_STATIC);
destTexture->getBuffer()->blit(destTextureRot->getBuffer());
// remove all the junk we've created // remove all the junk we've created
TextureManager::getSingleton().remove(destImageRot);
MaterialManager::getSingleton().remove("ImageRotateMaterial"); MaterialManager::getSingleton().remove("ImageRotateMaterial");
root->destroySceneManager(sceneMgr); root->destroySceneManager(sceneMgr);
delete rect; delete rect;

View file

@ -226,7 +226,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings&
1, 1, 1, 1,
0, 0,
Ogre::PF_A8R8G8B8, Ogre::PF_A8R8G8B8,
Ogre::TU_DYNAMIC_WRITE_ONLY); Ogre::TU_WRITE_ONLY);
} }
void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip) void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip)