From 3127602c573f1eccc497baf9606429f305220077 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 20 Aug 2010 12:56:46 +0200 Subject: [PATCH 1/4] Added file_finder component. Used by sound system. --- CMakeLists.txt | 12 ++++- apps/openmw/mwsound/soundmanager.cpp | 30 +++++++----- components/file_finder/file_finder.hpp | 62 ++++++++++++++++++++++++ components/file_finder/filename_less.hpp | 49 +++++++++++++++++++ components/file_finder/search.cpp | 28 +++++++++++ components/file_finder/search.hpp | 20 ++++++++ 6 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 components/file_finder/file_finder.hpp create mode 100644 components/file_finder/filename_less.hpp create mode 100644 components/file_finder/search.cpp create mode 100644 components/file_finder/search.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b4d2900b17..ca21824b6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,14 @@ set(TO_UTF8_HEADER ${COMP_DIR}/to_utf8/to_utf8.hpp) source_group(components\\to_utf8 FILES ${TO_UTF8} ${TO_UTF8_HEADER}) +set(FILE_FINDER + ${COMP_DIR}/file_finder/search.cpp) +set(FILE_FINDER_HEADER + ${COMP_DIR}/file_finder/file_finder.hpp + ${COMP_DIR}/file_finder/filename_less.hpp + ${COMP_DIR}/file_finder/search.hpp) +source_group(components\\file_finder FILES ${FILE_FINDER} ${FILE_FINDER_HEADER}) + set(ESM_STORE ${COMP_DIR}/esm_store/store.cpp) set(ESM_STORE_HEADER @@ -82,10 +90,10 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp) source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER}) set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC} ${TO_UTF8} - ${COMPILER} ${INTERPRETER} ${ESM}) + ${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER}) set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER} ${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} ${TO_UTF8_HEADER} - ${INTERPRETER_HEADER}) + ${INTERPRETER_HEADER} ${FILE_FINDER_HEADER}) # source directory: libs diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 2c6ab6339a..262c7a9749 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -1,4 +1,3 @@ - #include "soundmanager.hpp" #include @@ -8,6 +7,7 @@ using namespace std; #include #include +#include #include #include #include @@ -74,12 +74,17 @@ namespace MWSound Mangle::Sound::OgreListenerMover cameraTracker; const ESMS::ESMStore &store; - std::string dir; typedef std::map IDMap; typedef std::map PtrMap; PtrMap sounds; + // This is used for case insensitive and slash-type agnostic file + // finding. It takes DOS paths (any case, \\ slashes or / slashes) + // relative to the sound dir, and translates them into full paths + // of existing files in the filesystem, if they exist. + FileFinder::FileFinder files; + SoundImpl(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &str, const std::string &soundDir) @@ -87,6 +92,7 @@ namespace MWSound , updater(mgr) , cameraTracker(mgr) , store(str) + , files(soundDir) { cout << "Sound output: " << SOUND_OUT << endl; cout << "Sound decoder: " << SOUND_IN << endl; @@ -96,21 +102,20 @@ namespace MWSound // Tell Ogre to update the sound system each frame root->addFrameListener(&updater); + } - dir = soundDir + "/"; + bool hasFile(const std::string &str) + { + return files.has(str); } // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) std::string convertPath(const std::string &str) { - std::string file = dir + str; -#ifndef WIN32 - // Actually / path separators should work in Windows too, they - // just aren't necessary. - std::replace(file.begin(), file.end(), '\\', '/'); -#endif - return file; + if(hasFile(str)) + return files.lookup(str); + return ""; } // Convert a soundId to file name, and modify the volume @@ -277,7 +282,10 @@ namespace MWSound { // The range values are not tested if(!mData) return; - mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 10000, false); + if(mData->hasFile(filename)) + mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 10000, false); + else + cout << "Sound file " << filename << " not found, skipping.\n"; } bool SoundManager::sayDone (MWWorld::Ptr ptr) const diff --git a/components/file_finder/file_finder.hpp b/components/file_finder/file_finder.hpp new file mode 100644 index 0000000000..eda73a85f1 --- /dev/null +++ b/components/file_finder/file_finder.hpp @@ -0,0 +1,62 @@ +#ifndef FILE_FINDER_MAIN_H +#define FILE_FINDER_MAIN_H + +#include "search.hpp" +#include "filename_less.hpp" +#include + +namespace FileFinder +{ + +class FileFinder +{ + std::map table; + + struct Inserter : ReturnPath + { + FileFinder *owner; + int cut; + + void add(const boost::filesystem::path &pth) + { + std::string file = pth.file_string(); + std::string key = file.substr(cut); + owner->table[key] = file; + } + }; + + Inserter inserter; + +public: + FileFinder(const boost::filesystem::path &path, bool recurse=true) + { + inserter.owner = this; + + // Remember the original path length, so we can cut it away from + // the relative paths used as keys + std::string pstring = path.file_string(); + inserter.cut = pstring.size(); + + // If the path does not end in a slash, then boost will add one + // later, which means one more character we have to remove. + char last = pstring[pstring.size()-1]; + if(last != '\\' && last != '/') + inserter.cut++; + + // Fill the map + find(path, inserter, recurse); + } + + bool has(const std::string& file) const + { + return table.find(file) != table.end(); + } + + // Find the full path from a relative path. + const std::string &lookup(const std::string& file) const + { + return table.find(file)->second; + } +}; +} +#endif diff --git a/components/file_finder/filename_less.hpp b/components/file_finder/filename_less.hpp new file mode 100644 index 0000000000..9f251b1c84 --- /dev/null +++ b/components/file_finder/filename_less.hpp @@ -0,0 +1,49 @@ +#ifndef FILE_FINDER_LESS_H +#define FILE_FINDER_LESS_H + +#include +#include + +namespace FileFinder{ + +// Used for maps of file paths. Compares file paths, but ignores case +// AND treats \ and / as the same character. +struct path_less +{ + int compareChar(char a, char b) const + { + if(a>b) return 1; + else if(a= 'a' && a <= 'z') a += 'A'-'a'; + else if(a == '\\') a = '/'; + if(b >= 'a' && b <= 'z') b += 'A'-'a'; + else if(b == '\\') b = '/'; + return compareChar(a,b); + } + + int compareString(const char *a, const char *b) const + { + while(*a && *b) + { + int i = comparePathChar(*a,*b); + if(i != 0) return i; + a++; b++; + } + // At this point, one or both of the chars is a null terminator. + // Normal char comparison will get the correct final result here. + return compareChar(*a,*b); + } + + bool operator() (const std::string& a, const std::string& b) const + { + return compareString(a.c_str(), b.c_str()) < 0; + } +}; + +} +#endif diff --git a/components/file_finder/search.cpp b/components/file_finder/search.cpp new file mode 100644 index 0000000000..b05b30e835 --- /dev/null +++ b/components/file_finder/search.cpp @@ -0,0 +1,28 @@ +#include "search.hpp" + +#include + +using namespace std; +using namespace boost::filesystem; + +void FileFinder::find(const path & dir_path, ReturnPath &ret, bool recurse) +{ + if ( !exists( dir_path ) ) + { + cout << "Path " << dir_path << " not found\n"; + return; + } + + directory_iterator end_itr; // default construction yields past-the-end + for ( directory_iterator itr(dir_path); + itr != end_itr; + ++itr ) + { + if ( is_directory( *itr ) ) + { + if(recurse) find(*itr, ret); + } + else + ret.add(*itr); + } +} diff --git a/components/file_finder/search.hpp b/components/file_finder/search.hpp new file mode 100644 index 0000000000..4e16fb64a0 --- /dev/null +++ b/components/file_finder/search.hpp @@ -0,0 +1,20 @@ +#ifndef FILE_FINDER_SEARCH_H +#define FILE_FINDER_SEARCH_H + +#include +#include + +namespace FileFinder +{ + struct ReturnPath + { + virtual void add(const boost::filesystem::path &pth) = 0; + }; + + /** Search the given path and return all file paths through 'ret'. If + recurse==true, all files in subdirectories are returned as well. + */ + void find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse=true); +} + +#endif From 2ad98500704f7a20d387af0655d9b084d6e5e47f Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 20 Aug 2010 14:20:05 +0200 Subject: [PATCH 2/4] Added .wav -> .mp3 sound file lookup --- apps/openmw/mwsound/soundmanager.cpp | 24 ++++++++++++++++++++++-- components/file_finder/file_finder.hpp | 12 ++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 262c7a9749..bd81ef6e63 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -104,17 +104,37 @@ namespace MWSound root->addFrameListener(&updater); } + std::string toMp3(const std::string &str) + { + std::string wav = str; + int i = str.size()-3; + wav[i++] = 'm'; + wav[i++] = 'p'; + wav[i++] = '3'; + return wav; + } + bool hasFile(const std::string &str) { - return files.has(str); + if(files.has(str)) return true; + // Not found? Try exchanging .wav with .mp3 + return files.has(toMp3(str)); } // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) std::string convertPath(const std::string &str) { - if(hasFile(str)) + // Search and return + if(files.has(str)) return files.lookup(str); + + // Try mp3 if the wav wasn't found + std::string mp3 = toMp3(str); + if(files.has(mp3)) + return files.lookup(mp3); + + // Give up return ""; } diff --git a/components/file_finder/file_finder.hpp b/components/file_finder/file_finder.hpp index eda73a85f1..1071212780 100644 --- a/components/file_finder/file_finder.hpp +++ b/components/file_finder/file_finder.hpp @@ -8,13 +8,14 @@ namespace FileFinder { -class FileFinder +template +class FileFinderT { - std::map table; + std::map table; struct Inserter : ReturnPath { - FileFinder *owner; + FileFinderT *owner; int cut; void add(const boost::filesystem::path &pth) @@ -28,7 +29,7 @@ class FileFinder Inserter inserter; public: - FileFinder(const boost::filesystem::path &path, bool recurse=true) + FileFinderT(const boost::filesystem::path &path, bool recurse=true) { inserter.owner = this; @@ -58,5 +59,8 @@ public: return table.find(file)->second; } }; + +// The default is to use path_less for equality checks +typedef FileFinderT FileFinder; } #endif From 978c73add5a72c02f8e8cd424a3f78c3174a4c5b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 20 Aug 2010 15:24:05 +0200 Subject: [PATCH 3/4] fixed sound removal on cell change --- apps/openmw/mwsound/soundmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index bd81ef6e63..2e160f5c20 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -257,7 +257,7 @@ namespace MWSound { // Make sure to increase the iterator before we erase it. it2 = it++; - if(it->first.getCell() == cell) + if(it2->first.getCell() == cell) clearAll(it2); } } From 27166b4ee4b311d030ff46547f2a9e576060958a Mon Sep 17 00:00:00 2001 From: Armin Preiml Date: Sat, 21 Aug 2010 19:40:08 +0200 Subject: [PATCH 4/4] added bone assignment, skeleton is disabled for meshes Skeleton isn't assigned to the meshes for now, because it crashes on some. --- components/nifogre/ogre_nif_loader.cpp | 87 ++++++++++++++++---------- components/nifogre/ogre_nif_loader.hpp | 4 +- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index b935f49f8f..5053fbd7d5 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -30,9 +30,12 @@ #include "../nif/node.hpp" #include "../nif/data.hpp" #include "../nif/property.hpp" +#include "../nif/controller.hpp" +#include "../nif/extra.hpp" #include #include +#include // For warning messages #include @@ -170,7 +173,8 @@ public: }; // Conversion of blend / test mode from NIF -> OGRE. -/* Not in use yet, so let's comment it out. +// Not in use yet, so let's comment it out. +/* static SceneBlendFactor getBlendFactor(int mode) { switch(mode) @@ -191,9 +195,9 @@ static SceneBlendFactor getBlendFactor(int mode) return SBF_SOURCE_ALPHA; } } -*/ -/* This is also unused + +// This is also unused static CompareFunction getTestMode(int mode) { switch(mode) @@ -218,7 +222,7 @@ void NIFLoader::createMaterial(const String &name, const Vector &specular, const Vector &emissive, float glossiness, float alpha, - float alphaFlags, float alphaTest, + int alphaFlags, float alphaTest, const String &texName) { MaterialPtr material = MaterialManager::getSingleton().create(name, resourceGroup); @@ -234,32 +238,33 @@ void NIFLoader::createMaterial(const String &name, /*TextureUnitState *txt =*/ pass->createTextureUnitState(texName); - /* As of yet UNTESTED code from Chris: - pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); + // As of yet UNTESTED code from Chris: + /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); pass->setDepthCheckEnabled(true); // Add transparency if NiAlphaProperty was present - if(alphaFlags != -1) - { - if((alphaFlags&1)) - { + if (alphaFlags != -1) + { + std::cout << "Alpha flags set!" << endl; + if ((alphaFlags&1)) + { pass->setDepthWriteEnabled(false); pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf), getBlendFactor((alphaFlags>>5)&0xf)); - } + } else - pass->setDepthWriteEnabled(true); + pass->setDepthWriteEnabled(true); - if((alphaFlags>>9)&1) - pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), - alphaTest); + if ((alphaFlags>>9)&1) + pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), + alphaTest); pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1)); - } + } else - pass->setDepthWriteEnabled(true); - */ + pass->setDepthWriteEnabled(true); */ + // Add transparency if NiAlphaProperty was present if (alphaFlags != -1) @@ -322,7 +327,7 @@ void NIFLoader::findRealTexture(String &texName) // Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given // mesh. -void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material) +void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std::list &vertexBoneAssignments) { NiTriShapeData *data = shape->data.getPtr(); SubMesh *sub = mesh->createSubMesh(shape->name.toString()); @@ -410,6 +415,14 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material) // Set material if one was given if (!material.empty()) sub->setMaterialName(material); + + //add vertex bone assignments + + for (std::list::iterator it = vertexBoneAssignments.begin(); + it != vertexBoneAssignments.end(); it++) + { + sub->addBoneAssignment(*it); + } } // Helper math functions. Reinventing linear algebra for the win! @@ -595,12 +608,13 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou float *ptr = (float*)data->vertices.ptr; float *optr = ptr; + std::list vertexBoneAssignments; + //use niskindata for the position of vertices. if (!shape->skin.empty()) { // vector that stores if the position if a vertex is absolute std::vector vertexPosAbsolut(numVerts,false); - float *ptrNormals = (float*)data->normals.ptr; //the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex] @@ -654,16 +668,18 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; } - - //TODO: create vbas, and give them to createOgreSubMesh - vertexPosAbsolut[verIndex] = true; } + + VertexBoneAssignment vba; + vba.boneIndex = bonePtr->getHandle(); + vba.vertexIndex = verIndex; + vba.weight = (it->weights.ptr + i)->weight; + + vertexBoneAssignments.push_back(vba); } boneIndex++; - //it->trafo (pos, rot, scale) of the vertex - //it->weights array containt the vertices linked to the bone and the weight } } else @@ -696,7 +712,7 @@ void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bou bounds.add(optr, numVerts); // Create the submesh - createOgreSubMesh(shape, material); + createOgreSubMesh(shape, material, vertexBoneAssignments); } } @@ -737,9 +753,16 @@ void NIFLoader::handleNode(Nif::Node *node, int flags, // create skeleton or add bones if (node->recType == RC_NiNode) { - if (node->name == "Bip01") //root node, create a skeleton + //FIXME: "Bip01" isn't every time the root bone + if (node->name == "Bip01" || node->name == "Root Bone") //root node, create a skeleton { skel = SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true); + + /*if (node->extra->recType == RC_NiTextKeyExtraData ) + { + //TODO: Get animation names + std::cout << node->name.toString() << " is root bone and has textkeyextradata!\n"; + }*/ } if (!skel.isNull()) //if there is a skeleton @@ -845,17 +868,17 @@ void NIFLoader::loadResource(Resource *resource) // Handle the node handleNode(node, 0, NULL, bounds, 0); - //set skeleton -// if (!skel.isNull()) -// mesh->setSkeletonName(getSkeletonName()); - - // Finally, set the bounding value. + // set the bounding value. if (bounds.isValid()) { mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), bounds.maxX(), bounds.maxY(), bounds.maxZ())); mesh->_setBoundingSphereRadius(bounds.getRadius()); } + + // set skeleton +// if (!skel.isNull()) +// mesh->setSkeletonName(getSkeletonName()); } MeshPtr NIFLoader::load(const std::string &name, diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index aea8d3a8da..578308f0b0 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -90,7 +90,7 @@ class NIFLoader : Ogre::ManualResourceLoader void handleNiTriShape(Nif::NiTriShape *shape, int flags, BoundsFinder &bounds); - void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material); + void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material, std::list &vertexBoneAssignments); void createMaterial(const Ogre::String &name, const Nif::Vector &ambient, @@ -98,7 +98,7 @@ class NIFLoader : Ogre::ManualResourceLoader const Nif::Vector &specular, const Nif::Vector &emissive, float glossiness, float alpha, - float alphaFlags, float alphaTest, + int alphaFlags, float alphaTest, const Ogre::String &texName); void findRealTexture(Ogre::String &texName);