Browse Source

Various fixes related to GUI array:
- Trigger en event when array element changes
- More compact create method
- Dont trigger null events
- When attempting to clone a non-cloneable reference type set the value to null instead of creating a new instance in order to avoid creating instances of potentially complex types like resources
RenderableInspector fixes:
- Properly assign new materials when material array changes
- Properly refresh GUI when material element changes
ScriptGUIResourceField will now use proper string parameter when handling onChanged event
GUIResourceField fixes:
- It will no longer try to load resources when empty
- Resource name is properly retrieved
Added missing hover/active styles for GUI drop button
C# Renderable can now store unlimited number of materials and the duty to ignore the extra ones falls onto the native Renderable

BearishSun 10 năm trước cách đây
mục cha
commit
f70ad040e9

+ 1 - 1
BansheeCore/Include/BsResources.h

@@ -233,7 +233,7 @@ namespace BansheeEngine
 	};
 	};
 
 
 	/**
 	/**
-	 * @brief	Provides easy access to the resource manager.
+	 * @brief	Provides global access to the resource manager.
 	 */
 	 */
 	BS_CORE_EXPORT Resources& gResources();
 	BS_CORE_EXPORT Resources& gResources();
 }
 }

+ 5 - 0
BansheeEditor/Include/BsProjectLibrary.h

@@ -399,4 +399,9 @@ namespace BansheeEngine
 		UnorderedSet<Path> mReimportQueue;
 		UnorderedSet<Path> mReimportQueue;
 		UnorderedMap<String, Path> mUUIDToPath;
 		UnorderedMap<String, Path> mUUIDToPath;
 	};
 	};
+
+	/**
+	 * @brief	Provides global access to the project library.
+	 */
+	BS_ED_EXPORT ProjectLibrary& gProjectLibrary();
 }
 }

+ 7 - 3
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -390,7 +390,7 @@ namespace BansheeEngine
 		fontPath.append(BuiltinDataFolder);
 		fontPath.append(BuiltinDataFolder);
 		fontPath.append(DefaultFontFilename + L".asset");
 		fontPath.append(DefaultFontFilename + L".asset");
 
 
-		HFont font = Resources::instance().load<Font>(fontPath);
+		HFont font = gResources().load<Font>(fontPath);
 
 
 		// Blank entry
 		// Blank entry
 		GUIElementStyle blankStyle;
 		GUIElementStyle blankStyle;
@@ -1196,6 +1196,10 @@ namespace BansheeEngine
 		GUIElementStyle objectDropStyle;
 		GUIElementStyle objectDropStyle;
 		objectDropStyle.normal.texture = getGUITexture(ObjectDropBtnNormalTex);
 		objectDropStyle.normal.texture = getGUITexture(ObjectDropBtnNormalTex);
 		objectDropStyle.normalOn.texture = getGUITexture(ObjectDropBtnNormalOnTex);
 		objectDropStyle.normalOn.texture = getGUITexture(ObjectDropBtnNormalOnTex);
+		objectDropStyle.hover.texture = objectDropStyle.normal.texture;
+		objectDropStyle.hoverOn.texture = objectDropStyle.normalOn.texture;
+		objectDropStyle.active.texture = objectDropStyle.normal.texture;
+		objectDropStyle.activeOn.texture = objectDropStyle.normalOn.texture;
 		objectDropStyle.normal.textColor = TextNormalColor;
 		objectDropStyle.normal.textColor = TextNormalColor;
 		objectDropStyle.hover.textColor = TextNormalColor;
 		objectDropStyle.hover.textColor = TextNormalColor;
 		objectDropStyle.active.textColor = TextNormalColor;
 		objectDropStyle.active.textColor = TextNormalColor;
@@ -1740,7 +1744,7 @@ namespace BansheeEngine
 		texturePath.append(EditorSkinFolder);
 		texturePath.append(EditorSkinFolder);
 		texturePath.append(L"sprite_" + name + L".asset");
 		texturePath.append(L"sprite_" + name + L".asset");
 
 
-		return Resources::instance().load<SpriteTexture>(texturePath);
+		return gResources().load<SpriteTexture>(texturePath);
 	}
 	}
 
 
 	HSpriteTexture BuiltinEditorResources::getGUIIcon(const WString& name)
 	HSpriteTexture BuiltinEditorResources::getGUIIcon(const WString& name)
@@ -1749,7 +1753,7 @@ namespace BansheeEngine
 		texturePath.append(EditorIconFolder);
 		texturePath.append(EditorIconFolder);
 		texturePath.append(L"sprite_" + name + L".asset");
 		texturePath.append(L"sprite_" + name + L".asset");
 
 
-		return Resources::instance().load<SpriteTexture>(texturePath);
+		return gResources().load<SpriteTexture>(texturePath);
 	}
 	}
 
 
 	HShader BuiltinEditorResources::getShader(const WString& name)
 	HShader BuiltinEditorResources::getShader(const WString& name)

+ 1 - 1
BansheeEditor/Source/BsCodeEditor.cpp

@@ -80,7 +80,7 @@ namespace BansheeEngine
 			TID_ScriptCode, TID_PlainText, TID_Shader, TID_ShaderInclude
 			TID_ScriptCode, TID_PlainText, TID_Shader, TID_ShaderInclude
 		};
 		};
 
 
-		Vector<ProjectLibrary::LibraryEntry*> libraryEntries = ProjectLibrary::instance().search(L"*", scriptTypeIds);
+		Vector<ProjectLibrary::LibraryEntry*> libraryEntries = gProjectLibrary().search(L"*", scriptTypeIds);
 		
 		
 		PlatformType activePlatform = BuildManager::instance().getActivePlatform();
 		PlatformType activePlatform = BuildManager::instance().getActivePlatform();
 		Vector<WString> frameworkAssemblies = BuildManager::instance().getFrameworkAssemblies(activePlatform);
 		Vector<WString> frameworkAssemblies = BuildManager::instance().getFrameworkAssemblies(activePlatform);

+ 4 - 4
BansheeEditor/Source/BsEditorApplication.cpp

@@ -249,7 +249,7 @@ namespace BansheeEngine
 	{
 	{
 		Application::postUpdate();
 		Application::postUpdate();
 
 
-		ProjectLibrary::instance().update();
+		gProjectLibrary().update();
 		EditorWindowManager::instance().update();	
 		EditorWindowManager::instance().update();	
 	}
 	}
 
 
@@ -293,7 +293,7 @@ namespace BansheeEngine
 		saveEditorSettings();
 		saveEditorSettings();
 		saveProjectSettings();
 		saveProjectSettings();
 
 
-		ProjectLibrary::instance().saveLibrary();
+		gProjectLibrary().saveLibrary();
 	}
 	}
 
 
 	void EditorApplication::unloadProject()
 	void EditorApplication::unloadProject()
@@ -308,7 +308,7 @@ namespace BansheeEngine
 		UndoRedo::instance().clear();
 		UndoRedo::instance().clear();
 
 
 		EditorWidgetManager::instance().closeAll();
 		EditorWidgetManager::instance().closeAll();
-		ProjectLibrary::instance().unloadLibrary();
+		gProjectLibrary().unloadLibrary();
 		Resources::instance().unloadAllUnused();
 		Resources::instance().unloadAllUnused();
 		gCoreSceneManager().clearScene();
 		gCoreSceneManager().clearScene();
 
 
