mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 20:26:43 +00:00 
			
		
		
		
	Merge branch 'master' of git://github.com/korslund/openmw
Conflicts: game/main.cpp
This commit is contained in:
		
						commit
						3bb9d06e58
					
				
					 30 changed files with 803 additions and 1224 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -1,3 +1,4 @@ | |||
| screenshot*.png | ||||
| *.o | ||||
| *~ | ||||
| data | ||||
|  |  | |||
|  | @ -10,35 +10,45 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) | |||
| 
 | ||||
| set(BSA bsa/bsa_archive.cpp bsa/bsa_file.cpp) | ||||
| set(BSA_HEADER bsa/bsa_archive.hpp bsa/bsa_file.hpp) | ||||
| source_group(bsa FILES ${BSA} ${BSA_HEADER}) | ||||
| 
 | ||||
| set(NIF nif/nif_file.cpp) | ||||
| set(NIF_HEADER nif/controlled.hpp nif/effect.hpp nif/nif_types.hpp  nif/record.hpp | ||||
|     nif/controller.hpp nif/extra.hpp nif/node.hpp nif/record_ptr.hpp | ||||
|     nif/data.hpp nif/nif_file.hpp nif/property.hpp) | ||||
| source_group(nif FILES ${NIF} ${NIF_HEADER})     | ||||
| 
 | ||||
| set(NIFOGRE nifogre/ogre_nif_loader.cpp) | ||||
| set(NIFOGRE_HEADER nifogre/ogre_nif_loader.hpp) | ||||
| source_group(nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER})     | ||||
| 
 | ||||
| set(TOOLS tools/stringops.cpp tools/fileops.cpp) | ||||
| set(TOOLS_HEADER tools/fileops.hpp tools/slice_array.hpp tools/stringops.hpp) | ||||
| source_group(tools FILES ${TOOLS} ${TOOLS_HEADER})     | ||||
| 
 | ||||
| set(MANGLE_VFS mangle/vfs/servers/ogre_vfs.cpp) | ||||
| source_group(mangle_vfs FILES ${MANGLE_VFS}) | ||||
| 
 | ||||
| set(OGRE ogre/renderer.cpp) | ||||
| set(OGRE_HEADER ogre/renderer.hpp) | ||||
| source_group(ogre FILES ${OGRE} ${OGRE_HEADER}) | ||||
| 
 | ||||
| set(INPUT input/oismanager.cpp) | ||||
| set(INPUT_HEADER input/oismanager.hpp input/listener.hpp input/func_binder.hpp input/dispatch_map.hpp input/dispatcher.hpp) | ||||
| set(INPUT_HEADER input/oismanager.hpp input/listener.hpp input/func_binder.hpp input/dispatch_map.hpp input/dispatcher.hpp input/poller.hpp) | ||||
| source_group(input FILES ${INPUT} ${INPUT_HEADER}) | ||||
| 
 | ||||
| set(GAME game/main.cpp) | ||||
| set(GAME_HEADER game/mwinput/inputmanager.hpp) | ||||
| set(GAME game/main.cpp game/engine.cpp) | ||||
| set(GAME_HEADER game/mwinput/inputmanager.hpp game/engine.hpp) | ||||
| source_group(game FILES ${GAME} ${GAME_HEADER}) | ||||
| 
 | ||||
| set(ESM_STORE esm_store/store.cpp esm_store/cell_store.cpp) | ||||
| set(ESM_STORE_HEADER esm_store/cell_store.hpp esm_store/reclists.hpp esm_store/store.hpp) | ||||
| source_group(esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER}) | ||||
| 
 | ||||
| set(GAMEREND game/mwrender/mwscene.cpp game/mwrender/cell.cpp game/mwrender/interior.cpp) | ||||
| set(GAMEREND_HEADER game/mwrender/cell.hpp game/mwrender/mwscene.hpp | ||||
|     game/mwrender/interior.hpp) | ||||
|     game/mwrender/interior.hpp game/mwrender/playerpos.hpp) | ||||
| source_group(game_renderer FILES ${GAMEREND} ${GAMEREND_HEADER})     | ||||
| 
 | ||||
| set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp | ||||
|     esm/loadskil.hpp | ||||
|  | @ -50,6 +60,7 @@ set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp | |||
|     esm/loadbody.hpp esm/loaddial.hpp esm/loadlevlist.hpp esm/loadrace.hpp esm/loadweap.hpp | ||||
|     esm/loadbook.hpp esm/loaddoor.hpp esm/loadligh.hpp esm/loadregn.hpp esm/records.hpp | ||||
|     esm/loadbsgn.hpp esm/loadench.hpp esm/loadlocks.hpp esm/loadscpt.hpp) | ||||
| source_group(esm_header FILES ${ESM_HEADER})     | ||||
| 
 | ||||
