Browse Source

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

Lasse Öörni 12 years ago
parent
commit
ce562fb894

+ 8 - 3
Docs/GettingStarted.dox

@@ -531,13 +531,18 @@ int Run()
 {
 {
     SharedPtr<Context> context(new Context());
     SharedPtr<Context> context(new Context());
     SharedPtr<Engine> engine(new Engine(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));
     SharedPtr<HelloWorld> helloWorld(new HelloWorld(context));
     helloWorld->Start();
     helloWorld->Start();
     while (!engine->IsExiting())
     while (!engine->IsExiting())
         engine->RunFrame();
         engine->RunFrame();
-        
+
     return 0;
     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.
 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.
 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.
 /// Vector of variants.
 typedef Vector<Variant> VariantVector;
 typedef Vector<Variant> VariantVector;
+
 /// Map of variants.
 /// 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.
 /// Variable that supports a fixed set of types.
 class Variant
 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)
 static Variant& VariantMapAt(const String& key, VariantMap& map)
 {
 {
-    return map[ShortStringHash(key)];
+    return map[key];
 }
 }
 
 
 static Variant& VariantMapAtHash(ShortStringHash key, VariantMap& map)
 static Variant& VariantMapAtHash(ShortStringHash key, VariantMap& map)

+ 190 - 139
Engine/Engine/Engine.cpp

@@ -101,120 +101,11 @@ Engine::~Engine()
     context_->RemoveSubsystem<Graphics>();
     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_)
     if (initialized_)
         return true;
         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
     // Register object factories and attributes first, then subsystems
     RegisterObjects();
     RegisterObjects();
     RegisterSubsystems();
     RegisterSubsystems();
@@ -223,18 +114,22 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     Log* log = GetSubsystem<Log>();
     Log* log = GetSubsystem<Log>();
     if (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
     // Set maximally accurate low res timer
     GetSubsystem<Time>()->SetTimerPeriod(1);
     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
     // 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
     // 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)
     if (numThreads)
     {
     {
         GetSubsystem<WorkQueue>()->CreateThreads(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" : ""));
         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>();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     FileSystem* fileSystem = GetSubsystem<FileSystem>();
     FileSystem* fileSystem = GetSubsystem<FileSystem>();
     String exePath = fileSystem->GetProgramDir();
     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
     // Initialize graphics & audio output
     if (!headless_)
     if (!headless_)
     {
     {
         Graphics* graphics = GetSubsystem<Graphics>();
         Graphics* graphics = GetSubsystem<Graphics>();
         Renderer* renderer = GetSubsystem<Renderer>();
         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;
             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);
             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
     // Init FPU state of main thread
@@ -581,6 +525,113 @@ void Engine::ApplyFrameLimit()
     timeStep_ = elapsed / 1000000.0f;
     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()
 void Engine::RegisterObjects()
 {
 {
     RegisterResourceLibrary(context_);
     RegisterResourceLibrary(context_);

+ 9 - 2
Engine/Engine/Engine.h

@@ -42,8 +42,8 @@ public:
     /// Destruct. Free all subsystems.
     /// Destruct. Free all subsystems.
     virtual ~Engine();
     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.)
     /// Initialize script subsystem and register the script API. Return true if successful (engine must be initialized first.)
     bool InitializeScripting();
     bool InitializeScripting();
     /// Run one frame.
     /// Run one frame.
@@ -91,6 +91,13 @@ public:
     /// Get the timestep for the next frame and sleep for frame limiting if necessary.
     /// Get the timestep for the next frame and sleep for frame limiting if necessary.
     void ApplyFrameLimit();
     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:
 private:
     /// Register object factories and attributes.
     /// Register object factories and attributes.
     void RegisterObjects();
     void RegisterObjects();

+ 1 - 1
Engine/IO/Log.cpp

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

+ 3 - 2
Engine/IO/Log.h

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

+ 6 - 1
Extras/CharacterDemo/CharacterDemo.cpp

@@ -36,7 +36,12 @@ int Run()
 {
 {
     SharedPtr<Context> context(new Context());
     SharedPtr<Context> context(new Context());
     SharedPtr<Engine> engine(new Engine(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));
     SharedPtr<CharacterDemo> characterDemo(new CharacterDemo(context));
     characterDemo->Start();
     characterDemo->Start();

+ 1 - 1
Urho3D/Urho3D.cpp

@@ -131,7 +131,7 @@ int Application::Run()
     }
     }
     
     
     SharedPtr<Engine> engine(new Engine(context_));
     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
         // Hold a shared pointer to the script file to make sure it is not unloaded during runtime
         engine->InitializeScripting();
         engine->InitializeScripting();