@@ -334,7 +334,7 @@ namespace BansheeEngine
 
 
 		// Do this before restoring windows and loading library to ensure types are loaded
 		// Do this before restoring windows and loading library to ensure types are loaded
 		ScriptManager::instance().reload();
 		ScriptManager::instance().reload();
-		ProjectLibrary::instance().loadLibrary();
+		gProjectLibrary().loadLibrary();
 
 
 		EditorWidgetLayoutPtr layout = loadWidgetLayout();
 		EditorWidgetLayoutPtr layout = loadWidgetLayout();
 		if (layout != nullptr)
 		if (layout != nullptr)

+ 7 - 7
BansheeEditor/Source/BsGUIResourceTreeView.cpp

@@ -39,10 +39,10 @@ namespace BansheeEngine
 	{
 	{
 		ResourceTreeViewLocator::_provide(this);
 		ResourceTreeViewLocator::_provide(this);
 
 
-		ProjectLibrary::instance().onEntryAdded.connect(std::bind(&GUIResourceTreeView::entryAdded, this, _1));
-		ProjectLibrary::instance().onEntryRemoved.connect(std::bind(&GUIResourceTreeView::entryRemoved, this, _1));
+		gProjectLibrary().onEntryAdded.connect(std::bind(&GUIResourceTreeView::entryAdded, this, _1));
+		gProjectLibrary().onEntryRemoved.connect(std::bind(&GUIResourceTreeView::entryRemoved, this, _1));
 
 
-		const ProjectLibrary::LibraryEntry* rootEntry = ProjectLibrary::instance().getRootEntry();
+		const ProjectLibrary::LibraryEntry* rootEntry = gProjectLibrary().getRootEntry();
 
 
 		mRootElement.mFullPath = rootEntry->path;
 		mRootElement.mFullPath = rootEntry->path;
 		mRootElement.mElementName = mRootElement.mFullPath.getWTail();
 		mRootElement.mElementName = mRootElement.mFullPath.getWTail();
@@ -99,14 +99,14 @@ namespace BansheeEngine
 		Path newPath = oldPath.getParent();
 		Path newPath = oldPath.getParent();
 		newPath.append(name);
 		newPath.append(name);
 
 
-		ProjectLibrary::instance().moveEntry(oldPath, findUniquePath(newPath));
+		gProjectLibrary().moveEntry(oldPath, findUniquePath(newPath));
 	}
 	}
 
 
 	void GUIResourceTreeView::deleteTreeElement(TreeElement* element) 
 	void GUIResourceTreeView::deleteTreeElement(TreeElement* element) 
 	{
 	{
 		ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(element);
 		ResourceTreeElement* resourceTreeElement = static_cast<ResourceTreeElement*>(element);
 
 
-		ProjectLibrary::instance().deleteEntry(resourceTreeElement->mFullPath);
+		gProjectLibrary().deleteEntry(resourceTreeElement->mFullPath);
 	}
 	}
 
 
 	void GUIResourceTreeView::updateFromProjectLibraryEntry(ResourceTreeElement* treeElement, const ProjectLibrary::LibraryEntry* libraryEntry)
 	void GUIResourceTreeView::updateFromProjectLibraryEntry(ResourceTreeElement* treeElement, const ProjectLibrary::LibraryEntry* libraryEntry)
@@ -256,7 +256,7 @@ namespace BansheeEngine
 		ResourceTreeElement* newElement = addTreeElement(parentElement, path);
 		ResourceTreeElement* newElement = addTreeElement(parentElement, path);
 		sortTreeElement(parentElement);
 		sortTreeElement(parentElement);
 
 
-		ProjectLibrary::LibraryEntry* libEntry = ProjectLibrary::instance().findEntry(path);
+		ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(path);
 		
 		
 		assert(libEntry != nullptr);
 		assert(libEntry != nullptr);
 		updateFromProjectLibraryEntry(newElement, libEntry);
 		updateFromProjectLibraryEntry(newElement, libEntry);
@@ -432,7 +432,7 @@ namespace BansheeEngine
 					Path newPath = destDir;
 					Path newPath = destDir;
 					newPath.append(filename);
 					newPath.append(filename);
 
 
-					ProjectLibrary::instance().moveEntry(mDraggedResources->resourcePaths[i], findUniquePath(newPath));
+					gProjectLibrary().moveEntry(mDraggedResources->resourcePaths[i], findUniquePath(newPath));
 				}
 				}
 			}
 			}
 		}
 		}

+ 1 - 1
BansheeEditor/Source/BsGUISceneTreeView.cpp

@@ -323,7 +323,7 @@ namespace BansheeEngine
 
 
 			for (auto& path : draggedResources->resourcePaths)
 			for (auto& path : draggedResources->resourcePaths)
 			{
 			{
-				ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(path);
+				ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 
 
 				if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 				if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 				{
 				{

+ 6 - 1
BansheeEditor/Source/BsProjectLibrary.cpp

@@ -939,7 +939,7 @@ namespace BansheeEngine
 		ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
 		ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
 		String resUUID = resEntry->meta->getUUID();
 		String resUUID = resEntry->meta->getUUID();
 
 
-		return Resources::instance().loadFromUUID(resUUID);
+		return gResources().loadFromUUID(resUUID);
 	}
 	}
 
 
 	void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)
 	void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)
@@ -1280,4 +1280,9 @@ namespace BansheeEngine
 		for (auto& dependency : iterFind->second)
 		for (auto& dependency : iterFind->second)
 			mReimportQueue.insert(dependency);
 			mReimportQueue.insert(dependency);
 	}
 	}
+
+	BS_ED_EXPORT ProjectLibrary& gProjectLibrary()
+	{
+		return ProjectLibrary::instance();
+	}
 }
 }

+ 2 - 2
BansheeEditor/Source/BsSelection.cpp

@@ -57,7 +57,7 @@ namespace BansheeEngine
 		Vector<String> UUIDs;
 		Vector<String> UUIDs;
 		for (auto& path : mSelectedResourcePaths)
 		for (auto& path : mSelectedResourcePaths)
 		{
 		{
-			ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(path);
+			ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 			if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 			if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 			{
 			{
 				ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
 				ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
@@ -73,7 +73,7 @@ namespace BansheeEngine
 		mSelectedResourcePaths.clear();
 		mSelectedResourcePaths.clear();
 		for (auto& uuid : UUIDs)
 		for (auto& uuid : UUIDs)
 		{
 		{
-			Path path = ProjectLibrary::instance().uuidToPath(uuid);
+			Path path = gProjectLibrary().uuidToPath(uuid);
 			if (path != Path::BLANK)
 			if (path != Path::BLANK)
 				mSelectedResourcePaths.push_back(path);
 				mSelectedResourcePaths.push_back(path);
 		}
 		}

+ 1 - 1
BansheeEditor/Source/BsShaderIncludeHandler.cpp

@@ -20,7 +20,7 @@ namespace BansheeEngine
 				return static_resource_cast<ShaderInclude>(Resources::instance().load(path));
 				return static_resource_cast<ShaderInclude>(Resources::instance().load(path));
 		}
 		}
 
 
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(path);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 		{
 		{
 			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
 			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);

+ 4 - 4
BansheeEngine/Source/BsBuiltinResources.cpp

@@ -315,7 +315,7 @@ namespace BansheeEngine
 		fontPath.append(BuiltinDataFolder);
 		fontPath.append(BuiltinDataFolder);
 		fontPath.append(DefaultFontFilename + L".asset");
 		fontPath.append(DefaultFontFilename + L".asset");
 
 
-		HFont font = Resources::instance().load<Font>(fontPath);
+		HFont font = gResources().load<Font>(fontPath);
 		HGUISkin skin = GUISkin::create();
 		HGUISkin skin = GUISkin::create();
 
 
 		// Label
 		// Label
@@ -798,7 +798,7 @@ namespace BansheeEngine
 		texturePath.append(EngineSkinFolder);
 		texturePath.append(EngineSkinFolder);
 		texturePath.append(L"sprite_" + name + L".asset");
 		texturePath.append(L"sprite_" + name + L".asset");
 
 
-		return Resources::instance().load<SpriteTexture>(texturePath);
+		return gResources().load<SpriteTexture>(texturePath);
 	}
 	}
 
 
 	HShader BuiltinResources::getShader(const Path& path)
 	HShader BuiltinResources::getShader(const Path& path)
@@ -816,7 +816,7 @@ namespace BansheeEngine
 		cursorPath.append(EngineCursorFolder);
 		cursorPath.append(EngineCursorFolder);
 		cursorPath.append(name + L".asset");
 		cursorPath.append(name + L".asset");
 
 
-		return Resources::instance().load<Texture>(cursorPath);
+		return gResources().load<Texture>(cursorPath);
 	}
 	}
 
 
 	const PixelData& BuiltinResources::getCursorArrow(Vector2I& hotSpot)
 	const PixelData& BuiltinResources::getCursorArrow(Vector2I& hotSpot)
@@ -903,7 +903,7 @@ namespace BansheeEngine
 			break;
 			break;
 		}
 		}
 
 
-		return Resources::instance().load<Mesh>(meshPath);
+		return gResources().load<Mesh>(meshPath);
 	}
 	}
 
 
 	GUIMaterialInfo BuiltinResources::createSpriteTextMaterial() const
 	GUIMaterialInfo BuiltinResources::createSpriteTextMaterial() const

+ 31 - 26
MBansheeEditor/GUI/GUIArray.cs

@@ -161,7 +161,7 @@ namespace BansheeEditor
         /// <summary>
         /// <summary>
         /// Triggered when the user clicks on the clone button next to the list entry. Clones an element in the list and
         /// Triggered when the user clicks on the clone button next to the list entry. Clones an element in the list and
         /// adds the clone to the back of the list. Non-value types must implement the <see cref="ICloneable"/> interface 
         /// adds the clone to the back of the list. Non-value types must implement the <see cref="ICloneable"/> interface 
-        /// in order to be cloned. If it doesn't, an empty object is created instead of a clone.
+        /// in order to be cloned. If it doesn't the clone will point to a null reference.
         /// </summary>
         /// </summary>
         /// <param name="index">Sequential index of the element in the list to clone.</param>
         /// <param name="index">Sequential index of the element in the list to clone.</param>
         protected internal abstract void OnCloneButtonClicked(int index);
         protected internal abstract void OnCloneButtonClicked(int index);
@@ -193,6 +193,11 @@ namespace BansheeEditor
         /// </summary>
         /// </summary>
         public Action<Array> OnChanged;
         public Action<Array> OnChanged;
 
 
+        /// <summary>
+        /// Triggered when an element in the array has been changed.
+        /// </summary>
+        public Action OnValueChanged;
+
         /// <summary>
         /// <summary>
         /// Array object whose contents are displayed.
         /// Array object whose contents are displayed.
         /// </summary>
         /// </summary>
@@ -207,29 +212,16 @@ namespace BansheeEditor
         /// <summary>
         /// <summary>
         /// Creates a new GUI array.
         /// Creates a new GUI array.
         /// </summary>
         /// </summary>
-        /// <typeparam name="T">Type of rows that are used to handle GUI for individual list elements.</typeparam>
+        /// <typeparam name="RowType">Type of rows that are used to handle GUI for individual list elements.</typeparam>
+        /// <typeparam name="ElementType">Type of elements stored in the array.</typeparam>
         /// <param name="title">Label to display on the list GUI title.</param>
         /// <param name="title">Label to display on the list GUI title.</param>
         /// <param name="array">Object containing the list data. Cannot be null.</param>
         /// <param name="array">Object containing the list data. Cannot be null.</param>
         /// <param name="layout">Layout to which to append the list GUI elements to.</param>
         /// <param name="layout">Layout to which to append the list GUI elements to.</param>
-        public static GUIArray Create<T>(LocString title, Array array, GUILayout layout) where T : GUIListRow, new()
+        public static GUIArray Create<RowType, ElementType>(LocString title, ElementType[] array, GUILayout layout) 
+            where RowType : GUIListRow, new() 
         {
         {
             GUIArray newArray = new GUIArray();
             GUIArray newArray = new GUIArray();
-            newArray.Construct<T>(title, array, array.GetType(), layout);
-
-            return newArray;
-        }
-
-        /// <summary>
-        /// Creates a new GUI array with an initially empty array.
-        /// </summary>
-        /// <typeparam name="T">Type of rows that are used to handle GUI for individual list elements.</typeparam>
-        /// <param name="title">Label to display on the list GUI title.</param>
-        /// <param name="arrayType">Type of the array to create GUI for. Must be of <see cref="System.Array"/> type.</param>
-        /// <param name="layout">Layout to which to append the list GUI elements to.</param>
-        public static GUIArray Create<T>(LocString title, Type arrayType, GUILayout layout) where T : GUIListRow, new()
-        {
-            GUIArray newArray = new GUIArray();
-            newArray.Construct<T>(title, null, arrayType, layout);
+            newArray.Construct<RowType>(title, array, typeof(ElementType[]), layout);
 
 
             return newArray;
             return newArray;
         }
         }
@@ -276,7 +268,8 @@ namespace BansheeEditor
         {
         {
             list = Array.CreateInstance(listType.GetElementType(), 0);
             list = Array.CreateInstance(listType.GetElementType(), 0);
 
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -293,7 +286,8 @@ namespace BansheeEditor
 
 
             list = newArray;
             list = newArray;
 
 
-            OnChanged((Array)list);
+            if(OnChanged != null)
+                OnChanged((Array)list);
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -301,7 +295,8 @@ namespace BansheeEditor
         {
         {
             list = null;
             list = null;
 
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -314,6 +309,9 @@ namespace BansheeEditor
         protected internal override void SetValue(int seqIndex, object value)
         protected internal override void SetValue(int seqIndex, object value)
         {
         {
             list[seqIndex] = value;
             list[seqIndex] = value;
+
+            if (OnValueChanged != null)
+                OnValueChanged();
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -334,7 +332,8 @@ namespace BansheeEditor
 
 
             list = newArray;
             list = newArray;
 
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -347,7 +346,6 @@ namespace BansheeEditor
             for (int i = 0; i < list.Count; i++)
             for (int i = 0; i < list.Count; i++)
             {
             {
                 object value = list[i];
                 object value = list[i];
-
                 newArray.SetValue(value, i);
                 newArray.SetValue(value, i);
 
 
                 if (i == index)
                 if (i == index)
@@ -366,7 +364,7 @@ namespace BansheeEditor
                             if (cloneable != null)
                             if (cloneable != null)
                                 clonedEntry = cloneable.Clone();
                                 clonedEntry = cloneable.Clone();
                             else
                             else
-                                clonedEntry = Activator.CreateInstance(value.GetType());
+                                clonedEntry = null;
                         }
                         }
                     }
                     }
                 }
                 }
@@ -376,7 +374,8 @@ namespace BansheeEditor
 
 
             list = newArray;
             list = newArray;
 
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
@@ -388,6 +387,9 @@ namespace BansheeEditor
 
 
                 list[index - 1] = list[index];
                 list[index - 1] = list[index];
                 list[index] = previousEntry;
                 list[index] = previousEntry;
+
+                if (OnValueChanged != null)
+                    OnValueChanged();
             }
             }
         }
         }
 
 
@@ -400,6 +402,9 @@ namespace BansheeEditor
 
 
                 list[index + 1] = list[index];
                 list[index + 1] = list[index];
                 list[index] = nextEntry;
                 list[index] = nextEntry;
+
+                if (OnValueChanged != null)
+                    OnValueChanged();
             }
             }
         }
         }
     }
     }

+ 4 - 2
MBansheeEditor/Inspectors/FontInspector.cs

@@ -71,7 +71,8 @@ namespace BansheeEditor
         {
         {
             layout.Clear();
             layout.Clear();
 
 
-            fontSizes = GUIArray.Create<FontSizeArrayRow>(new LocEdString("Font sizes"), importOptions.FontSizes, layout);
+            fontSizes = GUIArray.Create<FontSizeArrayRow, int>(
+                new LocEdString("Font sizes"), importOptions.FontSizes, layout);
             fontSizes.OnChanged += x =>
             fontSizes.OnChanged += x =>
             {
             {
                 int[] newFontSizes = x as int[];
                 int[] newFontSizes = x as int[];
@@ -80,7 +81,8 @@ namespace BansheeEditor
                 RebuildGUI();
                 RebuildGUI();
             };
             };
 
 
-            charRanges = GUIArray.Create<CharRangeArrayRow>(new LocEdString("Character ranges"), importOptions.CharRanges, layout);
+            charRanges = GUIArray.Create<CharRangeArrayRow, CharRange>(
+                new LocEdString("Character ranges"), importOptions.CharRanges, layout);
             charRanges.OnChanged += x =>
             charRanges.OnChanged += x =>
             {
             {
                 CharRange[] newRanges = x as CharRange[];
                 CharRange[] newRanges = x as CharRange[];

+ 18 - 6
MBansheeEditor/Inspectors/RenderableInspector.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System.Collections;
+using System.Collections.Generic;
 using BansheeEngine;
 using BansheeEngine;
 
 
 namespace BansheeEditor
 namespace BansheeEditor
@@ -36,8 +37,13 @@ namespace BansheeEditor
             layout.AddElement(layersField);
             layout.AddElement(layersField);
 
 
             materials = renderable.Materials;
             materials = renderable.Materials;
-            materialsField = GUIArray.Create<MaterialArrayRow>(new LocEdString("Materials"), materials, layout);
+            materialsField = GUIArray.Create<MaterialArrayRow, Material>(new LocEdString("Materials"), materials, layout);
 
 
+            materialsField.OnChanged += x =>
+            {
+                renderable.Materials = (Material[]) x;
+                RebuildGUI();
+            };
             meshField.OnChanged += x => renderable.Mesh = x as Mesh;
             meshField.OnChanged += x => renderable.Mesh = x as Mesh;
             layersField.OnSelectionChanged += x =>
             layersField.OnSelectionChanged += x =>
             {
             {
@@ -56,7 +62,10 @@ namespace BansheeEditor
                 for (int i = 0; i < materials.Length; i++)
                 for (int i = 0; i < materials.Length; i++)
                 {
                 {
                     if (materials[i] == null)
                     if (materials[i] == null)
+                    {
+                        materialParams.Add(new MaterialParamGUI[0]);
                         continue;
                         continue;
+                    }
 
 
                     layout.AddSpace(10);
                     layout.AddSpace(10);
 
 
@@ -130,12 +139,15 @@ namespace BansheeEditor
                 anythingModified = true;
                 anythingModified = true;
             }
             }
 
 
-            for(int i = 0; i < materialParams.Count; i++)
+            if (materials != null)
             {
             {
-                if (materialParams[i] != null)
+                for (int i = 0; i < materialParams.Count; i++)
                 {
                 {
-                    foreach (var param in materialParams[i])
-                        anythingModified |= param.Refresh(materials[i]);
+                    if (materialParams[i] != null)
+                    {
+                        foreach (var param in materialParams[i])
+                            anythingModified |= param.Refresh(materials[i]);
+                    }
                 }
                 }
             }
             }
 
 

+ 2 - 23
MBansheeEngine/NativeRenderable.cs

@@ -19,15 +19,6 @@ namespace BansheeEngine
             {
             {
                 mesh = value;
                 mesh = value;
 
 
-                int subMeshCount = 0;
-                if (mesh != null)
-                    subMeshCount = mesh.SubMeshCount;
-
-                Material[] newMaterials = new Material[subMeshCount];
-                int numToCopy = MathEx.Min(newMaterials.Length, materials.Length);
-                Array.Copy(materials, newMaterials, numToCopy);
-                materials = newMaterials;
-
                 IntPtr meshPtr = IntPtr.Zero;
                 IntPtr meshPtr = IntPtr.Zero;
                 if (mesh != null)
                 if (mesh != null)
                     meshPtr = mesh.GetCachedPtr();
                     meshPtr = mesh.GetCachedPtr();
@@ -53,7 +44,7 @@ namespace BansheeEngine
             set { Internal_SetLayers(mCachedPtr, value); }
             set { Internal_SetLayers(mCachedPtr, value); }
         }
         }
 
 
-        private Material[] materials = new Material[0];
+        private Material[] materials = new Material[1];
         private Mesh mesh;
         private Mesh mesh;
         
         
         public NativeRenderable(SceneObject sceneObject)
         public NativeRenderable(SceneObject sceneObject)
@@ -68,19 +59,7 @@ namespace BansheeEngine
         internal Material[] Materials
         internal Material[] Materials
         {
         {
             get { return materials; }
             get { return materials; }
-            set
-            {
-                int newNumMaterials = value != null ? value.Length : 0;
-                int min = MathEx.Min(materials.Length, newNumMaterials);
-
-                for (int i = 0; i < min; i++)
-                    materials[i] = value[i];
-
-                for (int i = min; i < materials.Length; i++)
-                    materials[i] = null;
-
-                Internal_SetMaterials(mCachedPtr, materials);
-            }
+            set { materials = value; Internal_SetMaterials(mCachedPtr, value); }
         }
         }
 
 
         internal Material GetMaterial(int index = 0)
         internal Material GetMaterial(int index = 0)

+ 2 - 2
SBansheeEditor/Include/BsScriptGUIResourceField.h

@@ -18,9 +18,9 @@ namespace BansheeEngine
 		 * @brief	Triggered when the value in the native resource field changes.
 		 * @brief	Triggered when the value in the native resource field changes.
 		 *
 		 *
 		 * @param	instance	Managed GUIResourceField instance.
 		 * @param	instance	Managed GUIResourceField instance.
-		 * @param	newValue	New field value.
+		 * @param	newValue	UUID of the newly selected resource.
 		 */
 		 */
-		static void onChanged(MonoObject* instance, const HResource& newValue);
+		static void onChanged(MonoObject* instance, const String& newUUID);
 
 
 		/**
 		/**
 		 * @brief	Retrieves a managed instance of the specified native resource.
 		 * @brief	Retrieves a managed instance of the specified native resource.

+ 2 - 2
SBansheeEditor/Source/BsEditorResourceLoader.cpp

@@ -8,7 +8,7 @@ namespace BansheeEngine
 {
 {
 	HResource EditorResourceLoader::load(const Path& path) const
 	HResource EditorResourceLoader::load(const Path& path) const
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(path);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 
 
 		if (entry == nullptr || entry->type == ProjectLibrary::LibraryEntryType::Directory)
 		if (entry == nullptr || entry->type == ProjectLibrary::LibraryEntryType::Directory)
 			return HResource();
 			return HResource();
@@ -22,6 +22,6 @@ namespace BansheeEngine
 					isn't flagged to be included in the build. It may not be available outside of the editor.");
 					isn't flagged to be included in the build. It may not be available outside of the editor.");
 		}
 		}
 
 
-		return Resources::instance().loadFromUUID(resUUID);
+		return gResources().loadFromUUID(resUUID);
 	}
 	}
 }
 }

+ 9 - 6
SBansheeEditor/Source/BsGUIResourceField.cpp

@@ -178,7 +178,10 @@ namespace BansheeEngine
 
 
 	HResource GUIResourceField::getValue() const
 	HResource GUIResourceField::getValue() const
 	{
 	{
-		return Resources::instance().loadFromUUID(mUUID);
+		if (!mUUID.empty())
+			return gResources().loadFromUUID(mUUID);
+
+		return HResource();
 	}
 	}
 
 
 	void GUIResourceField::setValue(const HResource& value)
 	void GUIResourceField::setValue(const HResource& value)
@@ -193,10 +196,10 @@ namespace BansheeEngine
 	{ 
 	{ 
 		mUUID = uuid;
 		mUUID = uuid;
 
 
-		Path filePath;
-		if (Resources::instance().getFilePathFromUUID(mUUID, filePath))
+		Path resPath = gProjectLibrary().uuidToPath(mUUID);
+		if (!resPath.isEmpty())
 		{
 		{
-			WString title = filePath.getWFilename(false) + L" (" + toWString(mType) + L")";
+			WString title = resPath.getWFilename(false) + L" (" + toWString(mType) + L")";
 			mDropButton->setContent(GUIContent(HString(title)));
 			mDropButton->setContent(GUIContent(HString(title)));
 		}
 		}
 		else
 		else
@@ -230,7 +233,7 @@ namespace BansheeEngine
 		if (mUUID == "")
 		if (mUUID == "")
 			return;
 			return;
 
 
-		Path resPath = ProjectLibrary::instance().uuidToPath(mUUID);
+		Path resPath = gProjectLibrary().uuidToPath(mUUID);
 		Selection::instance().ping(resPath);
 		Selection::instance().ping(resPath);
 	}
 	}
 
 
@@ -248,7 +251,7 @@ namespace BansheeEngine
 		{
 		{
 			Path path = draggedResources->resourcePaths[i];
 			Path path = draggedResources->resourcePaths[i];
 
 
-			ProjectLibrary::LibraryEntry* libEntry = ProjectLibrary::instance().findEntry(path);
+			ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(path);
 			if (libEntry == nullptr || libEntry->type == ProjectLibrary::LibraryEntryType::Directory)
 			if (libEntry == nullptr || libEntry->type == ProjectLibrary::LibraryEntryType::Directory)
 				continue;
 				continue;
 
 

+ 3 - 3
SBansheeEditor/Source/BsGUITextureField.cpp

@@ -188,7 +188,7 @@ namespace BansheeEngine
 
 
 		Path filePath;
 		Path filePath;
 		if (Resources::instance().getFilePathFromUUID(mUUID, filePath))
 		if (Resources::instance().getFilePathFromUUID(mUUID, filePath))
-			texture = Resources::instance().load<Texture>(filePath);
+			texture = gResources().load<Texture>(filePath);
 		
 		
 		if (texture != nullptr)
 		if (texture != nullptr)
 		{
 		{
@@ -230,7 +230,7 @@ namespace BansheeEngine
 		if (mUUID == "")
 		if (mUUID == "")
 			return;
 			return;
 
 
-		Path resPath = ProjectLibrary::instance().uuidToPath(mUUID);
+		Path resPath = gProjectLibrary().uuidToPath(mUUID);
 		Selection::instance().ping(resPath);
 		Selection::instance().ping(resPath);
 	}
 	}
 
 
@@ -246,7 +246,7 @@ namespace BansheeEngine
 		{
 		{
 			Path path = draggedResources->resourcePaths[i];
 			Path path = draggedResources->resourcePaths[i];
 
 
-			ProjectLibrary::LibraryEntry* libEntry = ProjectLibrary::instance().findEntry(draggedResources->resourcePaths[i]);
+			ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(draggedResources->resourcePaths[i]);
 			if (libEntry == nullptr || libEntry->type == ProjectLibrary::LibraryEntryType::Directory)
 			if (libEntry == nullptr || libEntry->type == ProjectLibrary::LibraryEntryType::Directory)
 				continue;
 				continue;
 
 

+ 4 - 4
SBansheeEditor/Source/BsScriptEditorApplication.cpp

@@ -139,7 +139,7 @@ namespace BansheeEngine
 		Path nativePath = MonoUtil::monoToWString(path);
 		Path nativePath = MonoUtil::monoToWString(path);
 		HSceneObject sceneRoot = gSceneManager().getRootNode();
 		HSceneObject sceneRoot = gSceneManager().getRootNode();
 		
 		
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(nativePath);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(nativePath);
 		HPrefab scene;
 		HPrefab scene;
 		if (entry != nullptr)
 		if (entry != nullptr)
 		{
 		{
@@ -150,17 +150,17 @@ namespace BansheeEngine
 			if (resEntry->meta->getTypeID() != TID_Prefab)
 			if (resEntry->meta->getTypeID() != TID_Prefab)
 				return nullptr;
 				return nullptr;
 
 
-			scene = static_resource_cast<Prefab>(ProjectLibrary::instance().load(nativePath));
+			scene = static_resource_cast<Prefab>(gProjectLibrary().load(nativePath));
 			scene->update(sceneRoot);
 			scene->update(sceneRoot);
 
 
 			PrefabUtility::recordPrefabDiff(sceneRoot);
 			PrefabUtility::recordPrefabDiff(sceneRoot);
-			ProjectLibrary::instance().saveEntry(scene);
+			gProjectLibrary().saveEntry(scene);
 		}
 		}
 		else
 		else
 		{
 		{
 			scene = Prefab::create(sceneRoot);
 			scene = Prefab::create(sceneRoot);
 			PrefabUtility::recordPrefabDiff(sceneRoot);
 			PrefabUtility::recordPrefabDiff(sceneRoot);
-			ProjectLibrary::instance().createEntry(scene, nativePath);
+			gProjectLibrary().createEntry(scene, nativePath);
 		}
 		}
 
 
 		return MonoUtil::stringToMono(MonoManager::instance().getDomain(), scene.getUUID());
 		return MonoUtil::stringToMono(MonoManager::instance().getDomain(), scene.getUUID());

+ 10 - 3
SBansheeEditor/Source/BsScriptGUIResourceField.cpp

@@ -16,6 +16,7 @@
 #include "BsScriptGUIContent.h"
 #include "BsScriptGUIContent.h"
 #include "BsScriptResource.h"
 #include "BsScriptResource.h"
 #include "BsScriptResourceManager.h"
 #include "BsScriptResourceManager.h"
+#include "BsResources.h"
 
 
 using namespace std::placeholders;
 using namespace std::placeholders;
 
 
@@ -100,9 +101,11 @@ namespace BansheeEngine
 		resourceField->setTint(color);
 		resourceField->setTint(color);
 	}
 	}
 
 
-	void ScriptGUIResourceField::onChanged(MonoObject* instance, const HResource& newValue)
+	void ScriptGUIResourceField::onChanged(MonoObject* instance, const String& newUUID)
 	{
 	{
-		MonoObject* managedObj = nativeToManagedResource(newValue);
+		HResource resource = gResources().loadFromUUID(newUUID);
+
+		MonoObject* managedObj = nativeToManagedResource(resource);
 		MonoUtil::invokeThunk(onChangedThunk, instance, managedObj);
 		MonoUtil::invokeThunk(onChangedThunk, instance, managedObj);
 	}
 	}
 
 
@@ -116,7 +119,11 @@ namespace BansheeEngine
 		{
 		{
 			ScriptResourceBase* scriptResource = ScriptResourceManager::instance().getScriptResource(instance.getUUID());
 			ScriptResourceBase* scriptResource = ScriptResourceManager::instance().getScriptResource(instance.getUUID());
 			if (scriptResource == nullptr)
 			if (scriptResource == nullptr)
-				return nullptr;
+			{
+				ScriptResourceManager::instance().createScriptResource(instance, &scriptResource);
+
+				return scriptResource->getManagedInstance();
+			}
 			else
 			else
 				return scriptResource->getManagedInstance();
 				return scriptResource->getManagedInstance();
 		}
 		}

+ 34 - 34
SBansheeEditor/Source/BsScriptProjectLibrary.cpp

@@ -60,10 +60,10 @@ namespace BansheeEngine
 		Path nativePath = MonoUtil::monoToWString(path);
 		Path nativePath = MonoUtil::monoToWString(path);
 
 
 		if (!nativePath.isAbsolute())
 		if (!nativePath.isAbsolute())
-			nativePath.makeAbsolute(ProjectLibrary::instance().getResourcesFolder());
+			nativePath.makeAbsolute(gProjectLibrary().getResourcesFolder());
 
 
 		Vector<Path> dirtyResources;
 		Vector<Path> dirtyResources;
-		ProjectLibrary::instance().checkForModifications(nativePath, import, dirtyResources);
+		gProjectLibrary().checkForModifications(nativePath, import, dirtyResources);
 
 
 		ScriptArray output = ScriptArray::create<WString>((UINT32)dirtyResources.size());
 		ScriptArray output = ScriptArray::create<WString>((UINT32)dirtyResources.size());
 		for (UINT32 i = 0; i < (UINT32)dirtyResources.size(); i++)
 		for (UINT32 i = 0; i < (UINT32)dirtyResources.size(); i++)
@@ -79,7 +79,7 @@ namespace BansheeEngine
 		ScriptResource* scrResource = ScriptResource::toNative(resource);
 		ScriptResource* scrResource = ScriptResource::toNative(resource);
 		Path resourcePath = MonoUtil::monoToWString(path);
 		Path resourcePath = MonoUtil::monoToWString(path);
 
 
-		ProjectLibrary::instance().createEntry(scrResource->getNativeHandle(), resourcePath);
+		gProjectLibrary().createEntry(scrResource->getNativeHandle(), resourcePath);
 	}
 	}
 
 
 	MonoObject* ScriptProjectLibrary::internal_Load(MonoString* path)
 	MonoObject* ScriptProjectLibrary::internal_Load(MonoString* path)
@@ -87,11 +87,11 @@ namespace BansheeEngine
 		Path resourcePath = MonoUtil::monoToWString(path);
 		Path resourcePath = MonoUtil::monoToWString(path);
 
 
 		HResource resource;
 		HResource resource;
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(resourcePath);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(resourcePath);
 		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 		{
 		{
 			ProjectLibrary::ResourceEntry* resEntry = static_cast <ProjectLibrary::ResourceEntry*>(entry);
 			ProjectLibrary::ResourceEntry* resEntry = static_cast <ProjectLibrary::ResourceEntry*>(entry);
-			resource = Resources::instance().loadFromUUID(resEntry->meta->getUUID());
+			resource = gResources().loadFromUUID(resEntry->meta->getUUID());
 		}
 		}
 		
 		
 		if (!resource)
 		if (!resource)
@@ -108,12 +108,12 @@ namespace BansheeEngine
 		ScriptResource* srcResource = ScriptResource::toNative(resource);
 		ScriptResource* srcResource = ScriptResource::toNative(resource);
 
 
 		if (srcResource != nullptr)
 		if (srcResource != nullptr)
-			ProjectLibrary::instance().saveEntry(srcResource->getNativeHandle());
+			gProjectLibrary().saveEntry(srcResource->getNativeHandle());
 	}
 	}
 
 
 	MonoObject* ScriptProjectLibrary::internal_GetRoot()
 	MonoObject* ScriptProjectLibrary::internal_GetRoot()
 	{
 	{
-		return ScriptDirectoryEntry::create(static_cast<const ProjectLibrary::DirectoryEntry*>(ProjectLibrary::instance().getRootEntry()));
+		return ScriptDirectoryEntry::create(static_cast<const ProjectLibrary::DirectoryEntry*>(gProjectLibrary().getRootEntry()));
 	}
 	}
 
 
 	void ScriptProjectLibrary::internal_Reimport(MonoString* path, MonoObject* options, bool force)
 	void ScriptProjectLibrary::internal_Reimport(MonoString* path, MonoObject* options, bool force)
@@ -127,14 +127,14 @@ namespace BansheeEngine
 			nativeOptions = scriptOptions->getImportOptions();
 			nativeOptions = scriptOptions->getImportOptions();
 		}
 		}
 
 
-		ProjectLibrary::instance().reimport(assetPath, nativeOptions, force);
+		gProjectLibrary().reimport(assetPath, nativeOptions, force);
 	}
 	}
 
 
 	MonoObject* ScriptProjectLibrary::internal_GetEntry(MonoString* path)
 	MonoObject* ScriptProjectLibrary::internal_GetEntry(MonoString* path)
 	{
 	{
 		Path assetPath = MonoUtil::monoToWString(path);
 		Path assetPath = MonoUtil::monoToWString(path);
 
 
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(assetPath);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(assetPath);
 		if (entry == nullptr)
 		if (entry == nullptr)
 			return nullptr;
 			return nullptr;
 
 
@@ -147,7 +147,7 @@ namespace BansheeEngine
 	MonoString* ScriptProjectLibrary::internal_GetPathFromUUID(MonoString* uuid)
 	MonoString* ScriptProjectLibrary::internal_GetPathFromUUID(MonoString* uuid)
 	{
 	{
 		String nativeUUID = MonoUtil::monoToString(uuid);
 		String nativeUUID = MonoUtil::monoToString(uuid);
-		Path nativePath = ProjectLibrary::instance().uuidToPath(nativeUUID);
+		Path nativePath = gProjectLibrary().uuidToPath(nativeUUID);
 
 
 		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativePath.toWString());
 		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativePath.toWString());
 	}
 	}
@@ -158,8 +158,8 @@ namespace BansheeEngine
 
 
 		if (srcResource != nullptr)
 		if (srcResource != nullptr)
 		{
 		{
-			Path nativePath = ProjectLibrary::instance().uuidToPath(srcResource->getNativeHandle().getUUID());
-			nativePath.getRelative(ProjectLibrary::instance().getResourcesFolder());
+			Path nativePath = gProjectLibrary().uuidToPath(srcResource->getNativeHandle().getUUID());
+			nativePath.getRelative(gProjectLibrary().getResourcesFolder());
 
 
 			return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativePath.toWString());
 			return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativePath.toWString());
 		}
 		}
@@ -182,7 +182,7 @@ namespace BansheeEngine
 			}
 			}
 		}
 		}
 
 
-		Vector<ProjectLibrary::LibraryEntry*> foundEntries = ProjectLibrary::instance().search(strPattern, typeIds);
+		Vector<ProjectLibrary::LibraryEntry*> foundEntries = gProjectLibrary().search(strPattern, typeIds);
 
 
 		UINT32 idx = 0;
 		UINT32 idx = 0;
 		ScriptArray outArray = ScriptArray::create<ScriptLibraryEntry>((UINT32)foundEntries.size());
 		ScriptArray outArray = ScriptArray::create<ScriptLibraryEntry>((UINT32)foundEntries.size());
@@ -205,13 +205,13 @@ namespace BansheeEngine
 	void ScriptProjectLibrary::internal_Delete(MonoString* path)
 	void ScriptProjectLibrary::internal_Delete(MonoString* path)
 	{
 	{
 		Path pathToDelete = MonoUtil::monoToWString(path);
 		Path pathToDelete = MonoUtil::monoToWString(path);
-		ProjectLibrary::instance().deleteEntry(pathToDelete);
+		gProjectLibrary().deleteEntry(pathToDelete);
 	}
 	}
 
 
 	void ScriptProjectLibrary::internal_CreateFolder(MonoString* path)
 	void ScriptProjectLibrary::internal_CreateFolder(MonoString* path)
 	{
 	{
 		Path folderToCreate = MonoUtil::monoToWString(path);
 		Path folderToCreate = MonoUtil::monoToWString(path);
-		ProjectLibrary::instance().createFolderEntry(folderToCreate);
+		gProjectLibrary().createFolderEntry(folderToCreate);
 	}
 	}
 
 
 	void ScriptProjectLibrary::internal_Rename(MonoString* path, MonoString* name, bool overwrite)
 	void ScriptProjectLibrary::internal_Rename(MonoString* path, MonoString* name, bool overwrite)
@@ -219,7 +219,7 @@ namespace BansheeEngine
 		Path oldPath = MonoUtil::monoToWString(path);
 		Path oldPath = MonoUtil::monoToWString(path);
 		Path newPath = oldPath.getParent().append(MonoUtil::monoToWString(name));
 		Path newPath = oldPath.getParent().append(MonoUtil::monoToWString(name));
 
 
-		ProjectLibrary::instance().moveEntry(oldPath, newPath, overwrite);
+		gProjectLibrary().moveEntry(oldPath, newPath, overwrite);
 	}
 	}
 
 
 	void ScriptProjectLibrary::internal_Move(MonoString* oldPath, MonoString* newPath, bool overwrite)
 	void ScriptProjectLibrary::internal_Move(MonoString* oldPath, MonoString* newPath, bool overwrite)
@@ -227,7 +227,7 @@ namespace BansheeEngine
 		Path oldPathNative = MonoUtil::monoToWString(oldPath);
 		Path oldPathNative = MonoUtil::monoToWString(oldPath);
 		Path newPathNative = MonoUtil::monoToWString(newPath);
 		Path newPathNative = MonoUtil::monoToWString(newPath);
 
 
-		ProjectLibrary::instance().moveEntry(oldPathNative, newPathNative, overwrite);
+		gProjectLibrary().moveEntry(oldPathNative, newPathNative, overwrite);
 	}
 	}
 
 
 	void ScriptProjectLibrary::internal_Copy(MonoString* source, MonoString* destination, bool overwrite)
 	void ScriptProjectLibrary::internal_Copy(MonoString* source, MonoString* destination, bool overwrite)
@@ -235,25 +235,25 @@ namespace BansheeEngine
 		Path oldPathNative = MonoUtil::monoToWString(source);
 		Path oldPathNative = MonoUtil::monoToWString(source);
 		Path newPathNative = MonoUtil::monoToWString(destination);
 		Path newPathNative = MonoUtil::monoToWString(destination);
 
 
-		ProjectLibrary::instance().copyEntry(oldPathNative, newPathNative, overwrite);
+		gProjectLibrary().copyEntry(oldPathNative, newPathNative, overwrite);
 	}
 	}
 
 
 	MonoString* ScriptProjectLibrary::internal_GetResourceFolder()
 	MonoString* ScriptProjectLibrary::internal_GetResourceFolder()
 	{
 	{
-		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), ProjectLibrary::instance().getResourcesFolder().toWString());
+		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), gProjectLibrary().getResourcesFolder().toWString());
 	}
 	}
 
 
 	void ScriptProjectLibrary::internal_SetIncludeInBuild(MonoString* path, bool include)
 	void ScriptProjectLibrary::internal_SetIncludeInBuild(MonoString* path, bool include)
 	{
 	{
 		Path pathNative = MonoUtil::monoToWString(path);
 		Path pathNative = MonoUtil::monoToWString(path);
 
 
-		ProjectLibrary::instance().setIncludeInBuild(pathNative, include);
+		gProjectLibrary().setIncludeInBuild(pathNative, include);
 	}
 	}
 
 
 	void ScriptProjectLibrary::startUp()
 	void ScriptProjectLibrary::startUp()
 	{
 	{
-		mOnEntryAddedConn = ProjectLibrary::instance().onEntryAdded.connect(std::bind(&ScriptProjectLibrary::onEntryAdded, _1));
-		mOnEntryRemovedConn = ProjectLibrary::instance().onEntryRemoved.connect(std::bind(&ScriptProjectLibrary::onEntryRemoved, _1));
+		mOnEntryAddedConn = gProjectLibrary().onEntryAdded.connect(std::bind(&ScriptProjectLibrary::onEntryAdded, _1));
+		mOnEntryRemovedConn = gProjectLibrary().onEntryRemoved.connect(std::bind(&ScriptProjectLibrary::onEntryRemoved, _1));
 	}
 	}
 
 
 	void ScriptProjectLibrary::shutDown()
 	void ScriptProjectLibrary::shutDown()
@@ -266,7 +266,7 @@ namespace BansheeEngine
 	{
 	{
 		Path relativePath = path;
 		Path relativePath = path;
 		if (relativePath.isAbsolute())
 		if (relativePath.isAbsolute())
-			relativePath.makeRelative(ProjectLibrary::instance().getResourcesFolder());
+			relativePath.makeRelative(gProjectLibrary().getResourcesFolder());
 
 
 		MonoString* pathStr = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 		MonoString* pathStr = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 		MonoUtil::invokeThunk(OnEntryAddedThunk, pathStr);
 		MonoUtil::invokeThunk(OnEntryAddedThunk, pathStr);
@@ -276,7 +276,7 @@ namespace BansheeEngine
 	{
 	{
 		Path relativePath = path;
 		Path relativePath = path;
 		if (relativePath.isAbsolute())
 		if (relativePath.isAbsolute())
-			relativePath.makeRelative(ProjectLibrary::instance().getResourcesFolder());
+			relativePath.makeRelative(gProjectLibrary().getResourcesFolder());
 
 
 		MonoString* pathStr = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 		MonoString* pathStr = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 		MonoUtil::invokeThunk(OnEntryRemovedThunk, pathStr);
 		MonoUtil::invokeThunk(OnEntryRemovedThunk, pathStr);
@@ -296,19 +296,19 @@ namespace BansheeEngine
 
 
 	MonoString* ScriptLibraryEntry::internal_GetPath(ScriptLibraryEntryBase* thisPtr)
 	MonoString* ScriptLibraryEntry::internal_GetPath(ScriptLibraryEntryBase* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr)
 		if (entry == nullptr)
 			return nullptr;
 			return nullptr;
 
 
 		Path relativePath = entry->path;
 		Path relativePath = entry->path;
-		relativePath.makeRelative(ProjectLibrary::instance().getResourcesFolder());
+		relativePath.makeRelative(gProjectLibrary().getResourcesFolder());
 
 
 		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 	}
 	}
 
 
 	MonoString* ScriptLibraryEntry::internal_GetName(ScriptLibraryEntryBase* thisPtr)
 	MonoString* ScriptLibraryEntry::internal_GetName(ScriptLibraryEntryBase* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr)
 		if (entry == nullptr)
 			return nullptr;
 			return nullptr;
 
 
@@ -317,7 +317,7 @@ namespace BansheeEngine
 
 
 	ProjectLibrary::LibraryEntryType ScriptLibraryEntry::internal_GetType(ScriptLibraryEntryBase* thisPtr)
 	ProjectLibrary::LibraryEntryType ScriptLibraryEntry::internal_GetType(ScriptLibraryEntryBase* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr)
 		if (entry == nullptr)
 			return ProjectLibrary::LibraryEntryType::File; // Note: We don't actually know what this entry is, because it doesn't exist anymore
 			return ProjectLibrary::LibraryEntryType::File; // Note: We don't actually know what this entry is, because it doesn't exist anymore
 
 
@@ -326,7 +326,7 @@ namespace BansheeEngine
 
 
 	MonoObject* ScriptLibraryEntry::internal_GetParent(ScriptLibraryEntryBase* thisPtr)
 	MonoObject* ScriptLibraryEntry::internal_GetParent(ScriptLibraryEntryBase* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->parent == nullptr)
 		if (entry == nullptr || entry->parent == nullptr)
 			return nullptr;
 			return nullptr;
 
 
@@ -354,7 +354,7 @@ namespace BansheeEngine
 
 
 	MonoArray* ScriptDirectoryEntry::internal_GetChildren(ScriptDirectoryEntry* thisPtr)
 	MonoArray* ScriptDirectoryEntry::internal_GetChildren(ScriptDirectoryEntry* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::Directory)
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::Directory)
 			return ScriptArray::create<ScriptLibraryEntry>(0).getInternal();
 			return ScriptArray::create<ScriptLibraryEntry>(0).getInternal();
 
 
@@ -401,7 +401,7 @@ namespace BansheeEngine
 
 
 	MonoObject* ScriptFileEntry::internal_GetImportOptions(ScriptFileEntry* thisPtr)
 	MonoObject* ScriptFileEntry::internal_GetImportOptions(ScriptFileEntry* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 			return nullptr;
 			return nullptr;
 
 
@@ -415,7 +415,7 @@ namespace BansheeEngine
 
 
 	MonoString* ScriptFileEntry::internal_GetUUID(ScriptFileEntry* thisPtr)
 	MonoString* ScriptFileEntry::internal_GetUUID(ScriptFileEntry* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 			return nullptr;
 			return nullptr;
 
 
@@ -435,7 +435,7 @@ namespace BansheeEngine
 
 
 	ScriptResourceType ScriptFileEntry::internal_GetResourceType(ScriptFileEntry* thisPtr)
 	ScriptResourceType ScriptFileEntry::internal_GetResourceType(ScriptFileEntry* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 			return ScriptResourceType::Undefined;
 			return ScriptResourceType::Undefined;
 
 
@@ -449,7 +449,7 @@ namespace BansheeEngine
 
 
 	bool ScriptFileEntry::internal_GetIncludeInBuild(ScriptFileEntry* thisPtr)
 	bool ScriptFileEntry::internal_GetIncludeInBuild(ScriptFileEntry* thisPtr)
 	{
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 			return false;
 			return false;
 
 

+ 1 - 1
SBansheeEditor/Source/BsToolbarItemManager.cpp

@@ -128,7 +128,7 @@ namespace BansheeEngine
 			mIconField->getValue(toolbarItemAttrib, &monoTexturePath);
 			mIconField->getValue(toolbarItemAttrib, &monoTexturePath);
 
 
 			Path texturePath = MonoUtil::monoToWString(monoTexturePath);
 			Path texturePath = MonoUtil::monoToWString(monoTexturePath);
-			icon = static_resource_cast<SpriteTexture>(ProjectLibrary::instance().load(texturePath));
+			icon = static_resource_cast<SpriteTexture>(gProjectLibrary().load(texturePath));
 		}
 		}
 
 
 		MonoString* tooltipMono;
 		MonoString* tooltipMono;

+ 17 - 10
SBansheeEngine/Source/BsScriptRenderable.cpp

@@ -101,19 +101,26 @@ namespace BansheeEngine
 
 
 	void ScriptRenderable::internal_SetMaterials(ScriptRenderable* thisPtr, MonoArray* materials)
 	void ScriptRenderable::internal_SetMaterials(ScriptRenderable* thisPtr, MonoArray* materials)
 	{
 	{
-		ScriptArray scriptMaterials(materials);
-
-		Vector<HMaterial> nativeMaterials(scriptMaterials.size());
-		for (UINT32 i = 0; i < scriptMaterials.size(); i++)
+		if (materials != nullptr)
 		{
 		{
-			MonoObject* monoMaterial = scriptMaterials.get<MonoObject*>(i);
-			ScriptMaterial* scriptMaterial = ScriptMaterial::toNative(monoMaterial);
+			ScriptArray scriptMaterials(materials);
+
+			Vector<HMaterial> nativeMaterials(scriptMaterials.size());
+			for (UINT32 i = 0; i < scriptMaterials.size(); i++)
+			{
+				MonoObject* monoMaterial = scriptMaterials.get<MonoObject*>(i);
+				ScriptMaterial* scriptMaterial = ScriptMaterial::toNative(monoMaterial);
+
+				if (scriptMaterial != nullptr)
+					nativeMaterials[i] = scriptMaterial->getMaterialHandle();
+			}
 
 
-			if (scriptMaterial != nullptr)
-				nativeMaterials[i] = scriptMaterial->getMaterialHandle();
+			thisPtr->getInternal()->setMaterials(nativeMaterials);
+		}
+		else
+		{
+			thisPtr->getInternal()->setMaterials({});
 		}
 		}
-		
-		thisPtr->getInternal()->setMaterials(nativeMaterials);
 	}
 	}
 
 
 	void ScriptRenderable::internal_SetMaterial(ScriptRenderable* thisPtr, ScriptMaterial* material, int index)
 	void ScriptRenderable::internal_SetMaterial(ScriptRenderable* thisPtr, ScriptMaterial* material, int index)

+ 1 - 0
TODO.txt

@@ -67,6 +67,7 @@ Other polish:
    - Add a chaching mechanism to inspector (likely based on instance ID & property names)
    - Add a chaching mechanism to inspector (likely based on instance ID & property names)
    - This has to work not only when I come back to the object, but whenever inspector rebuilds (e.g. after removing element from array)
    - This has to work not only when I come back to the object, but whenever inspector rebuilds (e.g. after removing element from array)
    - Consider saving this information with the serialized object
    - Consider saving this information with the serialized object
+   - This has to work for custom inspectors as well (e.g. manually adding GUIFoldout) - manually recording "isExpanded" bool might be okay in this case
  - Need a proper way to detect when the scene was modified (and display it somewhere)
  - Need a proper way to detect when the scene was modified (and display it somewhere)
 
 
 Stage 2 polish:
 Stage 2 polish: