Browse Source

Refactor: Async resource loading exposed to scripting, refactored scene loading to support async loading

BearishSun 7 years ago
parent
commit
87f960dd28

+ 2 - 2
Source/EditorCore/GUI/BsGUIColor.cpp

@@ -18,8 +18,8 @@ namespace bs
 		mColorSprite = bs_new<ImageSprite>();
 		mAlphaSprite = bs_new<ImageSprite>();
 
-		mColorImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture().getInternalPtr();
-		mAlphaImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture().getInternalPtr();
+		mColorImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture();
+		mAlphaImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture();
 	}
 
 	GUIColor::~GUIColor()

+ 4 - 7
Source/EditorCore/GUI/BsGUIColorGradient.cpp

@@ -19,8 +19,8 @@ namespace bs
 		mColorSprite = bs_new<ImageSprite>();
 		mAlphaSprite = bs_new<ImageSprite>();
 
-		mColorImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture().getInternalPtr();
-		mAlphaImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture().getInternalPtr();
+		mColorImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture();
+		mAlphaImageDesc.texture = BuiltinResources::instance().getWhiteSpriteTexture();
 	}
 
 	GUIColorGradient::~GUIColorGradient()
@@ -106,11 +106,8 @@ namespace bs
 		mAlphaImageDesc.width = mLayoutData.area.width;
 		mAlphaImageDesc.height = mLayoutData.area.height - mColorImageDesc.height;
 
-		HSpriteTexture colorTex = SpriteTexture::create(generateGradientTexture(mValue, mLayoutData.area.width, false)); 
-		HSpriteTexture alphaTex = SpriteTexture::create(generateGradientTexture(mValue, mLayoutData.area.width, true)); 
-
-		mColorImageDesc.texture = colorTex.getInternalPtr();
-		mAlphaImageDesc.texture = alphaTex.getInternalPtr();
+		mColorImageDesc.texture = SpriteTexture::create(generateGradientTexture(mValue, mLayoutData.area.width, false)); 
+		mAlphaImageDesc.texture = SpriteTexture::create(generateGradientTexture(mValue, mLayoutData.area.width, true)); 
 
 		mColorSprite->update(mColorImageDesc, (UINT64)_getParentWidget());
 		mAlphaSprite->update(mAlphaImageDesc, (UINT64)_getParentWidget());

+ 37 - 0
Source/Scripting/MBansheeEngine/Generated/ResourceLoadFlag.generated.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+	/** @addtogroup Resources
+	 *  @{
+	 */
+
+	/// <summary>Flags that can be used to control resource loading.</summary>
+	public enum ResourceLoadFlag
+	{
+		/// <summary>No flags.</summary>
+		None = 0,
+		/// <summary>If enabled all resources referenced by the root resource will be loaded as well.</summary>
+		LoadDependencies = 1,
+		/// <summary>
+		/// If enabled the resource system will keep an internal reference to the resource so it doesn't get destroyed when it 
+		/// goes out of scope. You can call Resources::release() to release the internal reference. Each call to load will create 
+		/// a new internal reference and therefore must be followed by the same number of release calls. If dependencies are 
+		/// being loaded, they will not have internal references created regardless of this parameter.
+		/// </summary>
+		KeepInternalRef = 2,
+		/// <summary>
+		/// Determines if the loaded resource keeps original data loaded. Sometime resources will process loaded data and discard 
+		/// the original (e.g. uncompressing audio on load). This flag can prevent the resource from discarding the original 
+		/// data. The original data might be required for saving the resource (via Resources::save), but will use up extra 
+		/// memory. Normally you want to keep this enabled if you plan on saving the resource to disk.
+		/// </summary>
+		KeepSourceData = 4,
+		/// <summary>Default set of flags used for resource loading.</summary>
+		Default = 3
+	}
+
+	/** @} */
+}

+ 2 - 2
Source/Scripting/MBansheeEngine/Resources/ResourceRef.cs

@@ -26,7 +26,7 @@ namespace BansheeEngine
 
         /// <summary>
         /// Returns the referenced resource. If the resource hasn't been loaded it will be loaded as if calling
-        /// <see cref="Resources.Load{T}(string,bool)"/> using default settings.
+        /// <see cref="Resources.Load{T}(string,ResourceLoadFlag)"/> using default settings.
         /// </summary>
         public Resource GenericValue => Internal_GetResource(mCachedPtr);
 
@@ -129,7 +129,7 @@ namespace BansheeEngine
     {
         /// <summary>
         /// Returns the referenced resource. If the resource hasn't been loaded it will be loaded as if calling
-        /// <see cref="Resources.Load{T}(string,bool)"/> using default settings.
+        /// <see cref="Resources.Load{T}(string,ResourceLoadFlag)"/> using default settings.
         /// </summary>
         public T Value => (T) GenericValue;
     }

+ 83 - 27
Source/Scripting/MBansheeEngine/Resources/Resources.cs

@@ -15,44 +15,75 @@ namespace BansheeEngine
     public static class Resources
     {
         /// <summary>
-        /// Loads a resource at the specified path. If running outside of the editor you must make sure to mark that 
-        /// the resource gets included in the build. If running inside the editor this has similar functionality as
-        /// if loading using the project library. If resource is already loaded an existing instance is returned.
-        /// 
-        /// All resources are automatically unloaded when they go out of scope unless <see cref="keepLoaded"/> parameter
-        /// is specified. 
+        /// Loads a resource at the specified path. If resource is already loaded an existing instance is returned.
         /// </summary>
+        /// <remarks>
+        /// If running outside of the editor you must make sure to mark that the resource gets included in the build. If
+        /// running inside the editor this has similar functionality as if loading using the project library.
+        /// </remarks>
         /// <typeparam name="T">Type of the resource.</typeparam>
         /// <param name="path">Path of the resource, relative to game directory. If running from editor this will be
-        ///                    the same location as resource location in the project library. If a sub-resource within
-        ///                    a file is needed, append the name of the subresource to the path (for example 
-        ///                    mymesh.fbx/my_animation).</param>
-        /// <param name="keepLoaded">If true the system will keep the resource loaded even when it goes out of scope.
-        ///                          You must call <see cref="Release(Resource)"/> in order to allow the resource to be
-        ///                          unloaded (it must be called once for each corresponding load). </param>
+        ///                    relative to the project library. If a sub-resource within a file is needed, append the name
+        ///                    of the subresource to the path (for example mymesh.fbx/my_animation).</param>
+        /// <param name="flags">Flags used to control the load process.</param>
         /// <returns>Loaded resource, or null if resource cannot be found.</returns>
-        public static T Load<T>(string path, bool keepLoaded = true) where T : Resource
+        public static T Load<T>(string path, ResourceLoadFlag flags = ResourceLoadFlag.Default) where T : Resource
         {
-            return (T)Internal_Load(path, keepLoaded);
+            return (T)Internal_Load(path, flags);
         }
 
         /// <summary>
-        /// Loads a resource with the specified UUID. If running outside of the editor you must make sure to mark that 
-        /// the resource gets included in the build. If running inside the editor this has similar functionality as
-        /// if loading using the project library. If resource is already loaded an existing instance is returned.
-        /// 
-        /// All resources are automatically unloaded when they go out of scope unless <see cref="keepLoaded"/> parameter
-        /// is specified. 
+        /// Loads a resource with the specified UUID. If resource is already loaded an existing instance is returned.
         /// </summary>
+        /// <remarks>
+        /// If running outside of the editor you must make sure to mark that the resource gets included in the build. If
+        /// running inside the editor this has similar functionality as if loading using the project library.
+        /// </remarks>
         /// <typeparam name="T">Type of the resource.</typeparam>
         /// <param name="uuid">Unique identifier of the resource to load.</param>
-        /// <param name="keepLoaded">If true the system will keep the resource loaded even when it goes out of scope.
-        ///                          You must call <see cref="Release(Resource)"/> in order to allow the resource to be
-        ///                          unloaded (it must be called once for each corresponding load). </param>
+        /// <param name="flags">Flags used to control the load process.</param>
         /// <returns>Loaded resource, or null if resource cannot be found.</returns>
-        public static T Load<T>(UUID uuid, bool keepLoaded = true) where T : Resource
+        public static T Load<T>(UUID uuid, ResourceLoadFlag flags = ResourceLoadFlag.Default) where T : Resource
         {
-            return (T)Internal_LoadFromUUID(ref uuid, keepLoaded);
+            return (T)Internal_LoadFromUUID(ref uuid, flags);
+        }
+
+        /// <summary>
+        /// Loads a resource at the specified path asynchonously (on a separate thread). If resource is already loaded
+        /// an existing instance is returned. Use <see cref="RRefBase.IsLoaded"/> to confirm the resource has been loaded
+        /// before using it. Use <see cref="GetLoadProgress"/> to track the loading progress of the resource.
+        /// </summary>
+        /// <remarks>
+        /// If running outside of the editor you must make sure to mark that the resource gets included in the build. If
+        /// running inside the editor this has similar functionality as if loading using the project library.
+        /// </remarks>
+        /// <typeparam name="T">Type of the resource.</typeparam>
+        /// <param name="path">Path of the resource, relative to game directory. If running from editor this will be
+        ///                    relative to the project library. If a sub-resource within a file is needed, append the name
+        ///                    of the subresource to the path (for example mymesh.fbx/my_animation).</param>
+        /// <param name="flags">Flags used to control the load process.</param>
+        /// <returns>Loaded resource, or null if resource cannot be found.</returns>
+        public static RRef<T> LoadAsync<T>(string path, ResourceLoadFlag flags = ResourceLoadFlag.Default) where T : Resource
+        {
+            return Internal_LoadAsync(path, flags).As<T>();
+        }
+
+        /// <summary>
+        /// Loads a resource with the specified UUID asynchonously (on a separate thread). If resource is already loaded
+        /// an existing instance is returned. Use <see cref="RRefBase.IsLoaded"/> to confirm the resource has been loaded
+        /// before using it. Use <see cref="GetLoadProgress"/> to track the loading progress of the resource.
+        /// </summary>
+        /// <remarks>
+        /// If running outside of the editor you must make sure to mark that the resource gets included in the build. If
+        /// running inside the editor this has similar functionality as if loading using the project library.
+        /// </remarks>
+        /// <typeparam name="T">Type of the resource.</typeparam>
+        /// <param name="uuid">Unique identifier of the resource to load.</param>
+        /// <param name="flags">Flags used to control the load process.</param>
+        /// <returns>Loaded resource, or null if resource cannot be found.</returns>
+        public static RRef<T> LoadAsync<T>(UUID uuid, ResourceLoadFlag flags = ResourceLoadFlag.Default) where T : Resource
+        {
+            return Internal_LoadAsyncFromUUID(ref uuid, flags).As<T>();
         }
 
         /// <summary>
@@ -98,11 +129,36 @@ namespace BansheeEngine
             Internal_UnloadUnused();
         }
 
+        /// <summary>
+        /// Returns the loading progress of a resource that's being asynchronously loaded. 
+        /// </summary>
+        /// <param name="resource">Resource whose load progress to check.</param>
+        /// <param name="includeDependencies">If false the progress will reflect the load progress only for this individual
+        ///                                   resource. If true the progress will reflect load progress of this resource
+        ///                                   and all of its dependencies.</param>
+        /// <returns>Load progress in range [0, 1].</returns>
+        public static float GetLoadProgress(RRefBase resource, bool includeDependencies = true)
+        {
+            if(resource == null)
+                return 0.0f;
+
+            return Internal_GetLoadProgress(resource.GetCachedPtr(), includeDependencies);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Resource Internal_Load(string path, ResourceLoadFlag flags);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Resource Internal_LoadFromUUID(ref UUID uuid, ResourceLoadFlag flags);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern RRefBase Internal_LoadAsync(string path, ResourceLoadFlag flags);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern Resource Internal_Load(string path, bool keepLoaded);
+        private static extern RRefBase Internal_LoadAsyncFromUUID(ref UUID uuid, ResourceLoadFlag flags);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern Resource Internal_LoadFromUUID(ref UUID uuid, bool keepLoaded);
+        private static extern float Internal_GetLoadProgress(IntPtr resource, bool loadDependencies);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Release(IntPtr resource);

+ 40 - 3
Source/Scripting/MBansheeEngine/Scene/Scene.cs

@@ -1,5 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+
+using System;
 using System.Runtime.CompilerServices;
 
 namespace BansheeEngine
@@ -32,6 +34,7 @@ namespace BansheeEngine
         private static string activeSceneName = "Unnamed";
         private static UUID activeSceneUUID;
         private static bool isGenericPrefab = false;
+        private static RRef<Prefab> activateOnLoadScene;
 
         /// <summary>
         /// Returns the root scene object for the current scene.
@@ -64,36 +67,70 @@ namespace BansheeEngine
         public static void Clear()
         {
             Internal_ClearScene();
+
             activeSceneUUID = UUID.Empty;
             activeSceneName = "Unnamed";
+            activateOnLoadScene = null;
         }
 
         /// <summary>
         /// Loads a new scene.
         /// </summary>
         /// <param name="path">Path to the prefab to load.</param>
-        public static void Load(string path)
+        /// <returns>Prefab of the scene at the provided path.</returns>
+        public static Prefab Load(string path)
         {
             Clear();
-            Prefab scene = Internal_LoadScene(path);
 
+            Prefab scene = Resources.Load<Prefab>(path);
             SetActive(scene);
+
+            return scene;
         }
 
+        /// <summary>
+        /// Loads a new scene asynchronously.
+        /// </summary>
+        /// <param name="path">Path to the prefab to load.</param>
+        /// <returns>Handle to the prefab of the scene at the provided path.</returns>
+        public static RRef<Prefab> LoadAsync(string path)
+        {
+            Clear();
+
+            activateOnLoadScene = Resources.LoadAsync<Prefab>(path); 
+
+            if(activateOnLoadScene != null && activateOnLoadScene.IsLoaded)
+                SetActive(activateOnLoadScene.Value);
+
+            return activateOnLoadScene;
+        }
         /// <summary>
         /// Sets the currently active scene to the provided scene.
         /// </summary>
         /// <param name="scene">Scene which to set as active.</param>
         internal static void SetActive(Prefab scene)
         {
+            activateOnLoadScene = null;
+
             if (scene != null)
             {
                 activeSceneUUID = scene.UUID;
                 activeSceneName = scene.Name;
                 isGenericPrefab = !scene.IsScene;
+
+                Internal_SetActiveScene(scene.GetCachedPtr());
             }
         }
 
+        /// <summary>
+        /// Called once per frame by the runtime.
+        /// </summary>
+        private static void OnUpdate()
+        {
+            if (activateOnLoadScene != null && activateOnLoadScene.IsLoaded)
+                SetActive(activateOnLoadScene.Value);
+        }
+
         /// <summary>
         /// Wrapper around scene name static field because Mono has problems accessing static fields directly.
         /// </summary>
@@ -150,7 +187,7 @@ namespace BansheeEngine
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern Prefab Internal_LoadScene(string path);
+        private static extern void Internal_SetActiveScene(IntPtr prefab);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern SceneObject Internal_GetRoot();

+ 2 - 6
Source/Scripting/SBansheeEditor/BsEditorResourceLoader.cpp

@@ -8,7 +8,7 @@
 
 namespace bs
 {
-	HResource EditorResourceLoader::load(const Path& path, bool keepLoaded) const
+	HResource EditorResourceLoader::load(const Path& path, ResourceLoadFlags flags, bool async) const
 	{
 		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 
@@ -36,10 +36,6 @@ namespace bs
 					isn't flagged to be included in the build. It may not be available outside of the editor.");
 		}
 
-		ResourceLoadFlags loadFlags = ResourceLoadFlag::LoadDependencies | ResourceLoadFlag::KeepSourceData;
-		if (keepLoaded)
-			loadFlags |= ResourceLoadFlag::KeepInternalRef;
-
-		return gResources().loadFromUUID(resUUID, false, loadFlags);
+		return gResources().loadFromUUID(resUUID, async, flags);
 	}
 }

+ 1 - 1
Source/Scripting/SBansheeEditor/BsEditorResourceLoader.h

@@ -16,7 +16,7 @@ namespace bs
 	{
 	public:
 		/** @copydoc IGameResourceLoader::load */
-		HResource load(const Path& path, bool keepLoaded) const override;
+		HResource load(const Path& path, ResourceLoadFlags flags, bool async) const override;
 	};
 
 	/** @} */

+ 3 - 1
Source/Scripting/SBansheeEngine/BsScriptEnginePlugin.cpp

@@ -6,6 +6,7 @@
 #include "Script/BsScriptManager.h"
 #include "Wrappers/GUI/BsScriptGUI.h"
 #include "BsPlayInEditorManager.h"
+#include "Wrappers/BsScriptScene.h"
 
 namespace bs
 {
@@ -25,8 +26,9 @@ namespace bs
 
 	extern "C" BS_SCR_BE_EXPORT void updatePlugin()
 	{
+		ScriptScene::update();
 		PlayInEditorManager::instance().update();
 		ScriptObjectManager::instance().update();
 		ScriptGUI::update();
 	}
-}
+}

+ 45 - 7
Source/Scripting/SBansheeEngine/Wrappers/BsScriptResources.cpp

@@ -5,7 +5,6 @@
 #include "BsMonoClass.h"
 #include "BsMonoMethod.h"
 #include "BsMonoUtil.h"
-#include "Resources/BsResources.h"
 #include "Resources/BsGameResourceManager.h"
 #include "BsScriptResourceManager.h"
 #include "Wrappers/BsScriptResource.h"
@@ -21,16 +20,19 @@ namespace bs
 	{
 		metaData.scriptClass->addInternalCall("Internal_Load", (void*)&ScriptResources::internal_Load);
 		metaData.scriptClass->addInternalCall("Internal_LoadFromUUID", (void*)&ScriptResources::internal_LoadFromUUID);
+		metaData.scriptClass->addInternalCall("Internal_LoadAsync", (void*)&ScriptResources::internal_LoadAsync);
+		metaData.scriptClass->addInternalCall("Internal_LoadAsyncFromUUID", (void*)&ScriptResources::internal_LoadAsyncFromUUID);
 		metaData.scriptClass->addInternalCall("Internal_UnloadUnused", (void*)&ScriptResources::internal_UnloadUnused);
 		metaData.scriptClass->addInternalCall("Internal_Release", (void*)&ScriptResources::internal_Release);
 		metaData.scriptClass->addInternalCall("Internal_ReleaseRef", (void*)&ScriptResources::internal_ReleaseRef);
+		metaData.scriptClass->addInternalCall("Internal_GetLoadProgress", (void*)&ScriptResources::internal_GetLoadProgress);
 	}
 
-	MonoObject* ScriptResources::internal_Load(MonoString* path, bool keepLoaded)
+	MonoObject* ScriptResources::internal_Load(MonoString* path, ResourceLoadFlag flags)
 	{
 		Path nativePath = MonoUtil::monoToString(path);
 
-		HResource resource = GameResourceManager::instance().load(nativePath, keepLoaded);
+		HResource resource = GameResourceManager::instance().load(nativePath, flags, false);
 		if (resource == nullptr)
 			return nullptr;
 
@@ -38,11 +40,9 @@ namespace bs
 		return scriptResource->getManagedInstance();
 	}
 
-	MonoObject* ScriptResources::internal_LoadFromUUID(UUID* uuid, bool keepLoaded)
+	MonoObject* ScriptResources::internal_LoadFromUUID(UUID* uuid, ResourceLoadFlag flags)
 	{
-		ResourceLoadFlags loadFlags = ResourceLoadFlag::LoadDependencies;
-		if (keepLoaded)
-			loadFlags |= ResourceLoadFlag::KeepInternalRef;
+		ResourceLoadFlags loadFlags = flags;
 
 		if (gApplication().isEditor())
 			loadFlags |= ResourceLoadFlag::KeepSourceData;
@@ -55,6 +55,44 @@ namespace bs
 		return scriptResource->getManagedInstance();
 	}
 
+	MonoObject* ScriptResources::internal_LoadAsync(MonoString* path, ResourceLoadFlag flags)
+	{
+		Path nativePath = MonoUtil::monoToString(path);
+
+		HResource resource = GameResourceManager::instance().load(nativePath, flags, true);
+		if (resource == nullptr)
+			return nullptr;
+
+		ScriptRRefBase* scriptResource = ScriptResourceManager::instance().getScriptRRef(resource);
+		if(scriptResource != nullptr)
+			return scriptResource->getManagedInstance();
+
+		return nullptr;
+	}
+
+	MonoObject* ScriptResources::internal_LoadAsyncFromUUID(UUID* uuid, ResourceLoadFlag flags)
+	{
+		ResourceLoadFlags loadFlags = flags;
+
+		if (gApplication().isEditor())
+			loadFlags |= ResourceLoadFlag::KeepSourceData;
+
+		HResource resource = gResources().loadFromUUID(*uuid, loadFlags);
+		if (resource == nullptr)
+			return nullptr;
+
+		ScriptRRefBase* scriptResource = ScriptResourceManager::instance().getScriptRRef(resource);
+		if(scriptResource != nullptr)
+			return scriptResource->getManagedInstance();
+
+		return nullptr;
+	}
+
+	float ScriptResources::internal_GetLoadProgress(ScriptRRefBase* resource, bool loadDependencies)
+	{
+		return gResources().getLoadProgress(resource->getHandle(), loadDependencies);
+	}
+
 	void ScriptResources::internal_Release(ScriptResourceBase* resource)
 	{
 		resource->getGenericHandle().release();

+ 6 - 2
Source/Scripting/SBansheeEngine/Wrappers/BsScriptResources.h

@@ -4,6 +4,7 @@
 
 #include "BsScriptEnginePrerequisites.h"
 #include "BsScriptObject.h"
+#include "Resources/BsResources.h"
 
 namespace bs
 {
@@ -25,11 +26,14 @@ namespace bs
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static MonoObject* internal_Load(MonoString* path, bool keepLoaded);
-		static MonoObject* internal_LoadFromUUID(UUID* uuid, bool keepLoaded);
+		static MonoObject* internal_Load(MonoString* path, ResourceLoadFlag flags);
+		static MonoObject* internal_LoadFromUUID(UUID* uuid, ResourceLoadFlag flags);
+		static MonoObject* internal_LoadAsync(MonoString* path, ResourceLoadFlag flags);
+		static MonoObject* internal_LoadAsyncFromUUID(UUID* uuid, ResourceLoadFlag flags);
 		static void internal_Release(ScriptResourceBase* resource);
 		static void internal_ReleaseRef(ScriptRRefBase* resource);
 		static void internal_UnloadUnused();
+		static float internal_GetLoadProgress(ScriptRRefBase* resource, bool loadDependencies);
 	};
 
 	/** @} */

+ 27 - 18
Source/Scripting/SBansheeEngine/Wrappers/BsScriptScene.cpp

@@ -23,9 +23,11 @@ namespace bs
 	HEvent ScriptScene::OnRefreshDomainLoadedConn;
 	HEvent ScriptScene::OnRefreshStartedConn;
 
-	UUID ScriptScene::ActiveSceneUUID;
-	String ScriptScene::ActiveSceneName;
-	bool ScriptScene::IsGenericPrefab;
+	UUID ScriptScene::sActiveSceneUUID;
+	String ScriptScene::sActiveSceneName;
+	bool ScriptScene::sIsGenericPrefab;
+
+	ScriptScene::OnUpdateThunkDef ScriptScene::onUpdateThunk;
 
 	ScriptScene::ScriptScene(MonoObject* instance)
 		:ScriptObject(instance)
@@ -33,10 +35,13 @@ namespace bs
 
 	void ScriptScene::initRuntimeData()
 	{
-		metaData.scriptClass->addInternalCall("Internal_LoadScene", (void*)&ScriptScene::internal_LoadScene);
+		metaData.scriptClass->addInternalCall("Internal_SetActiveScene", (void*)&ScriptScene::internal_SetActiveScene);
 		metaData.scriptClass->addInternalCall("Internal_GetRoot", (void*)&ScriptScene::internal_GetRoot);
 		metaData.scriptClass->addInternalCall("Internal_ClearScene", (void*)&ScriptScene::internal_ClearScene);
 		metaData.scriptClass->addInternalCall("Internal_GetMainCameraSO", (void*)&ScriptScene::internal_GetMainCameraSO);
+
+		MonoMethod* updateMethod = metaData.scriptClass->getMethod("OnUpdate");
+		onUpdateThunk = (OnUpdateThunkDef)updateMethod->getThunk();
 	}
 
 	void ScriptScene::startUp()
@@ -51,11 +56,13 @@ namespace bs
 		OnRefreshDomainLoadedConn.disconnect();
 	}
 
-	MonoObject* ScriptScene::internal_LoadScene(MonoString* path)
+	void ScriptScene::update()
 	{
-		Path nativePath = MonoUtil::monoToString(path);
+		MonoUtil::invokeThunk(onUpdateThunk);
+	}
 
-		HPrefab prefab = GameResourceManager::instance().load<Prefab>(nativePath, true);
+	void ScriptScene::setActiveScene(const HPrefab& prefab)
+	{
 		if (prefab.isLoaded(false))
 		{
 			// If scene replace current root node, otherwise just append to the current root node
@@ -69,30 +76,32 @@ namespace bs
 				gSceneManager().clearScene();
 				prefab->instantiate();
 			}
-
-			ScriptResourceBase* scriptPrefab = ScriptResourceManager::instance().getScriptResource(prefab, true);
-			return scriptPrefab->getManagedInstance();
 		}
 		else
 		{
-			LOGERR("Failed loading scene at path: \"" + nativePath.toString() + "\"");
-			return nullptr;
+			LOGERR("Attempting to activate a scene that hasn't finished loading yet.");
 		}
 	}
 
+	void ScriptScene::internal_SetActiveScene(ScriptPrefab* scriptPrefab)
+	{
+		HPrefab prefab = scriptPrefab->getHandle();
+		setActiveScene(prefab);
+	}
+
 	void ScriptScene::onRefreshStarted()
 	{
 		MonoMethod* uuidMethod = metaData.scriptClass->getMethod("GetSceneUUID");
 		if (uuidMethod != nullptr)
-			ActiveSceneUUID = ScriptUUID::unbox(uuidMethod->invoke(nullptr, nullptr));
+			sActiveSceneUUID = ScriptUUID::unbox(uuidMethod->invoke(nullptr, nullptr));
 
 		MonoMethod* nameMethod = metaData.scriptClass->getMethod("GetSceneName");
 		if (nameMethod != nullptr)
-			ActiveSceneName = MonoUtil::monoToString((MonoString*)nameMethod->invoke(nullptr, nullptr));
+			sActiveSceneName = MonoUtil::monoToString((MonoString*)nameMethod->invoke(nullptr, nullptr));
 
 		MonoMethod* genericPrefabMethod = metaData.scriptClass->getMethod("GetIsGenericPrefab");
 		if (genericPrefabMethod != nullptr)
-			IsGenericPrefab = *(bool*)MonoUtil::unbox(genericPrefabMethod->invoke(nullptr, nullptr));
+			sIsGenericPrefab = *(bool*)MonoUtil::unbox(genericPrefabMethod->invoke(nullptr, nullptr));
 	}
 
 	void ScriptScene::onRefreshDomainLoaded()
@@ -101,7 +110,7 @@ namespace bs
 		if (uuidMethod != nullptr)
 		{
 			void* params[1];
-			params[0] = ScriptUUID::box(ActiveSceneUUID);
+			params[0] = ScriptUUID::box(sActiveSceneUUID);
 
 			uuidMethod->invoke(nullptr, params);
 		}
@@ -110,7 +119,7 @@ namespace bs
 		if (nameMethod != nullptr)
 		{
 			void* params[1];
-			params[0] = MonoUtil::stringToMono(ActiveSceneName);
+			params[0] = MonoUtil::stringToMono(sActiveSceneName);
 
 			nameMethod->invoke(nullptr, params);
 		}
@@ -118,7 +127,7 @@ namespace bs
 		MonoMethod* genericPrefabMethod = metaData.scriptClass->getMethod("SetIsGenericPrefab", 1);
 		if (genericPrefabMethod != nullptr)
 		{
-			void* params[1] = { &IsGenericPrefab };
+			void* params[1] = { &sIsGenericPrefab };
 			genericPrefabMethod->invoke(nullptr, params);
 		}
 	}

+ 13 - 4
Source/Scripting/SBansheeEngine/Wrappers/BsScriptScene.h

@@ -23,6 +23,9 @@ namespace bs
 		/** Unregisters internal callbacks. Must be called on scripting system shutdown. */
 		static void shutDown();
 
+		/** Handles per-frame operations. Needs to be called every frame. */
+		static void update();
+
 	private:
 		ScriptScene(MonoObject* instance);
 
@@ -32,20 +35,26 @@ namespace bs
 		/** Triggered when assembly domain is loaded during assembly refresh. */
 		static void onRefreshDomainLoaded();
 
+		/** Makes the provided prefab the currently active scene. */
+		static void setActiveScene(const HPrefab& prefab);
+
 		static HEvent OnRefreshDomainLoadedConn;
 		static HEvent OnRefreshStartedConn;
 
-		static UUID ActiveSceneUUID;
-		static String ActiveSceneName;
-		static bool IsGenericPrefab;
+		static UUID sActiveSceneUUID;
+		static String sActiveSceneName;
+		static bool sIsGenericPrefab;
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static MonoObject* internal_LoadScene(MonoString* path);
+		static void internal_SetActiveScene(ScriptPrefab* scriptPrefab);
 		static MonoObject* internal_GetRoot();
 		static void internal_ClearScene();
 		static MonoObject* internal_GetMainCameraSO();
+
+		typedef void(BS_THUNKCALL *OnUpdateThunkDef)(MonoException**);
+		static OnUpdateThunkDef onUpdateThunk;
 	};
 
 	/** @} */

+ 1 - 1
Source/bsf

@@ -1 +1 @@
-Subproject commit 2517d8cf77ba50608f8068d9e8ae815258b19885
+Subproject commit 6781f47e946ccae69f0ff6635f050fc4815c780f