소스 검색

Refactored Engine to use a startup parameters VariantMap.
Enhanced VariantMap to accept indexing via strings.

Lasse Öörni 12 년 전
부모
커밋
ce562fb894
9개의 변경된 파일235개의 추가작업 그리고 151개의 파일을 삭제
  1. 8 3
      Docs/GettingStarted.dox
  2. 16 1
      Engine/Core/Variant.h
  3. 1 1
      Engine/Engine/CoreAPI.cpp
  4. 190 139
      Engine/Engine/Engine.cpp
  5. 9 2
      Engine/Engine/Engine.h
  6. 1 1
      Engine/IO/Log.cpp
  7. 3 2
      Engine/IO/Log.h
  8. 6 1
      Extras/CharacterDemo/CharacterDemo.cpp
  9. 1 1
      Urho3D/Urho3D.cpp

+ 8 - 3
Docs/GettingStarted.dox

@@ -531,13 +531,18 @@ int Run()
 {
     SharedPtr<Context> context(new Context());
     SharedPtr<Engine> engine(new Engine(context));
-    engine->Initialize("HelloWorld", "HelloWorld.log", GetArguments());
+
+    VariantMap engineParameters = Engine::ParseParameters(GetArguments());
+    engineParameters["WindowTitle"] = "HelloWorld";
+    engineParameters["LogName"] = "HelloWorld.log";
+
+    engine->Initialize(engineParameters);
 
     SharedPtr<HelloWorld> helloWorld(new HelloWorld(context));
     helloWorld->Start();
     while (!engine->IsExiting())
         engine->RunFrame();
-        
+
     return 0;
 }
 
@@ -546,7 +551,7 @@ DEFINE_MAIN(Run())
 
 In our function, we first create the Context object, which holds all subsystems and object factories, and keeps track of event senders and receivers. All Object subclasses need to be supplied a pointer to that context. When using an object factory (such as when creating components) that is automatic, but when creating objects manually, the pointer also needs to be passed manually.
 
-With the context at hand, we create the Engine and initialize it. The arguments for the \ref Engine::Initialize "Initialize()" function are the initial window title, the log file name, and command line parameters, which are parsed using the ParseArguments() helper function.
+With the context at hand, we create the Engine and initialize it. The engine accepts startup parameters in a VariantMap; these can be parsed from the command line arguments using the static helper function Engine::ParseParameters. Here we customize the window title and log file name to differentiate from Urho3D.exe.
 
 After this, we instantiate the HelloWorld object, call its Start() function, and run the main loop until Engine tells that we should exit. The shared pointers will take care of deleting the objects in the correct order; the Context will be the last to be destroyed.
 

+ 16 - 1
Engine/Core/Variant.h

@@ -163,8 +163,23 @@ class Variant;
 
 /// Vector of variants.
 typedef Vector<Variant> VariantVector;
+
 /// Map of variants.
-typedef HashMap<ShortStringHash, Variant> VariantMap;
+class VariantMap : public HashMap<ShortStringHash, Variant>
+{
+public:
+    /// Index the map by string hash. Create a new pair if key not found.
+    Variant& operator [] (const ShortStringHash& key)
+    {
+        return HashMap<ShortStringHash, Variant>::operator [] (key);
+    }
+    
+    /// Index the map by string. Create a new pair if key not found.
+    Variant& operator [] (const String& key)
+    {
+        return HashMap<ShortStringHash, Variant>::operator [] (ShortStringHash(key));
+    }
+};
 
 /// Variable that supports a fixed set of types.
 class Variant

+ 1 - 1
Engine/Engine/CoreAPI.cpp

@@ -351,7 +351,7 @@ static void DestructVariantMap(VariantMap* ptr)
 
 static Variant& VariantMapAt(const String& key, VariantMap& map)
 {
-    return map[ShortStringHash(key)];
+    return map[key];
 }
 
 static Variant& VariantMapAtHash(ShortStringHash key, VariantMap& map)

+ 190 - 139
Engine/Engine/Engine.cpp

@@ -101,120 +101,11 @@ Engine::~Engine()
     context_->RemoveSubsystem<Graphics>();
 }
 
-bool Engine::Initialize(const String& windowTitle, const String& logName, const Vector<String>& arguments, void* externalWindow)
+bool Engine::Initialize(const VariantMap& parameters)
 {
     if (initialized_)
         return true;
     
-    String renderPath;
-    int width = 0;
-    int height = 0;
-    int multiSample = 1;
-    int buffer = 100;
-    int mixRate = 44100;
-    bool fullscreen = true;
-    bool resizable = false;
-    bool vsync = false;
-    bool tripleBuffer = false;
-    bool forceSM2 = false;
-    bool shadows = true;
-    bool lqShadows = false;
-    bool sound = true;
-    bool stereo = true;
-    bool interpolation = true;
-    bool threads = true;
-    int logLevel = -1;
-    bool quiet = false;
-    
-    for (unsigned i = 0; i < arguments.Size(); ++i)
-    {
-        if (arguments[i][0] == '-' && arguments[i].Length() >= 2)
-        {
-            String argument = arguments[i].Substring(1).ToLower();
-            
-            if (argument == "headless")
-                headless_ = true;
-            else if (argument.Substring(0, 3) == "log")
-            {
-                argument = argument.Substring(3).ToUpper();
-                for (logLevel = sizeof(levelPrefixes) / sizeof(String) - 1; logLevel > -1; --logLevel)
-                {
-                    if (argument == levelPrefixes[logLevel])
-                        break;
-                }
-            }
-            else if (argument == "nolimit")
-                SetMaxFps(0);
-            else if (argument == "nosound")
-                sound = false;
-            else if (argument == "noip")
-                interpolation = false;
-            else if (argument == "mono")
-                stereo = false;
-            else if (argument == "prepass")
-                renderPath = "RenderPaths/Prepass.xml";
-            else if (argument == "deferred")
-                renderPath = "RenderPaths/Deferred.xml";
-            else if (argument == "noshadows")
-                shadows = false;
-            else if (argument == "lqshadows")
-                lqShadows = true;
-            else if (argument == "nothreads")
-                threads = false;
-            else if (argument == "sm2")
-                forceSM2 = true;
-            else
-            {
-                int value;
-                if (argument.Length() > 1)
-                    value = ToInt(argument.Substring(1));
-                
-                switch (tolower(argument[0]))
-                {
-                case 'x':
-                    width = value;
-                    break;
-                    
-                case 'y':
-                    height = value;
-                    break;
-                
-                case 'm':
-                    multiSample = value;
-                    break;
-                    
-                case 'b':
-                    buffer = value;
-                    break;
-                    
-                case 'r':
-                    mixRate = value;
-                    break;
-                    
-                case 'v':
-                    vsync = true;
-                    break;
-                    
-                case 't':
-                    tripleBuffer = true;
-                    break;
-                    
-                case 'w':
-                    fullscreen = false;
-                    break;
-                        
-                case 's':
-                    resizable = true;
-                    break;
-
-                case 'q':
-                    quiet = true;
-                    break;
-                }
-            }
-        }
-    }
-    
     // Register object factories and attributes first, then subsystems
     RegisterObjects();
     RegisterSubsystems();
@@ -223,18 +114,22 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     Log* log = GetSubsystem<Log>();
     if (log)
     {
-        if (logLevel != -1)
-            log->SetLevel(logLevel);
-        log->SetQuiet(quiet);
-        log->Open(logName);
+        if (HasParameter(parameters, "LogLevel"))
+            log->SetLevel(GetParameter(parameters, "LogLevel").GetInt());
+        log->SetQuiet(GetParameter(parameters, "LogQuiet", false).GetBool());
+        log->Open(GetParameter(parameters, "LogName", "Urho3D.log").GetString());
     }
     
     // Set maximally accurate low res timer
     GetSubsystem<Time>()->SetTimerPeriod(1);
     
+    // Configure max FPS
+    if (GetParameter(parameters, "FrameLimiter", true) == false)
+        SetMaxFps(0);
+    
     // Set amount of worker threads according to the available physical CPU cores. Using also hyperthreaded cores results in
     // unpredictable extra synchronization overhead. Also reserve one core for the main thread
-    unsigned numThreads = threads ? GetNumPhysicalCPUs() - 1 : 0;
+    unsigned numThreads = GetParameter(parameters, "WorkerThreads", true).GetBool() ? GetNumPhysicalCPUs() - 1 : 0;
     if (numThreads)
     {
         GetSubsystem<WorkQueue>()->CreateThreads(numThreads);
@@ -242,50 +137,99 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
         LOGINFO(ToString("Created %u worker thread%s", numThreads, numThreads > 1 ? "s" : ""));
     }
     
-    // Add default resource paths: CoreData package or directory, Data package or directory
+    // Add resource paths
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     FileSystem* fileSystem = GetSubsystem<FileSystem>();
     String exePath = fileSystem->GetProgramDir();
     
-    if (fileSystem->FileExists(exePath + "CoreData.pak"))
+    Vector<String> resourcePaths = GetParameter(parameters, "ResourcePaths", "CoreData;Data").GetString().Split(';');
+    Vector<String> resourcePackages = GetParameter(parameters, "ResourcePackages").GetString().Split(';');
+    
+    // Prefer to add the resource paths as packages if possible
+    for (unsigned i = 0; i < resourcePaths.Size(); ++i)
     {
-        SharedPtr<PackageFile> package(new PackageFile(context_));
-        package->Open(exePath + "CoreData.pak");
-        cache->AddPackageFile(package);
+        bool success = false;
+        String packageName = exePath + resourcePaths[i] + ".pak";
+        
+        if (fileSystem->FileExists(packageName))
+        {
+            SharedPtr<PackageFile> package(new PackageFile(context_));
+            if (package->Open(packageName))
+            {
+                cache->AddPackageFile(package);
+                success = true;
+            }
+        }
+        
+        if (!success && fileSystem->DirExists(exePath + resourcePaths[i]))
+            success = cache->AddResourceDir(exePath + resourcePaths[i]);
+        
+        if (!success)
+        {
+            LOGERROR("Failed to add resource path " + resourcePaths[i]);
+            return false;
+        }
     }
-    else if (fileSystem->DirExists(exePath + "CoreData"))
-        cache->AddResourceDir(exePath + "CoreData");
     
-    if (fileSystem->FileExists(exePath + "Data.pak"))
+    // Then add specified packages
+    for (unsigned i = 0; i < resourcePackages.Size(); ++i)
     {
-        SharedPtr<PackageFile> package(new PackageFile(context_));
-        package->Open(exePath + "Data.pak");
-        cache->AddPackageFile(package);
+        bool success = false;
+        
+        if (fileSystem->FileExists(exePath + resourcePackages[i]))
+        {
+            SharedPtr<PackageFile> package(new PackageFile(context_));
+            if (package->Open(exePath + resourcePackages[i]))
+            {
+                cache->AddPackageFile(package);
+                success = true;
+            }
+        }
+        
+        if (!success)
+        {
+            LOGERROR("Failed to add resource package " + resourcePackages[i]);
+            return false;
+        }
     }
-    else if (fileSystem->DirExists(exePath + "Data"))
-        cache->AddResourceDir(exePath + "Data");
     
+
     // Initialize graphics & audio output
     if (!headless_)
     {
         Graphics* graphics = GetSubsystem<Graphics>();
         Renderer* renderer = GetSubsystem<Renderer>();
         
-        if (externalWindow)
-            graphics->SetExternalWindow(externalWindow);
-        graphics->SetForceSM2(forceSM2);
-        graphics->SetWindowTitle(windowTitle);
-        if (!graphics->SetMode(width, height, fullscreen, resizable, vsync, tripleBuffer, multiSample))
+        if (HasParameter(parameters, "ExternalWindow"))
+            graphics->SetExternalWindow(GetParameter(parameters, "ExternalWindow").GetPtr());
+        graphics->SetForceSM2(GetParameter(parameters, "ForceSM2", false).GetBool());
+        graphics->SetWindowTitle(GetParameter(parameters, "WindowTitle", "Urho3D").GetString());
+        if (!graphics->SetMode(
+            GetParameter(parameters, "WindowWidth", 0).GetInt(),
+            GetParameter(parameters, "WindowHeight", 0).GetInt(),
+            GetParameter(parameters, "FullScreen", true).GetBool(),
+            GetParameter(parameters, "WindowResizable", true).GetBool(),
+            GetParameter(parameters, "VSync", false).GetBool(),
+            GetParameter(parameters, "TripleBuffer", false).GetBool(),
+            GetParameter(parameters, "MultiSample", 1).GetInt()
+        ))
             return false;
         
-        if (!renderPath.Empty())
-            renderer->SetDefaultRenderPath(cache->GetResource<XMLFile>(renderPath));
-        renderer->SetDrawShadows(shadows);
-        if (shadows && lqShadows)
+        if (HasParameter(parameters, "RenderPath"))
+            renderer->SetDefaultRenderPath(cache->GetResource<XMLFile>(GetParameter(parameters, "RenderPath").GetString()));
+        renderer->SetDrawShadows(GetParameter(parameters, "Shadows", true).GetBool());
+        if (renderer->GetDrawShadows() && GetParameter(parameters, "LowQualityShadows", false).GetBool())
             renderer->SetShadowQuality(SHADOWQUALITY_LOW_16BIT);
         
-        if (sound)
-            GetSubsystem<Audio>()->SetMode(buffer, mixRate, stereo, interpolation);
+        if (GetParameter(parameters, "Sound", true).GetBool())
+        {
+            GetSubsystem<Audio>()->SetMode(
+                GetParameter(parameters, "SoundBuffer", 100).GetInt(),
+                GetParameter(parameters, "SoundMixRate", 44100).GetInt(),
+                GetParameter(parameters, "SoundStereo", true).GetBool(),
+                GetParameter(parameters, "SoundInterpolation", true).GetBool()
+            );
+        }
     }
     
     // Init FPU state of main thread
@@ -581,6 +525,113 @@ void Engine::ApplyFrameLimit()
     timeStep_ = elapsed / 1000000.0f;
 }
 
+VariantMap Engine::ParseParameters(const Vector<String>& arguments)
+{
+    VariantMap ret;
+    
+    for (unsigned i = 0; i < arguments.Size(); ++i)
+    {
+        if (arguments[i][0] == '-' && arguments[i].Length() >= 2)
+        {
+            String argument = arguments[i].Substring(1).ToLower();
+            
+            if (argument == "headless")
+                ret["Headless"] = true;
+            else if (argument.Substring(0, 3) == "log")
+            {
+                argument = argument.Substring(3);
+                int logLevel = GetStringListIndex(argument, logLevelPrefixes, -1);
+                if (logLevel != -1)
+                    ret["LogLevel"] = logLevel;
+            }
+            else if (argument == "nolimit")
+                ret["FrameLimiter"] = false;
+            else if (argument == "nosound")
+                ret["Sound"] = false;
+            else if (argument == "noip")
+                ret["SoundInterpolation"] = false;
+            else if (argument == "mono")
+                ret["SoundStereo"] = false;
+            else if (argument == "prepass")
+                ret["RenderPath"] = "RenderPaths/Prepass.xml";
+            else if (argument == "deferred")
+                ret["RenderPath"] = "RenderPaths/Deferred.xml";
+            else if (argument == "noshadows")
+                ret["Shadows"] = false;
+            else if (argument == "lqshadows")
+                ret["LowQualityShadows"] = true;
+            else if (argument == "nothreads")
+                ret["WorkerThreads"] = false;
+            else if (argument == "sm2")
+                ret["ForceSM2"] = true;
+            else
+            {
+                int value;
+                if (argument.Length() > 1)
+                    value = ToInt(argument.Substring(1));
+                
+                switch (tolower(argument[0]))
+                {
+                case 'x':
+                    ret["WindowWidth"] = value;
+                    break;
+                    
+                case 'y':
+                    ret["WindowHeight"] = value;
+                    break;
+                
+                case 'm':
+                    ret["MultiSample"] = value;
+                    break;
+                    
+                case 'b':
+                    ret["SoundBuffer"] = value;
+                    break;
+                    
+                case 'r':
+                    ret["SoundMixRate"] = value;
+                    break;
+                    
+                case 'v':
+                    ret["VSync"] = true;
+                    break;
+                    
+                case 't':
+                    ret["TripleBuffer"] = true;
+                    break;
+                    
+                case 'w':
+                    ret["FullScreen"] = false;
+                    break;
+                        
+                case 's':
+                    ret["WindowResizable"] = true;
+                    break;
+                    
+                case 'q':
+                    ret["LogQuiet"] = true;
+                    break;
+                }
+            }
+        }
+    }
+    
+    return ret;
+}
+
+bool Engine::HasParameter(const VariantMap& parameters, const String& parameter)
+{
+    ShortStringHash nameHash(parameter);
+    return parameters.Find(nameHash) != parameters.End();
+}
+
+const Variant& Engine::GetParameter(const VariantMap& parameters, const String& parameter, const Variant& defaultValue)
+{
+    ShortStringHash nameHash(parameter);
+    VariantMap::ConstIterator i = parameters.Find(nameHash);
+    return i != parameters.End() ? i->second_ : defaultValue;
+}
+
 void Engine::RegisterObjects()
 {
     RegisterResourceLibrary(context_);

+ 9 - 2
Engine/Engine/Engine.h

@@ -42,8 +42,8 @@ public:
     /// Destruct. Free all subsystems.
     virtual ~Engine();
     
-    /// Initialize and show the application window. Return true if successful.
-    bool Initialize(const String& windowTitle = "Urho3D", const String& logName = "Urho3D.log", const Vector<String>& arguments = Vector<String>(), void* externalWindow = 0);
+    /// Initialize engine using parameters given and show the application window. Return true if successful.
+    bool Initialize(const VariantMap& parameters);
     /// Initialize script subsystem and register the script API. Return true if successful (engine must be initialized first.)
     bool InitializeScripting();
     /// Run one frame.
@@ -91,6 +91,13 @@ public:
     /// Get the timestep for the next frame and sleep for frame limiting if necessary.
     void ApplyFrameLimit();
     
+    /// Parse the engine startup parameters map from command line arguments.
+    static VariantMap ParseParameters(const Vector<String>& arguments);
+    /// Return whether startup parameters contains a specific parameter.
+    static bool HasParameter(const VariantMap& parameters, const String& parameter);
+    /// Get an engine startup parameter, with default value if missing.
+    static const Variant& GetParameter(const VariantMap& parameters, const String& parameter, const Variant& defaultValue = Variant::EMPTY);
+    
 private:
     /// Register object factories and attributes.
     void RegisterObjects();

+ 1 - 1
Engine/IO/Log.cpp

@@ -130,7 +130,7 @@ void Log::Write(int level, const String& message)
     {
         MutexLock lock(GetStaticMutex());
         
-        String formattedMessage = levelPrefixes[level] + ": " + message;
+        String formattedMessage = logLevelPrefixes[level] + ": " + message;
         lastMessage_ = message;
         
         if (timeStamp_)

+ 3 - 2
Engine/IO/Log.h

@@ -38,12 +38,13 @@ static const int LOG_ERROR = 3;
 /// Disable all log messages.
 static const int LOG_NONE = 4;
 
-static const String levelPrefixes[] =
+static const String logLevelPrefixes[] =
 {
     "DEBUG",
     "INFO",
     "WARNING",
-    "ERROR"
+    "ERROR",
+    ""
 };
 
 class File;

+ 6 - 1
Extras/CharacterDemo/CharacterDemo.cpp

@@ -36,7 +36,12 @@ int Run()
 {
     SharedPtr<Context> context(new Context());
     SharedPtr<Engine> engine(new Engine(context));
-    engine->Initialize("CharacterDemo", "CharacterDemo.log", GetArguments());
+    
+    VariantMap engineParameters = Engine::ParseParameters(GetArguments());
+    engineParameters["WindowTitle"] = "CharacterDemo";
+    engineParameters["LogName"] = "CharacterDemo.log";
+    
+    engine->Initialize(engineParameters);
     
     SharedPtr<CharacterDemo> characterDemo(new CharacterDemo(context));
     characterDemo->Start();

+ 1 - 1
Urho3D/Urho3D.cpp

@@ -131,7 +131,7 @@ int Application::Run()
     }
     
     SharedPtr<Engine> engine(new Engine(context_));
-    if (engine->Initialize("Urho3D", "Urho3D.log", arguments))
+    if (engine->Initialize(Engine::ParseParameters(arguments)))
     {
         // Hold a shared pointer to the script file to make sure it is not unloaded during runtime
         engine->InitializeScripting();