| # Platform specific | ||||
| if (WIN32) | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ struct Potion | |||
|   void load(ESMReader &esm) | ||||
|   { | ||||
|     model = esm.getHNString("MODL"); | ||||
|     icon = esm.getHNString("TEXT"); // not ITEX here for some reason
 | ||||
|     icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
 | ||||
|     script = esm.getHNOString("SCRI"); | ||||
|     name = esm.getHNOString("FNAM"); | ||||
|     esm.getHNT(data, "ALDT", 12); | ||||
|  |  | |||
|  | @ -11,8 +11,9 @@ namespace ESM { | |||
|    loading process, but are rather loaded later on demand when we are | ||||
|    setting up a specific cell. | ||||
|  */ | ||||
| struct CellRef | ||||
| class CellRef | ||||
| { | ||||
| public: | ||||
|   int refnum;           // Reference number
 | ||||
|   std::string refID;    // ID of object being referenced
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -60,7 +60,7 @@ struct LeveledListBase | |||
|     // items. Also, some times we don't want to merge lists, just
 | ||||
|     // overwrite. Figure out a way to give the user this option.
 | ||||
|   | ||||
|     for(int i=0; i<list.size(); i++) | ||||
|     for(size_t i=0; i<list.size(); i++) | ||||
|       { | ||||
|         LevelItem &li = list[i]; | ||||
|         li.id = esm.getHNString(recName); | ||||
|  |  | |||
|  | @ -67,7 +67,7 @@ struct Script | |||
|         // The tmp buffer is a null-byte separated string list, we
 | ||||
|         // just have to pick out one string at a time.
 | ||||
|         char* str = tmp; | ||||
|         for(int i=0; i< varNames.size(); i++) | ||||
|         for(size_t i=0; i< varNames.size(); i++) | ||||
|           { | ||||
|             varNames[i] = std::string(str); | ||||
|             str += varNames[i].size()+1; | ||||
|  |  | |||
|  | @ -66,8 +66,9 @@ namespace ESMS | |||
|   }; | ||||
| 
 | ||||
|   /// A storage struct for one single cell reference.
 | ||||
|   struct CellStore | ||||
|   class CellStore | ||||
|   { | ||||
|   public: | ||||
|     CellStore() : cell (0) {} | ||||
|    | ||||
|     const ESM::Cell *cell; | ||||
|  |  | |||
							
								
								
									
										146
									
								
								game/engine.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								game/engine.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,146 @@ | |||
| #include "engine.hpp" | ||||
| 
 | ||||
| #include <cassert> | ||||
| 
 | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "esm_store/cell_store.hpp" | ||||
| #include "bsa/bsa_archive.hpp" | ||||
| #include "ogre/renderer.hpp" | ||||
| #include "tools/fileops.hpp" | ||||
| 
 | ||||
| #include "mwrender/interior.hpp" | ||||
| #include "mwinput/inputmanager.hpp" | ||||
| #include "mwrender/playerpos.hpp" | ||||
| 
 | ||||
| OMW::Engine::Engine() {} | ||||
| 
 | ||||
| // adjust name and load bsa
 | ||||
| 
 | ||||
| void OMW::Engine::prepareMaster() | ||||
| { | ||||
|     std::string::size_type sep = mMaster.find_last_of ("."); | ||||
|      | ||||
|     if (sep==std::string::npos) | ||||
|     { | ||||
|         mMaster += ".esm"; | ||||
|     } | ||||
| } | ||||
|      | ||||
| // Load all BSA files in data directory.
 | ||||
| 
 | ||||
| void OMW::Engine::loadBSA() | ||||
| { | ||||
|     boost::filesystem::directory_iterator end; | ||||
|      | ||||
|     for (boost::filesystem::directory_iterator iter (mDataDir); iter!=end; ++iter) | ||||
|     { | ||||
|         if (boost::filesystem::extension (iter->path())==".bsa") | ||||
|         { | ||||
|             std::cout << "Adding " << iter->path().string() << std::endl; | ||||
|             addBSA(iter->path().file_string());         | ||||
|         } | ||||
|     } | ||||
| } | ||||
|          | ||||
| // add resources directory
 | ||||
| // \note This function works recursively.
 | ||||
| 
 | ||||
| void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path) | ||||
| { | ||||
|     mOgre.getRoot()->addResourceLocation (path.file_string(), "FileSystem", | ||||
|         Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); | ||||
| }       | ||||
|      | ||||
| // Set data dir
 | ||||
| 
 | ||||
| void OMW::Engine::setDataDir (const boost::filesystem::path& dataDir) | ||||
| { | ||||
|     mDataDir = boost::filesystem::system_complete (dataDir); | ||||
| } | ||||
| 
 | ||||
| // Set start cell name (only interiors for now)
 | ||||
| 
 | ||||
| void OMW::Engine::setCell (const std::string& cellName) | ||||
| { | ||||
|     mCellName = cellName; | ||||
| } | ||||
| 
 | ||||
| // Set master file (esm)
 | ||||
| // - If the given name does not have an extension, ".esm" is added automatically
 | ||||
| // - Currently OpenMW only supports one master at the same time.
 | ||||
| 
 | ||||
| void OMW::Engine::addMaster (const std::string& master) | ||||
| { | ||||
|     assert (mMaster.empty()); | ||||
|     mMaster = master; | ||||
| } | ||||
| 
 | ||||
| // Initialise and enter main loop.
 | ||||
| 
 | ||||
| void OMW::Engine::go() | ||||
| { | ||||
|     assert (!mDataDir.empty()); | ||||
|     assert (!mCellName.empty()); | ||||
|     assert (!mMaster.empty()); | ||||
|      | ||||
|     std::cout << "Hello, fellow traveler!\n"; | ||||
|    | ||||
|     std::cout << "Your data directory for today is: " << mDataDir << "\n"; | ||||
| 
 | ||||
|     std::cout << "Initializing OGRE\n"; | ||||
| 
 | ||||
|     const char* plugCfg = "plugins.cfg"; | ||||
| 
 | ||||
|     mOgre.configure(!isFile("ogre.cfg"), plugCfg, false); | ||||
|      | ||||
|     addResourcesDirectory (mDataDir / "Meshes"); | ||||
|     addResourcesDirectory (mDataDir / "Textures"); | ||||
|      | ||||
|     prepareMaster(); | ||||
|     loadBSA(); | ||||
|      | ||||
|     boost::filesystem::path masterPath (mDataDir); | ||||
|     masterPath /= mMaster; | ||||
|      | ||||
|     std::cout << "Loading ESM " << masterPath.string() << "\n"; | ||||
|     ESM::ESMReader esm; | ||||
|     ESMS::ESMStore store; | ||||
|     ESMS::CellStore cell; | ||||
| 
 | ||||
|     // This parses the ESM file and loads a sample cell
 | ||||
|     esm.open(masterPath.file_string()); | ||||
|     store.load(esm); | ||||
| 
 | ||||
|     cell.loadInt(mCellName, store, esm); | ||||
| 
 | ||||
|     // Create the window
 | ||||
|     mOgre.createWindow("OpenMW"); | ||||
| 
 | ||||
|     std::cout << "\nSetting up cell rendering\n"; | ||||
| 
 | ||||
|     // Sets up camera, scene manager, and viewport.
 | ||||
|     MWRender::MWScene scene(mOgre); | ||||
| 
 | ||||
|     // Used to control the player camera and position
 | ||||
|     MWRender::PlayerPos player(scene.getCamera()); | ||||
| 
 | ||||
|     // This connects the cell data with the rendering scene.
 | ||||
|     MWRender::InteriorCellRender rend(cell, scene); | ||||
| 
 | ||||
|     // Load the cell and insert it into the renderer
 | ||||
|     rend.show(); | ||||
| 
 | ||||
|     std::cout << "Setting up input system\n"; | ||||
| 
 | ||||
|     // Sets up the input system
 | ||||
|     MWInput::MWInputManager input(mOgre, player); | ||||
| 
 | ||||
|     std::cout << "\nStart! Press Q/ESC or close window to exit.\n"; | ||||
| 
 | ||||
|     // Start the main rendering loop
 | ||||
|     mOgre.start(); | ||||
| 
 | ||||
|     std::cout << "\nThat's all for now!\n";         | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										55
									
								
								game/engine.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								game/engine.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| #ifndef ENGINE_H | ||||
| #define ENGINE_H | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include <boost/filesystem.hpp> | ||||
| 
 | ||||
| #include "mwrender/mwscene.hpp" | ||||
| 
 | ||||
| namespace OMW | ||||
| { | ||||
|     /// \brief Main engine class, that brings together all the components of OpenMW
 | ||||
| 
 | ||||
|     class Engine | ||||
|     { | ||||
|             boost::filesystem::path mDataDir; | ||||
|             Render::OgreRenderer mOgre; | ||||
|             std::string mCellName; | ||||
|             std::string mMaster; | ||||
|              | ||||
|             // not implemented
 | ||||
|             Engine (const Engine&); | ||||
|             Engine& operator= (const Engine&); | ||||
|      | ||||
|             /// adjust name and load bsa
 | ||||
|             void prepareMaster(); | ||||
|      | ||||
|             /// add resources directory
 | ||||
|             /// \note This function works recursively.
 | ||||
|             void addResourcesDirectory (const boost::filesystem::path& path); | ||||
|      | ||||
|             /// Load all BSA files in data directory.
 | ||||
|             void loadBSA(); | ||||
|      | ||||
|         public: | ||||
| 
 | ||||
|             Engine(); | ||||
| 
 | ||||
|             /// Set data dir
 | ||||
|             void setDataDir (const boost::filesystem::path& dataDir); | ||||
| 
 | ||||
|             /// Set start cell name (only interiors for now)
 | ||||
|             void setCell (const std::string& cellName); | ||||
|              | ||||
|             /// Set master file (esm)
 | ||||
|             /// - If the given name does not have an extension, ".esm" is added automatically
 | ||||
|             /// - Currently OpenMW only supports one master at the same time.
 | ||||
|             void addMaster (const std::string& master); | ||||
| 
 | ||||
|             /// Initialise and enter main loop.
 | ||||
|             void go(); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										145
									
								
								game/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								game/main.cpp
									
									
									
									
									
								
							|  | @ -3,94 +3,30 @@ | |||
| #include <string> | ||||
| #include <fstream> | ||||
| 
 | ||||
| #include "boost/program_options.hpp" | ||||
| #include <boost/program_options.hpp> | ||||
| 
 | ||||
| #include "esm_store/cell_store.hpp" | ||||
| #include "bsa/bsa_archive.hpp" | ||||
| #include "ogre/renderer.hpp" | ||||
| #include "tools/fileops.hpp" | ||||
| 
 | ||||
| #include "mwrender/interior.hpp" | ||||
| #include "mwrender/mwscene.hpp" | ||||
| #include "mwinput/inputmanager.hpp" | ||||
| #include "engine.hpp" | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| void maintest (std::string dataDir, const std::string& cellName) | ||||
| /// Parse command line options and openmw.cfg file (if one exists). Results are directly
 | ||||
| /// written to \a engine.
 | ||||
| /// \return Run OpenMW?
 | ||||
| 
 | ||||
| bool parseOptions (int argc, char**argv, OMW::Engine& engine) | ||||
| { | ||||
|   assert (!dataDir.empty()); | ||||
|    | ||||
|   if (dataDir[dataDir.size()-1]!='/' && dataDir[dataDir.size()-1]!='\\') | ||||
|     dataDir += "/"; | ||||
| 
 | ||||
|   const char* esmFile = "Morrowind.esm"; | ||||
|   const char* bsaFile = "Morrowind.bsa"; | ||||
| 
 | ||||
|   const char* plugCfg = "plugins.cfg"; | ||||
| 
 | ||||
|   cout << "Hello, fellow traveler!\n"; | ||||
|    | ||||
|   cout << "Your data directory for today is: " << dataDir << "\n"; | ||||
| 
 | ||||
|   cout << "Initializing OGRE\n"; | ||||
|   Render::OgreRenderer ogre; | ||||
|   ogre.configure(!isFile("ogre.cfg"), plugCfg, false); | ||||
| 
 | ||||
|   cout << "Adding " << bsaFile << endl; | ||||
|   addBSA(dataDir + bsaFile); | ||||
| 
 | ||||
|   cout << "Loading ESM " << esmFile << "\n"; | ||||
|   ESM::ESMReader esm; | ||||
|   ESMS::ESMStore store; | ||||
|   ESMS::CellStore cell; | ||||
| 
 | ||||
|   // This parses the ESM file and loads a sample cell
 | ||||
|   esm.open(dataDir + esmFile); | ||||
|   store.load(esm); | ||||
| 
 | ||||
|   cell.loadInt(cellName, store, esm); | ||||
| 
 | ||||
|   // Create the window
 | ||||
|   ogre.createWindow("OpenMW"); | ||||
| 
 | ||||
|   cout << "\nSetting up cell rendering\n"; | ||||
| 
 | ||||
|   // Sets up camera, scene manager etc
 | ||||
|   MWRender::MWScene scene(ogre); | ||||
| 
 | ||||
|   // This connects the cell data with the rendering scene.
 | ||||
|   MWRender::InteriorCellRender rend(cell, scene); | ||||
| 
 | ||||
|   // Load the cell and insert it into the renderer
 | ||||
|   rend.show(); | ||||
| 
 | ||||
|   cout << "Setting up input system\n"; | ||||
| 
 | ||||
|   // Sets up the input system
 | ||||
|   MWInput::MWInputManager input(ogre); | ||||
| 
 | ||||
|   cout << "\nStart! Press Q/ESC or close window to exit.\n"; | ||||
| 
 | ||||
|   // Start the main rendering loop
 | ||||
|   ogre.start(); | ||||
| 
 | ||||
|   cout << "\nThat's all for now!\n"; | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char**argv) | ||||
| { | ||||
|   try | ||||
|   { | ||||
|     boost::program_options::options_description desc ( | ||||
|       "Syntax: openmw <options>\nAllowed options"); | ||||
|         "Syntax: openmw <options>\nAllowed options"); | ||||
| 
 | ||||
|     desc.add_options() | ||||
|       ("help", "print help message") | ||||
|       ("data", boost::program_options::value<std::string>()->default_value ("data"), | ||||
|         "set data directory") | ||||
|       ("start", boost::program_options::value<std::string>()->default_value ("Beshara"), | ||||
|         "set initial cell (only interior cells supported at the moment") | ||||
|       ; | ||||
|         ("help", "print help message") | ||||
|         ("data", boost::program_options::value<std::string>()->default_value ("data"), | ||||
|             "set data directory") | ||||
|         ("start", boost::program_options::value<std::string>()->default_value ("Beshara"), | ||||
|             "set initial cell (only interior cells supported at the moment") | ||||
|         ("master", boost::program_options::value<std::string>()->default_value ("Morrowind"), | ||||
|             "master file") | ||||
|         ; | ||||
|    | ||||
|     boost::program_options::variables_map variables; | ||||
|      | ||||
|  | @ -101,24 +37,45 @@ int main(int argc, char**argv) | |||
| 
 | ||||
|     boost::program_options::store(valid_opts, variables); | ||||
|     boost::program_options::notify(variables); | ||||
| 	/*
 | ||||
|     boost::program_options::store ( | ||||
|         boost::program_options::parse_command_line (argc, argv, desc), variables); | ||||
|     boost::program_options::notify (variables); | ||||
| 	*/ | ||||
| 
 | ||||
|     if (configFile.is_open()) | ||||
|       boost::program_options::store ( | ||||
|         boost::program_options::parse_config_file (configFile, desc), variables); | ||||
|         boost::program_options::store ( | ||||
|             boost::program_options::parse_config_file (configFile, desc), variables); | ||||
| 
 | ||||
|     if (variables.count ("help")) | ||||
|     { | ||||
|       std::cout << desc << std::endl; | ||||
|         std::cout << desc << std::endl; | ||||
|         return false; | ||||
|     } | ||||
|     else | ||||
|     {           | ||||
|       maintest (variables["data"].as<std::string>(), variables["start"].as<std::string>()); | ||||
|     }   | ||||
|   } | ||||
|   catch(exception &e) | ||||
|     { | ||||
|       cout << "\nERROR: " << e.what() << endl; | ||||
|       return 1; | ||||
|     } | ||||
|   return 0; | ||||
| 
 | ||||
|     engine.setDataDir (variables["data"].as<std::string>()); | ||||
|     engine.setCell (variables["start"].as<std::string>()); | ||||
|     engine.addMaster (variables["master"].as<std::string>()); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char**argv) | ||||
| { | ||||
|     try | ||||
|     { | ||||
|         OMW::Engine engine; | ||||
|      | ||||
|         if (parseOptions (argc, argv, engine)) | ||||
|         { | ||||
|             engine.go(); | ||||
|         } | ||||
|     } | ||||
|     catch(exception &e) | ||||
|     { | ||||
|         cout << "\nERROR: " << e.what() << endl; | ||||
|         return 1; | ||||
|     } | ||||
|    | ||||
|     return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -1,9 +1,12 @@ | |||
| #ifndef _INPUT_MWINPUTMANAGER_H | ||||
| #define _INPUT_MWINPUTMANAGER_H | ||||
| #ifndef _MWINPUT_MWINPUTMANAGER_H | ||||
| #define _MWINPUT_MWINPUTMANAGER_H | ||||
| 
 | ||||
| #include "input/listener.hpp" | ||||
| #include "input/dispatcher.hpp" | ||||
| #include "input/poller.hpp" | ||||
| #include "boost/bind.hpp" | ||||
| #include "game/mwrender/playerpos.hpp" | ||||
| #include "platform/strings.h" | ||||
| 
 | ||||
| namespace MWInput | ||||
| { | ||||
|  | @ -11,33 +14,118 @@ namespace MWInput | |||
|     { | ||||
|       A_Quit,           // Exit the program
 | ||||
| 
 | ||||
|       A_Screenshot,     // Take a screenshot
 | ||||
| 
 | ||||
|       A_MoveLeft,       // Move player left / right
 | ||||
|       A_MoveRight, | ||||
|       A_MoveUp,         // Move up / down
 | ||||
|       A_MoveDown, | ||||
|       A_MoveForward,    // Forward / Backward
 | ||||
|       A_MoveBackward, | ||||
| 
 | ||||
|       A_LAST            // Marker for the last item
 | ||||
|     }; | ||||
| 
 | ||||
|   // Class that handles all input and key bindings for OpenMW
 | ||||
|   class MWInputManager | ||||
|   class MWInputManager : public Ogre::FrameListener | ||||
|   { | ||||
|     // Note: the order here is important. The OISManager must be
 | ||||
|     // initialized before poller and listener.
 | ||||
|     Input::Dispatcher disp; | ||||
|     Render::OgreRenderer &ogre; | ||||
|     Input::OISManager input; | ||||
|     Input::Poller poller; | ||||
|     Input::InputListener listener; | ||||
|     MWRender::PlayerPos &player; | ||||
| 
 | ||||
|     // Count screenshots. TODO: We should move this functionality to
 | ||||
|     // OgreRender or somewhere else.
 | ||||
|     int shotCount; | ||||
| 
 | ||||
|     // Write screenshot to file.
 | ||||
|     void screenshot() | ||||
|     { | ||||
|       // Find the first unused filename.
 | ||||
|       //
 | ||||
|       char buf[50];       | ||||
|       do | ||||
|       { | ||||
|         snprintf(buf, 50, "screenshot%03d.png", shotCount++); | ||||
|       } while (boost::filesystem::exists(buf)); | ||||
| 
 | ||||
|       ogre.screenshot(buf); | ||||
|     } | ||||
| 
 | ||||
|   public: | ||||
|     MWInputManager(Render::OgreRenderer &ogre) | ||||
|     MWInputManager(Render::OgreRenderer &_ogre, | ||||
|                    MWRender::PlayerPos &_player) | ||||
|       : disp(A_LAST), | ||||
|         input(ogre), | ||||
|         listener(ogre, input, disp) | ||||
|         ogre(_ogre), | ||||
|         input(_ogre), | ||||
|         poller(input), | ||||
|         listener(_ogre, input, disp), | ||||
|         player(_player), | ||||
|         shotCount(0) | ||||
|     { | ||||
|       using namespace Input; | ||||
|       using namespace OIS; | ||||
| 
 | ||||
|       // Bind MW-specific functions
 | ||||
|       disp.funcs.bind(A_Quit, | ||||
|                       boost::bind(&InputListener::exitNow, &listener), | ||||
|       disp.funcs.bind(A_Quit, boost::bind(&InputListener::exitNow, &listener), | ||||
|                       "Quit program"); | ||||
|       disp.funcs.bind(A_Screenshot, boost::bind(&MWInputManager::screenshot, this), | ||||
|                       "Screenshot"); | ||||
| 
 | ||||
|       // Add ourselves as a frame listener, to catch movement keys
 | ||||
|       ogre.getRoot()->addFrameListener(this); | ||||
| 
 | ||||
|       // Tell the input listener about the camera
 | ||||
|       listener.setCamera(player.getCamera()); | ||||
| 
 | ||||
|       // Key bindings
 | ||||
|       disp.bind(KC_Q, A_Quit); | ||||
|       disp.bind(KC_ESCAPE, A_Quit); | ||||
|       disp.bind(KC_SYSRQ, A_Screenshot); | ||||
| 
 | ||||
|       // Key bindings for polled keys
 | ||||
| 
 | ||||
|       // Arrow keys
 | ||||
|       poller.bind(A_MoveLeft, KC_LEFT); | ||||
|       poller.bind(A_MoveRight, KC_RIGHT); | ||||
|       poller.bind(A_MoveForward, KC_UP); | ||||
|       poller.bind(A_MoveBackward, KC_DOWN); | ||||
| 
 | ||||
|       // WASD keys
 | ||||
|       poller.bind(A_MoveLeft, KC_A); | ||||
|       poller.bind(A_MoveRight, KC_D); | ||||
|       poller.bind(A_MoveForward, KC_W); | ||||
|       poller.bind(A_MoveBackward, KC_S); | ||||
| 
 | ||||
|       // Use shift and ctrl for up and down
 | ||||
|       poller.bind(A_MoveUp, KC_LSHIFT); | ||||
|       poller.bind(A_MoveDown, KC_LCONTROL); | ||||
|     } | ||||
| 
 | ||||
|     // Used to check for movement keys
 | ||||
|     bool frameStarted(const Ogre::FrameEvent &evt) | ||||
|     { | ||||
|       float speed = 300 * evt.timeSinceLastFrame; | ||||
|       float moveX = 0, moveY = 0, moveZ = 0; | ||||
| 
 | ||||
|       if(poller.isDown(A_MoveLeft)) moveX -= speed; | ||||
|       if(poller.isDown(A_MoveRight)) moveX += speed; | ||||
|       if(poller.isDown(A_MoveForward)) moveZ -= speed; | ||||
|       if(poller.isDown(A_MoveBackward)) moveZ += speed; | ||||
| 
 | ||||
|       // TODO: These should be enabled for floating modes (like
 | ||||
|       // swimming and levitation) and disabled for everything else.
 | ||||
|       if(poller.isDown(A_MoveUp)) moveY += speed; | ||||
|       if(poller.isDown(A_MoveDown)) moveY -= speed; | ||||
| 
 | ||||
|       if(moveX != 0 || moveY != 0 || moveZ != 0) | ||||
|         player.moveRel(moveX, moveY, moveZ); | ||||
| 
 | ||||
|       return true; | ||||
|     } | ||||
|   }; | ||||
| } | ||||
|  |  | |||
|  | @ -29,13 +29,14 @@ void insertObj(CellRender& cellRender, const ESMS::LiveCellRef<ESM::Light>& live | |||
|     cellRender.insertBegin (liveRef.ref); | ||||
|      | ||||
|     cellRender.insertMesh ("meshes\\" + model); | ||||
|      | ||||
|     int color = liveRef.base->data.color; | ||||
|      | ||||
|     cellRender.insertLight(color & 255, | ||||
|                            (color >> 8) & 255, | ||||
|                            (color >> 16) & 255, | ||||
|                            liveRef.base->data.radius); | ||||
|          | ||||
|     // Extract the color and convert to floating point
 | ||||
|     const int color = liveRef.base->data.color; | ||||
|     const float r = ((color >>  0) & 0xFF) / 255.0f; | ||||
|     const float g = ((color >>  8) & 0xFF) / 255.0f; | ||||
|     const float b = ((color >> 16) & 0xFF) / 255.0f; | ||||
|     const float radius = float(liveRef.base->data.radius); | ||||
|     cellRender.insertLight(r, g, b, radius); | ||||
|      | ||||
|     cellRender.insertEnd(); | ||||
|   }   | ||||
|  |  | |||
|  | @ -156,7 +156,7 @@ void InteriorCellRender::setAmbientMode() | |||
|      | ||||
|     case 1: | ||||
| 
 | ||||
|       scene.getMgr()->setAmbientLight(0.7*ambientColor + 0.3*ColourValue(1,1,1)); | ||||
|       scene.getMgr()->setAmbientLight(0.7f*ambientColor + 0.3f*ColourValue(1,1,1)); | ||||
|       break; | ||||
|            | ||||
|     case 2: | ||||
|  |  | |||
							
								
								
									
										58
									
								
								game/mwrender/playerpos.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								game/mwrender/playerpos.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| #ifndef _MWRENDER_PLAYERPOS_H | ||||
| #define _MWRENDER_PLAYERPOS_H | ||||
| 
 | ||||
| #include "OgreCamera.h" | ||||
| 
 | ||||
| namespace MWRender | ||||
| { | ||||
|   // This class keeps track of the player position. It takes care of
 | ||||
|   // camera movement, sound listener updates, and collision handling
 | ||||
|   // (to be done).
 | ||||
|   class PlayerPos | ||||
|   { | ||||
|     float x, y, z; | ||||
|     Ogre::Camera *camera; | ||||
| 
 | ||||
|   public: | ||||
|     PlayerPos(Ogre::Camera *cam) : | ||||
|       camera(cam), x(0), y(0), z(0) {} | ||||
| 
 | ||||
|     // Set the player position. Uses Morrowind coordinates.
 | ||||
|     void setPos(float _x, float _y, float _z) | ||||
|     { | ||||
|       x = _x; | ||||
|       y = _y; | ||||
|       z = _z; | ||||
| 
 | ||||
|       // TODO: Update sound listener
 | ||||
|     } | ||||
| 
 | ||||
|     Ogre::Camera *getCamera() { return camera; } | ||||
| 
 | ||||
|     // Move the player relative to her own position and
 | ||||
|     // orientation. After the call, the new position is returned.
 | ||||
|     void moveRel(float &relX, float &relY, float &relZ) | ||||
|     { | ||||
|       using namespace Ogre; | ||||
| 
 | ||||
|       // Move camera relative to its own direction
 | ||||
|       camera->moveRelative(Vector3(relX,0,relZ)); | ||||
| 
 | ||||
|       // Up/down movement is always done relative the world axis.
 | ||||
|       camera->move(Vector3(0,relY,0)); | ||||
| 
 | ||||
|       // Get new camera position, converting back to MW coords.
 | ||||
|       Vector3 pos = camera->getPosition(); | ||||
|       relX = pos[0]; | ||||
|       relY = -pos[2]; | ||||
|       relZ = pos[1]; | ||||
| 
 | ||||
|       // TODO: Collision detection must be used to find the REAL new
 | ||||
|       // position.
 | ||||
| 
 | ||||
|       // Set the position
 | ||||
|       setPos(relX, relY, relZ); | ||||
|     } | ||||
|   }; | ||||
| } | ||||
| #endif | ||||
|  | @ -54,7 +54,8 @@ public: | |||
|    */ | ||||
|   void bind(int index, Action action, const std::string &name="") | ||||
|   { | ||||
|     assert(index >= 0 && index < bindings.size()); | ||||
|     assert(index >= 0 && index < (int)bindings.size()); | ||||
| 
 | ||||
|     FuncBinding &fb = bindings[index]; | ||||
|     fb.action = action; | ||||
|     fb.name = name; | ||||
|  | @ -65,7 +66,8 @@ public: | |||
|    */ | ||||
|   void unbind(int index) | ||||
|   { | ||||
|     assert(index >= 0 && index < bindings.size()); | ||||
|     assert(index >= 0 && index < (int)bindings.size()); | ||||
| 
 | ||||
|     bindings[index] = FuncBinding(); | ||||
|   } | ||||
| 
 | ||||
|  | @ -75,7 +77,8 @@ public: | |||
|    */ | ||||
|   void call(int index, const void *p=NULL) const | ||||
|   { | ||||
|     assert(index >= 0 && index < bindings.size()); | ||||
|     assert(index >= 0 && index < (int)bindings.size()); | ||||
| 
 | ||||
|     const FuncBinding &fb = bindings[index]; | ||||
|     if(fb.action) fb.action(index, p); | ||||
|   } | ||||
|  | @ -83,14 +86,16 @@ public: | |||
|   /// Check if a given index is bound to anything
 | ||||
|   bool isBound(int index) const | ||||
|   { | ||||
|     assert(index >= 0 && index < bindings.size()); | ||||
|     assert(index >= 0 && index < (int)bindings.size()); | ||||
| 
 | ||||
|     return !bindings[index].action.empty(); | ||||
|   } | ||||
| 
 | ||||
|   /// Return the name associated with an action (empty if not bound)
 | ||||
|   const std::string &getName(int index) const | ||||
|   { | ||||
|     assert(index >= 0 && index < bindings.size()); | ||||
|     assert(index >= 0 && index < (int)bindings.size()); | ||||
| 
 | ||||
|     return bindings[index].name; | ||||
|   } | ||||
| }; | ||||
|  |  | |||
|  | @ -24,12 +24,17 @@ namespace Input | |||
|       mMouse = input.mouse; | ||||
|       mKeyboard = input.keyboard; | ||||
| 
 | ||||
|       assert(mKeyboard); | ||||
|       assert(mWindow); | ||||
| 
 | ||||
|       // Add ourself to the managers
 | ||||
|       rend.getRoot() -> addFrameListener(this); | ||||
|       mKeyboard      -> setEventCallback(this); | ||||
|       mMouse         -> setEventCallback(this); | ||||
|     } | ||||
| 
 | ||||
|     void setCamera(Ogre::Camera *cam) { camera = cam; } | ||||
| 
 | ||||
|     // Call this to exit the main loop
 | ||||
|     void exitNow() { doExit = true; } | ||||
| 
 | ||||
|  | @ -59,6 +64,29 @@ namespace Input | |||
| 
 | ||||
|     bool mouseMoved( const OIS::MouseEvent &arg ) | ||||
|     { | ||||
|       using namespace Ogre; | ||||
|       assert(camera); | ||||
| 
 | ||||
|       // Mouse sensitivity. Should be a config option later.
 | ||||
|       const float MS = 0.2f; | ||||
| 
 | ||||
|       float x = arg.state.X.rel * MS; | ||||
|       float y = arg.state.Y.rel * MS; | ||||
| 
 | ||||
|       camera->yaw(Degree(-x)); | ||||
| 
 | ||||
|       // The camera before pitching
 | ||||
|       Quaternion nopitch = camera->getOrientation(); | ||||
| 
 | ||||
|       camera->pitch(Degree(-y)); | ||||
| 
 | ||||
|       // Apply some failsafe measures against the camera flipping
 | ||||
|       // upside down. Is the camera close to pointing straight up or
 | ||||
|       // down?
 | ||||
|       if(camera->getUp()[1] <= 0.1) | ||||
|         // If so, undo the last pitch
 | ||||
|         camera->setOrientation(nopitch); | ||||
| 
 | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -76,6 +104,7 @@ namespace Input | |||
| 
 | ||||
|     const Dispatcher &disp; | ||||
|     Ogre::RenderWindow *mWindow; | ||||
|     Ogre::Camera *camera; | ||||
|     OIS::Mouse *mMouse; | ||||
|     OIS::Keyboard *mKeyboard; | ||||
|     bool doExit; | ||||
|  |  | |||
|  | @ -30,8 +30,9 @@ OISManager::OISManager(Render::OgreRenderer &rend) | |||
|   windowHndStr << windowHnd; | ||||
|   pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); | ||||
| 
 | ||||
|   // Non-exclusive mouse and keyboard input in debug mode
 | ||||
|   if(true) | ||||
|   // Non-exclusive mouse and keyboard input in debug mode. Debug mode
 | ||||
|   // isn't implemented yet though.
 | ||||
|   if(false) | ||||
|     { | ||||
| #if defined OIS_WIN32_PLATFORM | ||||
|       pl.insert(std::make_pair(std::string("w32_mouse"), | ||||
|  |  | |||
							
								
								
									
										51
									
								
								input/poller.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								input/poller.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| #ifndef _INPUT_POLLER_H | ||||
| #define _INPUT_POLLER_H | ||||
| 
 | ||||
| #include "dispatch_map.hpp" | ||||
| #include "oismanager.hpp" | ||||
| 
 | ||||
| namespace Input { | ||||
| 
 | ||||
| /** The poller is used to check (poll) for keys rather than waiting
 | ||||
|     for events. TODO: Might make this OIS-independent later. */ | ||||
| struct Poller | ||||
| { | ||||
|   DispatchMap map; | ||||
|   OIS::Keyboard *keyboard; | ||||
| 
 | ||||
|   Poller(Input::OISManager &ois) | ||||
|   { | ||||
|     keyboard = ois.keyboard; | ||||
|     assert(keyboard); | ||||
|   } | ||||
| 
 | ||||
|   /** Bind or unbind a given action with a key. The action is the first
 | ||||
|       parameter, the key is the second. | ||||
|   */ | ||||
|   void bind(int in, int out) { map.bind(in, out); } | ||||
|   void unbind(int in, int out) { map.unbind(in, out); } | ||||
|   bool isBound(int in) const { return map.isBound(in); } | ||||
| 
 | ||||
|   /// Check whether a given action button is currently pressed.
 | ||||
|   typedef DispatchMap::OutList _O; | ||||
|   bool isDown(int index) const | ||||
|   { | ||||
|     assert(keyboard); | ||||
| 
 | ||||
|     // No bindings, no action
 | ||||
|     if(!isBound(index)) | ||||
|       return false; | ||||
| 
 | ||||
|     // Get all the keys bound to this action, and check them.
 | ||||
|     const _O &list = map.getList(index); | ||||
|     _O::const_iterator it; | ||||
|     for(it = list.begin(); it != list.end(); it++) | ||||
|       // If there's any match, we're good to go.
 | ||||
|       if(keyboard->isKeyDown((OIS::KeyCode)*it)) return true; | ||||
| 
 | ||||
|     return false; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| #endif | ||||
|  | @ -30,10 +30,14 @@ | |||
| #include "nif/node.hpp" | ||||
| #include "nif/data.hpp" | ||||
| #include "nif/property.hpp" | ||||
| #include "platform/strings.h" | ||||
| 
 | ||||
| // For warning messages
 | ||||
| #include <iostream> | ||||
| 
 | ||||
| // float infinity
 | ||||
| #include <limits> | ||||
| 
 | ||||
| typedef unsigned char ubyte; | ||||
| 
 | ||||
| using namespace std; | ||||
|  | @ -58,6 +62,78 @@ static void warn(const string &msg) | |||
|   cout << "WARNING (NIF:" << errName << "): " << msg << endl; | ||||
| } | ||||
| 
 | ||||
| // Helper class that computes the bounding box and of a mesh
 | ||||
| class BoundsFinder | ||||
| { | ||||
|   struct MaxMinFinder | ||||
|   { | ||||
|     float max, min; | ||||
| 
 | ||||
|     MaxMinFinder() | ||||
|     { | ||||
|       min = numeric_limits<float>::infinity(); | ||||
|       max = -min; | ||||
|     } | ||||
| 
 | ||||
|     void add(float f) | ||||
|     { | ||||
|       if(f > max) max = f; | ||||
|       if(f < min) min = f; | ||||
|     } | ||||
| 
 | ||||
|     // Return Max(max**2, min**2)
 | ||||
|     float getMaxSquared() | ||||
|     { | ||||
|       float m1 = max*max; | ||||
|       float m2 = min*min; | ||||
|       if(m1 >= m2) return m1; | ||||
|       return m2; | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   MaxMinFinder X, Y, Z; | ||||
| 
 | ||||
| public: | ||||
|   // Add 'verts' vertices to the calculation. The 'data' pointer is
 | ||||
|   // expected to point to 3*verts floats representing x,y,z for each
 | ||||
|   // point.
 | ||||
|   void add(float *data, int verts) | ||||
|   { | ||||
|     for(int i=0;i<verts;i++) | ||||
|       { | ||||
|         X.add(*(data++)); | ||||
|         Y.add(*(data++)); | ||||
|         Z.add(*(data++)); | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   // True if this structure has valid values
 | ||||
|   bool isValid() | ||||
|   { | ||||
|     return | ||||
|       minX() <= maxX() && | ||||
|       minY() <= maxY() && | ||||
|       minZ() <= maxZ(); | ||||
|   } | ||||
| 
 | ||||
|   // Compute radius
 | ||||
|   float getRadius() | ||||
|   { | ||||
|     assert(isValid()); | ||||
| 
 | ||||
|     // The radius is computed from the origin, not from the geometric
 | ||||
|     // center of the mesh.
 | ||||
|     return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared()); | ||||
|   } | ||||
| 
 | ||||
|   float minX() { return X.min; } | ||||
|   float maxX() { return X.max; } | ||||
|   float minY() { return Y.min; } | ||||
|   float maxY() { return Y.max; } | ||||
|   float minZ() { return Z.min; } | ||||
|   float maxZ() { return Z.max; } | ||||
| }; | ||||
| 
 | ||||
| // Conversion of blend / test mode from NIF -> OGRE. Not in use yet.
 | ||||
| static SceneBlendFactor getBlendFactor(int mode) | ||||
| { | ||||
|  | @ -177,10 +253,6 @@ static void createMaterial(const String &name, | |||
| // make sure that all materials are given unique names.
 | ||||
| static String getUniqueName(const String &input) | ||||
| { | ||||
| #ifdef WIN32 | ||||
| #define snprintf _snprintf | ||||
| #endif | ||||
| 
 | ||||
|   static int addon = 0; | ||||
|   static char buf[8]; | ||||
|   snprintf(buf, 8, "_%d", addon++); | ||||
|  | @ -296,6 +368,22 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material | |||
| 
 | ||||
|   // Set material if one was given
 | ||||
|   if(!material.empty()) sub->setMaterialName(material); | ||||
| 
 | ||||
|   /* Old commented D code. Might be useful when reimplementing
 | ||||
|      animation. | ||||
|   // Assign this submesh to the given bone
 | ||||
|   VertexBoneAssignment v; | ||||
|   v.boneIndex = ((Bone*)bone)->getHandle(); | ||||
|   v.weight = 1.0; | ||||
| 
 | ||||
|   std::cerr << "+ Assigning bone index " << v.boneIndex << "\n"; | ||||
| 
 | ||||
|   for(int i=0; i < numVerts; i++) | ||||
|     { | ||||
|       v.vertexIndex = i; | ||||
|       sub->addBoneAssignment(v); | ||||
|     } | ||||
|   */ | ||||
| } | ||||
| 
 | ||||
| // Helper math functions. Reinventing linear algebra for the win!
 | ||||
|  | @ -341,8 +429,10 @@ static void vectorMul(const Matrix &A, float *C) | |||
|     C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2]; | ||||
| } | ||||
| 
 | ||||
| static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags) | ||||
| static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds) | ||||
| { | ||||
|   assert(shape != NULL); | ||||
| 
 | ||||
|   // Interpret flags
 | ||||
|   bool hidden    = (flags & 0x01) != 0; // Not displayed
 | ||||
|   bool collide   = (flags & 0x02) != 0; // Use mesh for collision
 | ||||
|  | @ -462,47 +552,53 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags) | |||
|                              alphaFlags, alphaTest, texName); | ||||
|             } | ||||
|         } | ||||
|     } // End of material block, if(!hidden) ...
 | ||||
| 
 | ||||
|   /* Do in-place transformation of all the vertices and normals. This
 | ||||
|      is pretty messy stuff, but we need it to make the sub-meshes | ||||
|      appear in the correct place. Neither Ogre nor Bullet support | ||||
|      nested levels of sub-meshes with transformations applied to each | ||||
|      level. | ||||
|   */ | ||||
|   NiTriShapeData *data = shape->data.getPtr(); | ||||
|   int numVerts = data->vertices.length / 3; | ||||
| 
 | ||||
|   float *ptr = (float*)data->vertices.ptr; | ||||
|   float *optr = ptr; | ||||
| 
 | ||||
|   // Rotate, scale and translate all the vertices
 | ||||
|   const Matrix &rot = shape->trafo->rotation; | ||||
|   const Vector &pos = shape->trafo->pos; | ||||
|   float scale = shape->trafo->scale; | ||||
|   for(int i=0; i<numVerts; i++) | ||||
|     { | ||||
|       vectorMulAdd(rot, pos, ptr, scale); | ||||
|       ptr += 3; | ||||
|     } | ||||
| 
 | ||||
|   { | ||||
|     /* Do in-place transformation of all the vertices and normals. This
 | ||||
|        is pretty messy stuff, but we need it to make the sub-meshes | ||||
|        appear in the correct place. Neither Ogre nor Bullet support | ||||
|        nested levels of sub-meshes with transformations applied to each | ||||
|        level. | ||||
|     */ | ||||
|     NiTriShapeData *data = shape->data.getPtr(); | ||||
|     int numVerts = data->vertices.length / 3; | ||||
| 
 | ||||
|     float *ptr = (float*)data->vertices.ptr; | ||||
| 
 | ||||
|     // Rotate, scale and translate all the vertices
 | ||||
|     const Matrix &rot = shape->trafo->rotation; | ||||
|     const Vector &pos = shape->trafo->pos; | ||||
|     float scale = shape->trafo->scale; | ||||
|     for(int i=0; i<numVerts; i++) | ||||
|       { | ||||
|         vectorMulAdd(rot, pos, ptr, scale); | ||||
|         ptr += 3; | ||||
|       } | ||||
| 
 | ||||
|     // Remember to rotate all the vertex normals as well
 | ||||
|     if(data->normals.length) | ||||
|       { | ||||
|         ptr = (float*)data->normals.ptr; | ||||
|         for(int i=0; i<numVerts; i++) | ||||
|           { | ||||
|             vectorMul(rot, ptr); | ||||
|             ptr += 3; | ||||
|           } | ||||
|       } | ||||
|   } | ||||
|   // Remember to rotate all the vertex normals as well
 | ||||
|   if(data->normals.length) | ||||
|     { | ||||
|       ptr = (float*)data->normals.ptr; | ||||
|       for(int i=0; i<numVerts; i++) | ||||
|         { | ||||
|           vectorMul(rot, ptr); | ||||
|           ptr += 3; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   if(!hidden) | ||||
|     createOgreMesh(mesh, shape, material); | ||||
|     { | ||||
|       // Add this vertex set to the bounding box
 | ||||
|       bounds.add(optr, numVerts); | ||||
| 
 | ||||
|       // Create the submesh
 | ||||
|       createOgreMesh(mesh, shape, material); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformation *trafo = NULL) | ||||
| static void handleNode(Mesh* mesh, Nif::Node *node, int flags, | ||||
|                        const Transformation *trafo, BoundsFinder &bounds) | ||||
| { | ||||
|   // Accumulate the flags from all the child nodes. This works for all
 | ||||
|   // the flags we currently use, at least.
 | ||||
|  | @ -538,7 +634,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat | |||
|   if(trafo) | ||||
|     { | ||||
|       // Get a non-const reference to the node's data, since we're
 | ||||
|       // overwriting it.
 | ||||
|       // overwriting it. TODO: Is this necessary?
 | ||||
|       Transformation &final = *((Transformation*)node->trafo); | ||||
| 
 | ||||
|       // For both position and rotation we have that:
 | ||||
|  | @ -550,7 +646,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat | |||
|       matrixMul(trafo->rotation, final.rotation); | ||||
| 
 | ||||
|       // Scalar values are so nice to deal with. Why can't everything
 | ||||
|       // just be scalars?
 | ||||
|       // just be scalar?
 | ||||
|       final.scale *= trafo->scale; | ||||
|     } | ||||
| 
 | ||||
|  | @ -562,12 +658,12 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat | |||
|       for(int i=0; i<n; i++) | ||||
|         { | ||||
|           if(list.has(i)) | ||||
|             handleNode(mesh, &list[i], flags, node->trafo); | ||||
|             handleNode(mesh, &list[i], flags, node->trafo, bounds); | ||||
|         } | ||||
|     } | ||||
|   else if(node->recType == RC_NiTriShape) | ||||
|     // For shapes
 | ||||
|     handleNiTriShape(mesh, (NiTriShape*)node, flags); | ||||
|     handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds); | ||||
| } | ||||
| 
 | ||||
| void NIFLoader::loadResource(Resource *resource) | ||||
|  | @ -588,6 +684,9 @@ void NIFLoader::loadResource(Resource *resource) | |||
|       return; | ||||
|     } | ||||
| 
 | ||||
|   // Helper that computes bounding boxes for us.
 | ||||
|   BoundsFinder bounds; | ||||
| 
 | ||||
|   // Load the NIF. TODO: Wrap this in a try-catch block once we're out
 | ||||
|   // of the early stages of development. Right now we WANT to catch
 | ||||
|   // every error as early and intrusively as possible, as it's most
 | ||||
|  | @ -604,19 +703,25 @@ void NIFLoader::loadResource(Resource *resource) | |||
|   Record *r = nif.getRecord(0); | ||||
|   assert(r != NULL); | ||||
| 
 | ||||
|   if(r->recType != RC_NiNode) | ||||
|   Nif::Node *node = dynamic_cast<Nif::Node*>(r); | ||||
| 
 | ||||
|   if(node == NULL) | ||||
|     { | ||||
|       warn("First record in file was not a NiNode, but a " + | ||||
|       warn("First record in file was not a node, but a " + | ||||
|            r->recName.toString() + ". Skipping file."); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|   // Handle the node
 | ||||
|   handleNode(mesh, (Nif::Node*)r, 0); | ||||
|   handleNode(mesh, node, 0, NULL, bounds); | ||||
| 
 | ||||
|   // Finally, set the bounding value. Just use bogus info right now.
 | ||||
|   mesh->_setBounds(AxisAlignedBox(-10,-10,-10,10,10,10)); | ||||
|   mesh->_setBoundingSphereRadius(10); | ||||
|   // Finally, 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()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| MeshPtr NIFLoader::load(const std::string &name, | ||||
|  | @ -632,3 +737,109 @@ MeshPtr NIFLoader::load(const std::string &name, | |||
|   // Nope, create a new one.
 | ||||
|   return MeshManager::getSingleton().createManual(name, group, &g_sing); | ||||
| } | ||||
| 
 | ||||
| /* More code currently not in use, from the old D source. This was
 | ||||
|    used in the first attempt at loading NIF meshes, where each submesh | ||||
|    in the file was given a separate bone in a skeleton. Unfortunately | ||||
|    the OGRE skeletons can't hold more than 256 bones, and some NIFs go | ||||
|    way beyond that. The code might be of use if we implement animated | ||||
|    submeshes like this (the part of the NIF that is animated is | ||||
|    usually much less than the entire file, but the method might still | ||||
|    not be water tight.) | ||||
| 
 | ||||
| // Insert a raw RGBA image into the texture system.
 | ||||
| extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, void *data) | ||||
| { | ||||
|   TexturePtr texture = TextureManager::getSingleton().createManual( | ||||
|       name, 		// name
 | ||||
|       "General",	// group
 | ||||
|       TEX_TYPE_2D,     	// type
 | ||||
|       width, height,    // width & height
 | ||||
|       0,                // number of mipmaps
 | ||||
|       PF_BYTE_RGBA,     // pixel format
 | ||||
|       TU_DEFAULT);      // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for
 | ||||
|                         // textures updated very often (e.g. each frame)
 | ||||
| 
 | ||||
|   // Get the pixel buffer
 | ||||
|   HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); | ||||
| 
 | ||||
|   // Lock the pixel buffer and get a pixel box
 | ||||
|   pixelBuffer->lock(HardwareBuffer::HBL_NORMAL); // for best performance use HBL_DISCARD!
 | ||||
|   const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); | ||||
| 
 | ||||
|   void *dest = pixelBox.data; | ||||
| 
 | ||||
|   // Copy the data
 | ||||
|   memcpy(dest, data, width*height*4); | ||||
| 
 | ||||
|   // Unlock the pixel buffer
 | ||||
|   pixelBuffer->unlock(); | ||||
| } | ||||
| 
 | ||||
| // We need this later for animated meshes.
 | ||||
| extern "C" void* ogre_setupSkeleton(char* name) | ||||
| { | ||||
|   SkeletonPtr skel = SkeletonManager::getSingleton().create( | ||||
|     name, "Closet", true); | ||||
| 
 | ||||
|   skel->load(); | ||||
| 
 | ||||
|   // Create all bones at the origin and unrotated. This is necessary
 | ||||
|   // since our submeshes each have their own model space. We must
 | ||||
|   // move the bones after creating an entity, then copy this entity.
 | ||||
|   return (void*)skel->createBone(); | ||||
| } | ||||
| 
 | ||||
| extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index) | ||||
| { | ||||
|   return (void*) ( ((Bone*)rootBone)->createChild(index) ); | ||||
| } | ||||
| */ | ||||
| /* This was the D part:
 | ||||
| 
 | ||||
|     // Create a skeleton and get the root bone (index 0)
 | ||||
|     BonePtr bone = ogre_setupSkeleton(name); | ||||
| 
 | ||||
|     // Reset the bone index. The next bone to be created has index 1.
 | ||||
|     boneIndex = 1; | ||||
|     // Create a mesh and assign the skeleton to it
 | ||||
|     MeshPtr mesh = ogre_setupMesh(name); | ||||
| 
 | ||||
|     // Loop through the nodes, creating submeshes, materials and
 | ||||
|     // skeleton bones in the process.
 | ||||
|     handleNode(node, bone, mesh); | ||||
| 
 | ||||
|   // Create the "template" entity
 | ||||
|   EntityPtr entity = ogre_createEntity(name); | ||||
| 
 | ||||
|   // Loop through once again, this time to set the right
 | ||||
|   // transformations on the entity's SkeletonInstance. The order of
 | ||||
|   // children will be the same, allowing us to reference bones using
 | ||||
|   // their boneIndex.
 | ||||
|   int lastBone = boneIndex; | ||||
|   boneIndex = 1; | ||||
|   transformBones(node, entity); | ||||
|   if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match"); | ||||
| 
 | ||||
|   if(!hasBBox) | ||||
|     ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ); | ||||
| 
 | ||||
|   return entity; | ||||
| } | ||||
| void handleNode(Node node, BonePtr root, MeshPtr mesh) | ||||
| { | ||||
|   // Insert a new bone for this node
 | ||||
|   BonePtr bone = ogre_insertBone(node.name, root, boneIndex++); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void transformBones(Node node, EntityPtr entity) | ||||
| { | ||||
|   ogre_transformBone(entity, &node.trafo, boneIndex++); | ||||
| 
 | ||||
|   NiNode n = cast(NiNode)node; | ||||
|   if(n !is null) | ||||
|     foreach(Node nd; n.children) | ||||
|       transformBones(nd, entity); | ||||
| } | ||||
| */ | ||||
|  |  | |||
|  | @ -15,6 +15,11 @@ void OgreRenderer::cleanup() | |||
|   mRoot = NULL; | ||||
| } | ||||
| 
 | ||||
| void OgreRenderer::screenshot(const std::string &file) | ||||
| { | ||||
|   mWindow->writeContentsToFile(file); | ||||
| } | ||||
| 
 | ||||
| bool OgreRenderer::configure(bool showConfig, | ||||
|                              const std::string &pluginCfg, | ||||
|                              bool _logging) | ||||
|  |  | |||
|  | @ -42,6 +42,9 @@ namespace Render | |||
|     /// Start the main rendering loop
 | ||||
|     void start() { mRoot->startRendering(); } | ||||
| 
 | ||||
|     /// Write a screenshot to file
 | ||||
|     void screenshot(const std::string &file); | ||||
| 
 | ||||
|     /// Get the Root
 | ||||
|     Ogre::Root *getRoot() { return mRoot; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -57,11 +57,6 @@ import input.ois; | |||
| bool pause = false; | ||||
| int *guiMode; | ||||
| 
 | ||||
| void toggleFullscreen() | ||||
| { | ||||
|   ogre_toggleFullscreen(); | ||||
| } | ||||
| 
 | ||||
| const float volDiff = 0.05; | ||||
| 
 | ||||
| void musVolume(bool increase) | ||||
|  | @ -88,16 +83,6 @@ void mainVolume(bool increase) | |||
|   writefln(increase?"Increasing":"Decreasing", " main volume to ", config.getMainVolume); | ||||
| } | ||||
| 
 | ||||
| void takeScreenShot() | ||||
| { | ||||
|   char[] file = format("screenshot_%06d.png", config.screenShotNum++); | ||||
|   ogre_screenshot(toStringz(file)); | ||||
|   writefln("Wrote '%s'", file); | ||||
| } | ||||
| 
 | ||||
| // Mouse sensitivity
 | ||||
| float effMX, effMY; | ||||
| 
 | ||||
| void updateMouseSensitivity() | ||||
| { | ||||
|   effMX = *config.mouseSensX; | ||||
|  | @ -112,19 +97,6 @@ void togglePause() | |||
|   else writefln("Pause off"); | ||||
| } | ||||
| 
 | ||||
| extern(C) void d_handleMouseMove(MouseState *state) | ||||
| { | ||||
|   debug(printMouseMove) | ||||
|     writefln("handleMouseMove: Abs(%s, %s, %s) Rel(%s, %s, %s)", | ||||
|              state.X.abs, state.Y.abs, state.Z.abs, | ||||
|              state.X.rel, state.Y.rel, state.Z.rel); | ||||
| 
 | ||||
|   if(*guiMode) return; | ||||
| 
 | ||||
|   ogre_rotateCamera( state.X.rel * effMX, | ||||
|                      state.Y.rel * effMY ); | ||||
| } | ||||
| 
 | ||||
| extern(C) void d_handleMouseButton(MouseState *state, int button) | ||||
| { | ||||
|   debug(printMouse) | ||||
|  |  | |||
|  | @ -1,272 +0,0 @@ | |||
| /* | ||||
|   OpenMW - The completely unofficial reimplementation of Morrowind | ||||
|   Copyright (C) 2008  Nicolay Korslund | ||||
|   Email: < korslund@gmail.com > | ||||
|   WWW: http://openmw.snaptoad.com/
 | ||||
| 
 | ||||
|   This file (keys.d) is part of the OpenMW package. | ||||
| 
 | ||||
|   OpenMW is distributed as free software: you can redistribute it | ||||
|   and/or modify it under the terms of the GNU General Public License | ||||
|   version 3, as published by the Free Software Foundation. | ||||
| 
 | ||||
|   This program is distributed in the hope that it will be useful, but | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|   General Public License for more details. | ||||
| 
 | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   version 3 along with this program. If not, see | ||||
|   http://www.gnu.org/licenses/ .
 | ||||
| 
 | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
|  * This module handles keyboard and mouse button configuration | ||||
|  */ | ||||
| 
 | ||||
| module input.keys; | ||||
| 
 | ||||
| import std.string; | ||||
| import std.stdio; | ||||
| 
 | ||||
| import input.ois; | ||||
| 
 | ||||
| // List of all functions we need to map to keys. If you add new keys,
 | ||||
| // REMEMBER to add strings for them below as well. TODO: We should
 | ||||
| // redo this entire section so that we insert actual functions into
 | ||||
| // the system instead. That way, if we do not insert a function, the
 | ||||
| // key gets treated as a "non-event" key. Then we will also force the
 | ||||
| // definition of strings and function call to be in the same
 | ||||
| // place. The Keys enum can be eliminated, really. Default keysyms can
 | ||||
| // be added when the functions are inserted. But don't do anything
 | ||||
| // until you know how this will interact with script code.
 | ||||
| enum Keys | ||||
|   { | ||||
|     None = 0, | ||||
| 
 | ||||
|     // Movement
 | ||||
|     MoveLeft, MoveRight, | ||||
|     TurnLeft, TurnRight, | ||||
|     MoveForward, MoveBackward, | ||||
| 
 | ||||
|     // Used eg. when flying or swimming
 | ||||
|     MoveUp, MoveDown, | ||||
| 
 | ||||
|     // These are handled as events, while the above are not.
 | ||||
|     FirstEvent, | ||||
| 
 | ||||
|     // Sound control
 | ||||
|     MainVolUp, MainVolDown, | ||||
|     MusVolUp, MusVolDown, | ||||
|     SfxVolUp, SfxVolDown, | ||||
|     Mute, | ||||
| 
 | ||||
|     // These will not be part of the finished product
 | ||||
|     Fullscreen, | ||||
|     ToggleBattleMusic, | ||||
|     PhysMode, // Toggle physics mode between walking, flying and ghost
 | ||||
|     Nighteye, // Full ambient lighting
 | ||||
|     ToggleGui,// Turn the GUI on/off
 | ||||
|     Console,  // Turn console on/off
 | ||||
|     Debug, | ||||
| 
 | ||||
|     // Misc
 | ||||
|     Pause, | ||||
|     ScreenShot, | ||||
|     Exit, | ||||
| 
 | ||||
|     Length | ||||
|   } | ||||
| 
 | ||||
| // List of keyboard-bound functions
 | ||||
| char[][] keyToString; | ||||
| 
 | ||||
| // Lookup for keyboard key names. TODO: This is currently case
 | ||||
| // sensitive, we should use my own AA to fix that.
 | ||||
| int[char[]] stringToKeysym; | ||||
| 
 | ||||
| // Represents a key binding for a single function. Each function can
 | ||||
| // have any number keys bound to it, including mouse buttons. This
 | ||||
| // might be extended later to allow joystick buttons and other input
 | ||||
| // devices.
 | ||||
| struct KeyBind | ||||
| { | ||||
|   int syms[]; | ||||
| 
 | ||||
|   // Does the given keysym match this binding?
 | ||||
|   bool isMatch(int sym, char ch = 0) | ||||
|   { | ||||
|     assert(sym != 0, "don't send empty syms to isMatch"); | ||||
| 
 | ||||
|     // We don't match characters yet
 | ||||
|     if(sym == KC.CharOnly) | ||||
|       return false; | ||||
| 
 | ||||
|     foreach(s; syms) | ||||
|       if(sym == s) return true; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // Does any of the given syms match this binding?
 | ||||
|   bool isMatchArray(int arr[] ...) | ||||
|   { | ||||
|     foreach(i; arr) | ||||
|       if(i!=0  && isMatch(i)) return true; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // Assign key bindings to this structure. Can be called multiple
 | ||||
|   // times or with multiple paramters (or an array) to bind multiple
 | ||||
|   // keys.
 | ||||
|   void bind(int symlist[] ...) | ||||
|   { | ||||
|     syms ~= symlist; | ||||
|   } | ||||
| 
 | ||||
|   // Remove all bindings to this function
 | ||||
|   void clear() | ||||
|   { | ||||
|     syms = null; | ||||
|   } | ||||
| 
 | ||||
|   // Remove the given syms from this binding, if found.
 | ||||
|   void remove(int symlist[] ...) | ||||
|   { | ||||
|     foreach(rs; symlist) | ||||
|       // Just loop though all the syms and set matching values to
 | ||||
|       // zero. isMatch() will ignore zeros.
 | ||||
|       foreach(ref s; syms) | ||||
|         if(s == rs) s = 0; | ||||
|   } | ||||
| 
 | ||||
|   // Turn the keysym list into a comma separated list of key names
 | ||||
|   char[] getString() | ||||
|   { | ||||
|     char[] res = null; | ||||
|     bool notFirst = false; | ||||
| 
 | ||||
|     foreach(int k; syms) | ||||
|       if(k != 0) // Ignore empty keysyms
 | ||||
|         { | ||||
|           if(notFirst) res ~= ","; | ||||
|           else notFirst = true; | ||||
| 
 | ||||
|           res ~= keysymToString[k]; | ||||
|         } | ||||
| 
 | ||||
|     //writefln("getString returned %s", res);
 | ||||
|     return res; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| KeyBindings keyBindings; | ||||
| 
 | ||||
| // This structure holds the bindings of all the functions
 | ||||
| struct KeyBindings | ||||
| { | ||||
|   KeyBind[] bindings; | ||||
| 
 | ||||
|   // Bind the given function to the given key(s)
 | ||||
|   void bind(Keys func, char[] key1, char[] key2 = "") | ||||
|   { | ||||
|     bind(func, getSym(key1), getSym(key2)); | ||||
|   } | ||||
| 
 | ||||
|   void bind(Keys func, int syms[] ...) | ||||
|   { | ||||
|     // Find other bindings that match this key
 | ||||
|     foreach(int i, ref KeyBind kb; bindings) | ||||
|       if(kb.isMatchArray(syms)) | ||||
|         kb.remove(syms); | ||||
|     bindings[func].bind(syms); | ||||
|   } | ||||
| 
 | ||||
|   // Find the function that matches the given keysym. We could
 | ||||
|   // optimize this, but I'm not sure it's worth it.
 | ||||
|   Keys findMatch(KC keysym, dchar ch) | ||||
|   { | ||||
|     int start=cast(int)Keys.FirstEvent + 1; | ||||
|     foreach(int i, ref KeyBind kb; bindings[start..$]) | ||||
|       if( kb.isMatch(keysym, ch) ) | ||||
|         return cast(Keys)(i+start); | ||||
|     return cast(Keys)0; // No match
 | ||||
|   } | ||||
| 
 | ||||
|   static int getSym(char[] key) | ||||
|   { | ||||
|     key = strip(key); | ||||
|     if(key.length) | ||||
|       { | ||||
| 	int *p = key in stringToKeysym; | ||||
| 	if(p) return *p; | ||||
| 	else writefln("Warning: unknown key '%s'", key); | ||||
|       } | ||||
|     return 0; | ||||
|   } | ||||
| 
 | ||||
|   // Bind a function to a comma-separated key list (intended to be
 | ||||
|   // used directly with the ini file reader.)
 | ||||
|   void bindComma(Keys func, char[] keys) | ||||
|   { | ||||
|     int index = keys.find(','); | ||||
|     if(index != -1) | ||||
|       { | ||||
|         // Bind the first in the list
 | ||||
|         bind(func, keys[0..index]); | ||||
|         // Recurse on the rest
 | ||||
|         bindComma(func, keys[index+1..$]); | ||||
|       } | ||||
|     // Last or only element in the list
 | ||||
|     else bind(func, keys); | ||||
|   } | ||||
| 
 | ||||
|   // Remove all key bindings
 | ||||
|   void clear() | ||||
|   { | ||||
|     foreach(ref kb; bindings) | ||||
|       kb.clear(); | ||||
|   } | ||||
| 
 | ||||
|   void initKeys() | ||||
|   { | ||||
|     // Keyboard functions
 | ||||
|     keyToString.length = Keys.Length; | ||||
| 
 | ||||
|     keyToString[Keys.MoveLeft] = "Move Left"; | ||||
|     keyToString[Keys.MoveRight] = "Move Right"; | ||||
|     keyToString[Keys.TurnLeft] = "Turn Left"; | ||||
|     keyToString[Keys.TurnRight] = "Turn Right"; | ||||
|     keyToString[Keys.MoveForward] = "Move Forward"; | ||||
|     keyToString[Keys.MoveBackward] = "Move Backward"; | ||||
|     keyToString[Keys.MoveUp] = "Move Up"; | ||||
|     keyToString[Keys.MoveDown] = "Move Down"; | ||||
| 
 | ||||
|     keyToString[Keys.MainVolUp] = "Increase Main Volume"; | ||||
|     keyToString[Keys.MainVolDown] = "Decrease Main Volume"; | ||||
|     keyToString[Keys.MusVolUp] = "Increase Music Volume"; | ||||
|     keyToString[Keys.MusVolDown] = "Decrease Music Volume"; | ||||
|     keyToString[Keys.SfxVolUp] = "Increase SFX Volume"; | ||||
|     keyToString[Keys.SfxVolDown] = "Decrease SFX Volume"; | ||||
|     keyToString[Keys.Mute] = "Mute Sound"; | ||||
| 
 | ||||
|     keyToString[Keys.Fullscreen] = "Toggle Fullscreen Mode"; | ||||
|     keyToString[Keys.ToggleBattleMusic] = "Toggle Battle Music"; | ||||
|     keyToString[Keys.PhysMode] = "Toggle Physics Mode"; | ||||
|     keyToString[Keys.Nighteye] = "Toggle Nighteye"; | ||||
|     keyToString[Keys.ToggleGui] = "Toggle GUI"; | ||||
|     keyToString[Keys.Console] = "Console"; | ||||
|     keyToString[Keys.Debug] = "OGRE Test Action"; | ||||
| 
 | ||||
|     keyToString[Keys.Pause] = "Pause"; | ||||
|     keyToString[Keys.ScreenShot] = "Screen Shot"; | ||||
|     keyToString[Keys.Exit] = "Quick Exit"; | ||||
|     //keyToString[Keys.] = "";
 | ||||
| 
 | ||||
|     bindings.length = Keys.Length; | ||||
| 
 | ||||
|     // Store all the key strings in a lookup-table
 | ||||
|     foreach(int k, ref char[] s; keysymToString) | ||||
|       if(s.length) stringToKeysym[s] = k; | ||||
|   } | ||||
| } | ||||
|  | @ -1,166 +0,0 @@ | |||
| /* | ||||
|   OpenMW - The completely unofficial reimplementation of Morrowind | ||||
|   Copyright (C) 2008  Nicolay Korslund | ||||
|   Email: < korslund@gmail.com > | ||||
|   WWW: http://openmw.snaptoad.com/
 | ||||
| 
 | ||||
|   This file (bindings.d) is part of the OpenMW package. | ||||
| 
 | ||||
|   OpenMW is distributed as free software: you can redistribute it | ||||
|   and/or modify it under the terms of the GNU General Public License | ||||
|   version 3, as published by the Free Software Foundation. | ||||
| 
 | ||||
|   This program is distributed in the hope that it will be useful, but | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|   General Public License for more details. | ||||
| 
 | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   version 3 along with this program. If not, see | ||||
|   http://www.gnu.org/licenses/ .
 | ||||
| 
 | ||||
|  */ | ||||
| 
 | ||||
| module ogre.bindings; | ||||
| 
 | ||||
| import nif.misc; // for Transformation
 | ||||
| import ogre.ogre; // for Placement
 | ||||
| 
 | ||||
| import core.resource; | ||||
| 
 | ||||
| /* | ||||
|  * This module is the interface to OGRE from D. Since OGRE is written | ||||
|  * in C++, all the code that deals directly with the graphics engine | ||||
|  * is packaged in a bunch of C++ functions. These functions are | ||||
|  * exported from C++ through the C calling convention, and imported | ||||
|  * here. | ||||
|  * | ||||
|  * Note that the C calling convension is not in any way type | ||||
|  * safe. This is convenient, as it allows us to send pointers as one | ||||
|  * type and recieve them as another, without casting, but also | ||||
|  * dangerous since it opens for some nasty bugs. | ||||
|  */ | ||||
| 
 | ||||
| // Represents a pointer to a Node in the OGRE engine. We never use
 | ||||
| // these directly in D code, only pass them back to the C++ code.
 | ||||
| typedef void* NodePtr; | ||||
| 
 | ||||
| extern(C): | ||||
| 
 | ||||
| // Do engine configuration. Returns 0 if we should continue, 1 if
 | ||||
| // not.
 | ||||
| int ogre_configure(int showConfig, // Do we show the config dialogue?
 | ||||
|                    char *plugincfg,// Name of 'plugin.cfg' file
 | ||||
|                    int debutOut);  // Enable or disable debug output
 | ||||
| 
 | ||||
| // Sets up the window
 | ||||
| void ogre_initWindow(); | ||||
| 
 | ||||
| // Set up an empty scene.
 | ||||
| void ogre_makeScene(); | ||||
| 
 | ||||
| // Set the ambient light and "sunlight"
 | ||||
| void ogre_setAmbient(float r, float g, float b, | ||||
| 		    float rs, float gs, float bs); | ||||
| 
 | ||||
| // Set fog color and view distance
 | ||||
| void ogre_setFog(float rf, float gf, float bf, | ||||
| 		float flow, float fhigh); | ||||
| 
 | ||||
| // Create a simple sky dome
 | ||||
| int ogre_makeSky(); | ||||
| 
 | ||||
| // Toggle full ambient lighting on and off
 | ||||
| void ogre_toggleLight(); | ||||
| 
 | ||||
| // Enter main rendering loop
 | ||||
| void ogre_startRendering(); | ||||
| 
 | ||||
| // Cleans up after ogre
 | ||||
| void ogre_cleanup(); | ||||
| 
 | ||||
| // Gets a child SceneNode from the root node, then detatches it to
 | ||||
| // hide it from view. Used for creating the "template" node associated
 | ||||
| // with a NIF mesh.
 | ||||
| NodePtr ogre_getDetachedNode(); | ||||
| 
 | ||||
| // Convert a Morrowind rotation (3 floats) to a quaternion (4 floats)
 | ||||
| void ogre_mwToQuaternion(float *mw, float *quat); | ||||
| 
 | ||||
| // Create a copy of the given scene node, with the given coordinates
 | ||||
| // and rotation (as a quaternion.)
 | ||||
| NodePtr ogre_insertNode(NodePtr base, char* name, | ||||
|                         float *pos, float *quat, float scale); | ||||
| 
 | ||||
| // Get the world transformation of a node, returned as a translation
 | ||||
| // and a matrix. The matrix includes both rotation and scaling. The
 | ||||
| // buffers given must be large enough to store the result (3 and 9
 | ||||
| // floats respectively.)
 | ||||
| void ogre_getWorldTransform(NodePtr node, float *trans, float *matrix); | ||||
| 
 | ||||
| // Create a (very crappy looking) plane to simulate the water level
 | ||||
| void ogre_createWater(float level); | ||||
| 
 | ||||
| // Creates a scene node as a child of 'parent', then translates and
 | ||||
| // rotates it according to the data in 'trafo'.
 | ||||
| NodePtr ogre_createNode( | ||||
| 	     char *name, 		// Name to give the node
 | ||||
| 	     Transformation *trafo,	// Transformation
 | ||||
| 	     NodePtr parent,		// Parent node
 | ||||
| 	     int noRot);		// If 1, don't rotate node
 | ||||
| 
 | ||||
| // Create a light with the given diffuse color. Attach it to SceneNode
 | ||||
| // 'parent'.
 | ||||
| NodePtr ogre_attachLight(char* name, NodePtr parent, | ||||
|                          float r, float g, float b, | ||||
|                          float radius); | ||||
| 
 | ||||
| // Create the specified material
 | ||||
| void ogre_createMaterial(char *name,	  // Name to give resource
 | ||||
|                          float *ambient,  // Ambient RBG value
 | ||||
|                          float *diffuse, | ||||
|                          float *specular, | ||||
|                          float *emissive, // Self illumination
 | ||||
|                          float glossiness,// Same as shininess?
 | ||||
|                          float alpha,     // Reflection alpha?
 | ||||
|                          char *texture,   // Texture name
 | ||||
|                          int alphaFlags,  // Alpha settings (see
 | ||||
|                          ubyte alphaTest);// NiAlphaProperty in nif/)
 | ||||
| 
 | ||||
| // Creates a mesh and gives it a bounding box. Also creates an entity
 | ||||
| // and attached it to the given SceneNode 'owner'.
 | ||||
| void ogre_createMesh( | ||||
| 		char* name,		// Name of the mesh
 | ||||
| 		int numVerts,		// Number of vertices
 | ||||
| 		float* vertices,	// Vertex list
 | ||||
| 		float* normals,		// Normal list
 | ||||
| 		float* colors,		// Vertex colors
 | ||||
| 		float* uvs,		// Texture coordinates
 | ||||
| 		int numFaces,		// Number of faces*3
 | ||||
| 		short* faces,		// Faces
 | ||||
| 		float radius,		// Bounding sphere
 | ||||
| 		char* material,		// Material name, if any
 | ||||
| 
 | ||||
| 		// Bounding box
 | ||||
| 		float minX,float minY,float minZ, | ||||
| 		float maxX,float maxY,float maxZ, | ||||
| 
 | ||||
| 		NodePtr owner		// Scene node to attach to.
 | ||||
| 		); | ||||
| 
 | ||||
| // Toggle fullscreen mode
 | ||||
| void ogre_toggleFullscreen(); | ||||
| 
 | ||||
| // Save a screen shot to the given file name
 | ||||
| void ogre_screenshot(char *filename); | ||||
| 
 | ||||
| // Camera control and information
 | ||||
| void ogre_rotateCamera(float x, float y); | ||||
| void ogre_moveCamera(float x, float y, float z); | ||||
| void ogre_setCameraRotation(float r1, float r2, float r3); | ||||
| void ogre_getCameraPos(float *x, float *y, float *z); | ||||
| void ogre_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz); | ||||
| void ogre_moveCameraRel(float x, float y, float z); | ||||
| 
 | ||||
| // Insert a raw RGBA image into the texture system.
 | ||||
| //void ogre_insertTexture(char *name, int width, int height, void *data);
 | ||||
|  | @ -1,39 +1,3 @@ | |||
| /*
 | ||||
|   OpenMW - The completely unofficial reimplementation of Morrowind | ||||
|   Copyright (C) 2008  Nicolay Korslund | ||||
|   Email: < korslund@gmail.com > | ||||
|   WWW: http://openmw.snaptoad.com/
 | ||||
| 
 | ||||
|   This file (cpp_framelistener.cpp) is part of the OpenMW package. | ||||
| 
 | ||||
|   OpenMW is distributed as free software: you can redistribute it | ||||
|   and/or modify it under the terms of the GNU General Public License | ||||
|   version 3, as published by the Free Software Foundation. | ||||
| 
 | ||||
|   This program is distributed in the hope that it will be useful, but | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|   General Public License for more details. | ||||
| 
 | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   version 3 along with this program. If not, see | ||||
|   http://www.gnu.org/licenses/ .
 | ||||
| 
 | ||||
|  */ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| // Callbacks to D code.
 | ||||
| 
 | ||||
| // Called once each frame
 | ||||
| extern "C" int32_t d_frameStarted(float time); | ||||
| 
 | ||||
| // Handle events
 | ||||
| extern "C" void d_handleKey(int keycode, uint32_t text); | ||||
| extern "C" void d_handleMouseMove(const OIS::MouseState *state); | ||||
| extern "C" void d_handleMouseButton(const OIS::MouseState *state, | ||||
|                                     int32_t button); | ||||
| 
 | ||||
| // Frame listener, passed to Ogre. The only thing we use this for is
 | ||||
| // to capture input and pass control to D code.
 | ||||
| class MorroFrameListener: public FrameListener | ||||
|  | @ -128,47 +92,6 @@ public: | |||
| MorroFrameListener mFrameListener; | ||||
| InputListener mInput; | ||||
| 
 | ||||
| // Functions called from D during event handling
 | ||||
| 
 | ||||
| extern "C" int32_t ois_isPressed(int32_t keysym) | ||||
| { | ||||
|   return mKeyboard->isKeyDown((OIS::KeyCode)keysym); | ||||
| } | ||||
| 
 | ||||
| // Dump screen contents to file
 | ||||
| extern "C" void ogre_screenshot(char* filename) | ||||
| { | ||||
|   mWindow->writeContentsToFile(filename); | ||||
| 
 | ||||
|   //This doesn't work, I think I have to set up an overlay or
 | ||||
|   //something first and display the text manually.
 | ||||
|   //mWindow->setDebugText(String("Wrote ") + filename);
 | ||||
| } | ||||
| 
 | ||||
| // Rotate camera as result of mouse movement
 | ||||
| extern "C" void ogre_rotateCamera(float x, float y) | ||||
| { | ||||
|   mCamera->yaw(Degree(-x)); | ||||
| 
 | ||||
|   Quaternion nopitch = mCamera->getOrientation(); | ||||
| 
 | ||||
|   mCamera->pitch(Degree(-y)); | ||||
| 
 | ||||
|   // Is the camera close to being upside down?
 | ||||
|   if(mCamera->getUp()[1] <= 0.1) | ||||
|     // If so, undo the last pitch
 | ||||
|     mCamera->setOrientation(nopitch); | ||||
| } | ||||
| 
 | ||||
| // Get current camera position
 | ||||
| extern "C" void ogre_getCameraPos(float *x, float *y, float *z) | ||||
| { | ||||
|   Vector3 pos = mCamera->getPosition(); | ||||
|   *x = pos[0]; | ||||
|   *y = -pos[2]; | ||||
|   *z = pos[1]; | ||||
| } | ||||
| 
 | ||||
| // Get current camera orientation, in the form of 'front' and 'up'
 | ||||
| // vectors.
 | ||||
| extern "C" void ogre_getCameraOrientation(float *fx, float *fy, float *fz, | ||||
|  | @ -191,8 +114,6 @@ extern "C" void ogre_moveCamera(float x, float y, float z) | |||
|   // is not affected by the rotation of the root node, so we must
 | ||||
|   // transform this manually.
 | ||||
|   mCamera->setPosition(Vector3(x,z+90,-y)); | ||||
| 
 | ||||
|   //g_light->setPosition(mCamera->getPosition());
 | ||||
| } | ||||
| 
 | ||||
| // Rotate camera using Morrowind rotation specifiers
 | ||||
|  | @ -212,12 +133,3 @@ extern "C" void ogre_setCameraRotation(float r1, float r2, float r3) | |||
|   // Rotates first around z, then y, then x
 | ||||
|   mCamera->setOrientation(xr*yr*zr); | ||||
| } | ||||
| 
 | ||||
| // Move camera relative to its own axis set.
 | ||||
| extern "C" void ogre_moveCameraRel(float x, float y, float z) | ||||
| { | ||||
|   mCamera->moveRelative(Vector3(x,0,z)); | ||||
| 
 | ||||
|   // up/down movement is always done relative the world axis
 | ||||
|   mCamera->move(Vector3(0,y,0)); | ||||
| } | ||||
|  |  | |||
|  | @ -1,96 +1,3 @@ | |||
| /*
 | ||||
|   OpenMW - The completely unofficial reimplementation of Morrowind | ||||
|   Copyright (C) 2008  Nicolay Korslund | ||||
|   Email: < korslund@gmail.com > | ||||
|   WWW: http://openmw.snaptoad.com/
 | ||||
| 
 | ||||
|   This file (cpp_interface.cpp) is part of the OpenMW package. | ||||
| 
 | ||||
|   OpenMW is distributed as free software: you can redistribute it | ||||
|   and/or modify it under the terms of the GNU General Public License | ||||
|   version 3, as published by the Free Software Foundation. | ||||
| 
 | ||||
|   This program is distributed in the hope that it will be useful, but | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|   General Public License for more details. | ||||
| 
 | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   version 3 along with this program. If not, see | ||||
|   http://www.gnu.org/licenses/ .
 | ||||
| 
 | ||||
|  */ | ||||
| 
 | ||||
| extern "C" Light* ogre_attachLight(char *name, SceneNode* base, | ||||
| 				  float r, float g, float b, | ||||
| 				  float radius) | ||||
| { | ||||
|   Light *l = mSceneMgr->createLight(name); | ||||
|   l->setDiffuseColour(r,g,b); | ||||
| 
 | ||||
|   radius /= 4.0f; | ||||
| 
 | ||||
|   float cval=0.0f, lval=0.0f, qval=0.0f; | ||||
|   if(lightConst) | ||||
|     cval = lightConstValue; | ||||
|   if(!lightOutQuadInLin) | ||||
|   { | ||||
|     if(lightLinear) | ||||
|       radius *= lightLinearRadiusMult; | ||||
|     if(lightQuadratic) | ||||
|       radius *= lightQuadraticRadiusMult; | ||||
| 
 | ||||
|     if(lightLinear) | ||||
|       lval = lightLinearValue / pow(radius, lightLinearMethod); | ||||
|     if(lightQuadratic) | ||||
|       qval = lightQuadraticValue / pow(radius, lightQuadraticMethod); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     // FIXME:
 | ||||
|     // Do quadratic or linear, depending if we're in an exterior or interior
 | ||||
|     // cell, respectively. Ignore lightLinear and lightQuadratic.
 | ||||
|   } | ||||
| 
 | ||||
|   // The first parameter is a cutoff value on which meshes to
 | ||||
|   // light. If it's set to small, some meshes will end up 'flashing'
 | ||||
|   // in and out of light depending on the camera distance from the
 | ||||
|   // light.
 | ||||
|   l->setAttenuation(10*radius, cval, lval, qval); | ||||
| 
 | ||||
|   // base might be null, sometimes lights don't have meshes
 | ||||
|   if(base) base->attachObject(l); | ||||
| 
 | ||||
|   return l; | ||||
| } | ||||
| 
 | ||||
| extern "C" void ogre_setAmbient(float r, float g, float b, // Ambient light
 | ||||
| 			       float rs, float gs, float bs) // "Sunlight"
 | ||||
| { | ||||
|   g_ambient = ColourValue(r, g, b); | ||||
|   mSceneMgr->setAmbientLight(g_ambient); | ||||
| 
 | ||||
|   // Create a "sun" that shines light downwards. It doesn't look
 | ||||
|   // completely right, but leave it for now.
 | ||||
|   Light *l = mSceneMgr->createLight("Sun"); | ||||
|   l->setDiffuseColour(rs, gs, bs); | ||||
|   l->setType(Light::LT_DIRECTIONAL); | ||||
|   l->setDirection(0,-1,0); | ||||
| } | ||||
| 
 | ||||
| extern "C" void ogre_setFog(float rf, float gf, float bf, // Fog color
 | ||||
| 			float flow, float fhigh) // Fog distance
 | ||||
| { | ||||
|   ColourValue fogColor( rf, gf, bf ); | ||||
|   mSceneMgr->setFog( FOG_LINEAR, fogColor, 0.0, flow, fhigh ); | ||||
| 
 | ||||
|   // Don't render what you can't see anyway
 | ||||
|   mCamera->setFarClipDistance(fhigh + 10); | ||||
| 
 | ||||
|   // Leave this out for now
 | ||||
|   vp->setBackgroundColour(fogColor); | ||||
| } | ||||
| 
 | ||||
| // Copy a scene node and all its children
 | ||||
| void cloneNode(SceneNode *from, SceneNode *to, char* name) | ||||
| { | ||||
|  | @ -185,246 +92,6 @@ extern "C" void ogre_createWater(float level) | |||
|     ent->setCastShadows(false); | ||||
| } | ||||
| 
 | ||||
| // Manual loader for meshes. Reloading individual meshes is too
 | ||||
| // difficult, and not worth the trouble. Later I should make one
 | ||||
| // loader for each NIF file, and whenever it is invoked it should
 | ||||
| // somehow reload the entire file. How this is to be done when some of
 | ||||
| // the meshes might be loaded and in use already, I have no
 | ||||
| // idea. Let's just ignore it for now.
 | ||||
| 
 | ||||
| class MeshLoader : public ManualResourceLoader | ||||
| { | ||||
| public: | ||||
| 
 | ||||
|   void loadResource(Resource *resource) | ||||
|   { | ||||
|   } | ||||
| } dummyLoader; | ||||
| 
 | ||||
| // Load the contents of a mesh
 | ||||
| extern "C" void ogre_createMesh( | ||||
| 		char* name,		// Name of the mesh
 | ||||
| 		int32_t numVerts,	// Number of vertices
 | ||||
| 		float* vertices,	// Vertex list
 | ||||
| 		float* normals,		// Normal list
 | ||||
| 		float* colors,		// Vertex colors
 | ||||
| 		float* uvs,		// Texture coordinates
 | ||||
| 		int32_t numFaces,	// Number of faces*3
 | ||||
| 		uint16_t* faces,	// Faces
 | ||||
| 		float radius,		// Bounding sphere
 | ||||
| 		char* material,		// Material
 | ||||
| 		// Bounding box
 | ||||
| 		float minX,float minY,float minZ, | ||||
| 		float maxX,float maxY,float maxZ, | ||||
| 		SceneNode *owner | ||||
| 		) | ||||
| { | ||||
|   //std::cerr << "Creating mesh " << name << "\n";
 | ||||
| 
 | ||||
|   MeshPtr msh = MeshManager::getSingleton().createManual(name, "Meshes", | ||||
| 							 &dummyLoader); | ||||
| 
 | ||||
|   Entity *e = mSceneMgr->createEntity(name, name); | ||||
| 
 | ||||
|   owner->attachObject(e); | ||||
|   //msh->setSkeletonName(name);
 | ||||
| 
 | ||||
|   // Create vertex data structure
 | ||||
|   msh->sharedVertexData = new VertexData(); | ||||
|   msh->sharedVertexData->vertexCount = numVerts; | ||||
| 
 | ||||
|   /// Create declaration (memory format) of vertex data
 | ||||
|   VertexDeclaration* decl = msh->sharedVertexData->vertexDeclaration; | ||||
| 
 | ||||
|   int nextBuf = 0; | ||||
|   // 1st buffer
 | ||||
|   decl->addElement(nextBuf, 0, VET_FLOAT3, VES_POSITION); | ||||
| 
 | ||||
|   /// Allocate vertex buffer of the requested number of vertices (vertexCount) 
 | ||||
|   /// and bytes per vertex (offset)
 | ||||
|   HardwareVertexBufferSharedPtr vbuf =  | ||||
|     HardwareBufferManager::getSingleton().createVertexBuffer( | ||||
| 	VertexElement::getTypeSize(VET_FLOAT3), | ||||
|         numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); | ||||
| 
 | ||||
|   /// Upload the vertex data to the card
 | ||||
|   vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true); | ||||
| 
 | ||||
|   /// Set vertex buffer binding so buffer 0 is bound to our vertex buffer
 | ||||
|   VertexBufferBinding* bind = msh->sharedVertexData->vertexBufferBinding;  | ||||
|   bind->setBinding(nextBuf++, vbuf); | ||||
| 
 | ||||
|   // The lists are read in the same order that they appear in NIF
 | ||||
|   // files, and likely in memory. Sequential reads might possibly
 | ||||
|   // avert an occational cache miss.
 | ||||
| 
 | ||||
|   // normals
 | ||||
|   if(normals) | ||||
|     { | ||||
|       //std::cerr << "+ Adding normals\n";
 | ||||
|       decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL); | ||||
|       vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( | ||||
|           VertexElement::getTypeSize(VET_FLOAT3), | ||||
|           numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); | ||||
| 
 | ||||
|       vbuf->writeData(0, vbuf->getSizeInBytes(), normals, true); | ||||
| 
 | ||||
|       bind->setBinding(nextBuf++, vbuf);        | ||||
|     } | ||||
| 
 | ||||
|   // vertex colors
 | ||||
|   if(colors) | ||||
|     { | ||||
|       //std::cerr << "+ Adding vertex colors\n";
 | ||||
|       // Use render system to convert colour value since colour packing varies
 | ||||
|       RenderSystem* rs = Root::getSingleton().getRenderSystem(); | ||||
|       RGBA colorsRGB[numVerts]; | ||||
|       RGBA *pColour = colorsRGB; | ||||
|       for(int i=0; i<numVerts; i++) | ||||
| 	{ | ||||
| 	  rs->convertColourValue(ColourValue(colors[0],colors[1],colors[2], colors[3]), | ||||
| 				 pColour++); | ||||
| 	  colors += 4; | ||||
| 	} | ||||
| 
 | ||||
|       decl->addElement(nextBuf, 0, VET_COLOUR, VES_DIFFUSE); | ||||
|       /// Allocate vertex buffer of the requested number of vertices (vertexCount) 
 | ||||
|       /// and bytes per vertex (offset)
 | ||||
|       vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( | ||||
|           VertexElement::getTypeSize(VET_COLOUR), | ||||
| 	  numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); | ||||
|       /// Upload the vertex data to the card
 | ||||
|       vbuf->writeData(0, vbuf->getSizeInBytes(), colorsRGB, true); | ||||
| 
 | ||||
|       /// Set vertex buffer binding so buffer 1 is bound to our colour buffer
 | ||||
|       bind->setBinding(nextBuf++, vbuf); | ||||
|     } | ||||
| 
 | ||||
|   if(uvs) | ||||
|     { | ||||
|       //std::cerr << "+ Adding texture coordinates\n";
 | ||||
|       decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES); | ||||
|       vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( | ||||
|           VertexElement::getTypeSize(VET_FLOAT2), | ||||
|           numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY); | ||||
| 
 | ||||
|       vbuf->writeData(0, vbuf->getSizeInBytes(), uvs, true); | ||||
| 
 | ||||
|       bind->setBinding(nextBuf++, vbuf);        | ||||
|     } | ||||
| 
 | ||||
|   // Create the submesh that holds triangle data
 | ||||
|   SubMesh* sub = msh->createSubMesh(name); | ||||
|   sub->useSharedVertices = true; | ||||
| 
 | ||||
|   if(numFaces) | ||||
|     { | ||||
|       //std::cerr << "+ Adding faces\n";
 | ||||
|       /// Allocate index buffer of the requested number of faces
 | ||||
|       HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton(). | ||||
| 	createIndexBuffer( | ||||
| 			  HardwareIndexBuffer::IT_16BIT,  | ||||
| 			  numFaces, | ||||
| 			  HardwareBuffer::HBU_STATIC_WRITE_ONLY); | ||||
| 
 | ||||
|       /// Upload the index data to the card
 | ||||
|       ibuf->writeData(0, ibuf->getSizeInBytes(), faces, true); | ||||
| 
 | ||||
|       /// Set parameters of the submesh
 | ||||
|       sub->indexData->indexBuffer = ibuf; | ||||
|       sub->indexData->indexCount = numFaces; | ||||
|       sub->indexData->indexStart = 0; | ||||
|     } | ||||
| 
 | ||||
|   // Create a material with the given texture, if any.
 | ||||
| 
 | ||||
|   // If this mesh has a material, attach it.
 | ||||
|   if(material) sub->setMaterialName(name); | ||||
| 
 | ||||
|   /*
 | ||||
|   // Assign this submesh to the given bone
 | ||||
|   VertexBoneAssignment v; | ||||
|   v.boneIndex = ((Bone*)bone)->getHandle(); | ||||
|   v.weight = 1.0; | ||||
| 
 | ||||
|   std::cerr << "+ Assigning bone index " << v.boneIndex << "\n"; | ||||
| 
 | ||||
|   for(int i=0; i < numVerts; i++) | ||||
|     { | ||||
|       v.vertexIndex = i; | ||||
|       sub->addBoneAssignment(v); | ||||
|     } | ||||
|   */ | ||||
|   /// Set bounding information (for culling)
 | ||||
|   msh->_setBounds(AxisAlignedBox(minX,minY,minZ,maxX,maxY,maxZ)); | ||||
| 
 | ||||
|   //std::cerr << "+ Radius: " << radius << "\n";
 | ||||
|   msh->_setBoundingSphereRadius(radius); | ||||
| } | ||||
| 
 | ||||
| extern "C" void ogre_createMaterial(char *name,	    // Name to give
 | ||||
| 						    // resource
 | ||||
| 
 | ||||
| 				   float *ambient,  // Ambient RBG
 | ||||
| 						    // value
 | ||||
| 				   float *diffuse, | ||||
| 				   float *specular, | ||||
| 				   float *emissive, // Self
 | ||||
| 						    // illumination
 | ||||
| 
 | ||||
| 				   float glossiness,// Same as
 | ||||
| 						    // shininess?
 | ||||
| 
 | ||||
| 				   float alpha,     // Use this in all
 | ||||
| 						    // alpha values?
 | ||||
| 
 | ||||
| 				   char* texture,   // Texture
 | ||||
| 
 | ||||
|                                    int32_t alphaFlags, | ||||
|                                    uint8_t alphaTest) // Alpha settings
 | ||||
| { | ||||
|       MaterialPtr material = MaterialManager::getSingleton().create( | ||||
|         name, | ||||
| 	ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); | ||||
| 
 | ||||
|       // This assigns the texture to this material. If the texture
 | ||||
|       // name is a file name, and this file exists (in a resource
 | ||||
|       // directory), it will automatically be loaded when needed. If
 | ||||
|       // not, we should already have inserted a manual loader for the texture.
 | ||||
|       if(texture) | ||||
|         { | ||||
|           Pass *pass = material->getTechnique(0)->getPass(0); | ||||
|           TextureUnitState *txt = pass->createTextureUnitState(texture); | ||||
| 
 | ||||
|           // Add transparencly.
 | ||||
| 
 | ||||
|           if(alphaFlags != -1) | ||||
|             { | ||||
|               // The 237 alpha flags are by far the most common. Check
 | ||||
|               // NiAlphaProperty in nif/properties.d if you need to
 | ||||
|               // decode other values. 237 basically means normal
 | ||||
|               // transparencly.
 | ||||
|               if(alphaFlags == 237) | ||||
|                 { | ||||
|                   // Enable transparency
 | ||||
|                   pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); | ||||
| 
 | ||||
|                   //pass->setDepthCheckEnabled(false);
 | ||||
|                   pass->setDepthWriteEnabled(false); | ||||
|                 } | ||||
|               else | ||||
|                 std::cout << "UNHANDLED ALPHA FOR " << texture << ": " << alphaFlags << "\n"; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|       // Set bells and whistles
 | ||||
|       material->setAmbient(ambient[0], ambient[1], ambient[2]); | ||||
|       material->setDiffuse(diffuse[0], diffuse[1], diffuse[2], alpha); | ||||
|       material->setSpecular(specular[0], specular[1], specular[2], alpha); | ||||
|       material->setSelfIllumination(emissive[0], emissive[1], emissive[2]); | ||||
|       material->setShininess(glossiness); | ||||
| } | ||||
| 
 | ||||
| extern "C" SceneNode *ogre_getDetachedNode() | ||||
| { | ||||
|   SceneNode *node = mwRoot->createChildSceneNode(); | ||||
|  | @ -466,54 +133,3 @@ extern "C" SceneNode* ogre_createNode( | |||
| 
 | ||||
|   return node; | ||||
| } | ||||
| 
 | ||||
| /* Code currently not in use
 | ||||
| 
 | ||||
| // Insert a raw RGBA image into the texture system.
 | ||||
| extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, void *data) | ||||
| { | ||||
|   TexturePtr texture = TextureManager::getSingleton().createManual( | ||||
|       name, 		// name
 | ||||
|       "General",	// group
 | ||||
|       TEX_TYPE_2D,     	// type
 | ||||
|       width, height,    // width & height
 | ||||
|       0,                // number of mipmaps
 | ||||
|       PF_BYTE_RGBA,     // pixel format
 | ||||
|       TU_DEFAULT);      // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for
 | ||||
|                         // textures updated very often (e.g. each frame)
 | ||||
| 
 | ||||
|   // Get the pixel buffer
 | ||||
|   HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); | ||||
| 
 | ||||
|   // Lock the pixel buffer and get a pixel box
 | ||||
|   pixelBuffer->lock(HardwareBuffer::HBL_NORMAL); // for best performance use HBL_DISCARD!
 | ||||
|   const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); | ||||
| 
 | ||||
|   void *dest = pixelBox.data; | ||||
| 
 | ||||
|   // Copy the data
 | ||||
|   memcpy(dest, data, width*height*4); | ||||
| 
 | ||||
|   // Unlock the pixel buffer
 | ||||
|   pixelBuffer->unlock(); | ||||
| } | ||||
| 
 | ||||
| // We need this later for animated meshes.
 | ||||
| extern "C" void* ogre_setupSkeleton(char* name) | ||||
| { | ||||
|   SkeletonPtr skel = SkeletonManager::getSingleton().create( | ||||
|     name, "Closet", true); | ||||
| 
 | ||||
|   skel->load(); | ||||
| 
 | ||||
|   // Create all bones at the origin and unrotated. This is necessary
 | ||||
|   // since our submeshes each have their own model space. We must
 | ||||
|   // move the bones after creating an entity, then copy this entity.
 | ||||
|   return (void*)skel->createBone();   | ||||
| } | ||||
| 
 | ||||
| extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index) | ||||
| { | ||||
|   return (void*) ( ((Bone*)rootBone)->createChild(index) ); | ||||
| } | ||||
| */ | ||||
|  |  | |||
|  | @ -1,48 +1,5 @@ | |||
| /*
 | ||||
|   OpenMW - The completely unofficial reimplementation of Morrowind | ||||
|   Copyright (C) 2008  Nicolay Korslund | ||||
|   Email: < korslund@gmail.com > | ||||
|   WWW: http://openmw.snaptoad.com/
 | ||||
| 
 | ||||
|   This file (cpp_ogre.cpp) is part of the OpenMW package. | ||||
| 
 | ||||
|   OpenMW is distributed as free software: you can redistribute it | ||||
|   and/or modify it under the terms of the GNU General Public License | ||||
|   version 3, as published by the Free Software Foundation. | ||||
| 
 | ||||
|   This program is distributed in the hope that it will be useful, but | ||||
|   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|   General Public License for more details. | ||||
| 
 | ||||
|   You should have received a copy of the GNU General Public License | ||||
|   version 3 along with this program. If not, see | ||||
|   http://www.gnu.org/licenses/ .
 | ||||
| 
 | ||||
|  */ | ||||
| 
 | ||||
| #include <Ogre.h> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include <Ogre.h> | ||||
| #include <OgreConfigFile.h> | ||||
| #include <OgreStringConverter.h> | ||||
| #include <OgreException.h> | ||||
| #include <OgreArchive.h> | ||||
| #include <OgreArchiveFactory.h> | ||||
| 
 | ||||
| #include <MyGUI.h> | ||||
| 
 | ||||
| #include "../util/dbg.h" | ||||
| 
 | ||||
| using namespace Ogre; | ||||
| 
 | ||||
| ColourValue g_ambient; | ||||
| int g_lightOn = 0; | ||||
| 
 | ||||
| // Set to nonzero if debug mode is enabled
 | ||||
| int g_isDebug = 0; | ||||
| 
 | ||||
| // The global GUI object
 | ||||
| MyGUI::Gui *mGUI; | ||||
| 
 | ||||
|  | @ -51,11 +8,5 @@ MyGUI::Gui *mGUI; | |||
| // input into MyGUI.
 | ||||
| int32_t guiMode = 0; | ||||
| 
 | ||||
| // Include the other parts of the code, and make one big happy object
 | ||||
| // file. This is extremely against the grain of C++ "recomended
 | ||||
| // practice", but I don't care.
 | ||||
| #include "../gui/cpp_mygui.cpp" | ||||
| #include "cpp_framelistener.cpp" | ||||
| #include "cpp_bsaarchive.cpp" | ||||
| #include "cpp_interface.cpp" | ||||
| #include "../terrain/cpp_terrain.cpp" | ||||
|  |  | |||
|  | @ -389,50 +389,3 @@ struct MeshLoader | |||
|       } | ||||
|   } | ||||
| } | ||||
| /* | ||||
|     // Create a skeleton and get the root bone (index 0)
 | ||||
|     BonePtr bone = ogre_setupSkeleton(name); | ||||
| 
 | ||||
|     // Reset the bone index. The next bone to be created has index 1.
 | ||||
|     boneIndex = 1; | ||||
|     // Create a mesh and assign the skeleton to it
 | ||||
|     MeshPtr mesh = ogre_setupMesh(name); | ||||
| 
 | ||||
|     // Loop through the nodes, creating submeshes, materials and
 | ||||
|     // skeleton bones in the process.
 | ||||
|     handleNode(node, bone, mesh); | ||||
| 
 | ||||
|   // Create the "template" entity
 | ||||
|   EntityPtr entity = ogre_createEntity(name); | ||||
| 
 | ||||
|   // Loop through once again, this time to set the right
 | ||||
|   // transformations on the entity's SkeletonInstance. The order of
 | ||||
|   // children will be the same, allowing us to reference bones using
 | ||||
|   // their boneIndex.
 | ||||
|   int lastBone = boneIndex; | ||||
|   boneIndex = 1; | ||||
|   transformBones(node, entity); | ||||
|   if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match"); | ||||
| 
 | ||||
|   if(!hasBBox) | ||||
|     ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ); | ||||
| 
 | ||||
|   return entity; | ||||
| } | ||||
| void handleNode(Node node, BonePtr root, MeshPtr mesh) | ||||
| { | ||||
|   // Insert a new bone for this node
 | ||||
|   BonePtr bone = ogre_insertBone(node.name, root, boneIndex++); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void transformBones(Node node, EntityPtr entity) | ||||
| { | ||||
|   ogre_transformBone(entity, &node.trafo, boneIndex++); | ||||
| 
 | ||||
|   NiNode n = cast(NiNode)node; | ||||
|   if(n !is null) | ||||
|     foreach(Node nd; n.children) | ||||
|       transformBones(nd, entity); | ||||
| } | ||||
| */ | ||||
|  |  | |||
|  | @ -30,19 +30,6 @@ NodePtr placeObject(MeshIndex mesh, Placement *pos, float scale, | |||
|   return node; | ||||
| } | ||||
| 
 | ||||
| void setAmbient(Color amb, Color sun, Color fog, float density) | ||||
| { | ||||
|   ogre_setAmbient(amb.red/255.0, amb.green/255.0, amb.blue/255.0, | ||||
| 		 sun.red/255.0, sun.green/255.0, sun.blue/255.0); | ||||
| 
 | ||||
|   // Calculate fog distance
 | ||||
|   // TODO: Mesh with absolute view distance later
 | ||||
|   float fhigh = 4500 + 9000*(1-density); | ||||
|   float flow = 200 + 2000*(1-density); | ||||
| 
 | ||||
|   ogre_setFog(fog.red/255.0, fog.green/255.0, fog.blue/255.0, 200, fhigh); | ||||
| } | ||||
| 
 | ||||
| // Gives the placement of an item in the scene (position and
 | ||||
| // orientation). It must have this exact structure since we also use
 | ||||
| // it when reading ES files.
 | ||||
|  |  | |||
|  | @ -2,8 +2,10 @@ | |||
| #ifndef _STRINGS_WRAPPER_H | ||||
| #define _STRINGS_WRAPPER_H | ||||
| 
 | ||||
| #ifdef WIN32 | ||||
| #pragma warning(disable: 4996) | ||||
| 
 | ||||
| #define strcasecmp stricmp | ||||
| #define snprintf _snprintf | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue