Просмотр исходного кода

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 лет назад
Родитель
Сommit
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();
 }

+ 5 - 0
BansheeEditor/Include/BsProjectLibrary.h

@@ -399,4 +399,9 @@ namespace BansheeEngine
 		UnorderedSet<Path> mReimportQueue;
 		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(DefaultFontFilename + L".asset");
 
-		HFont font = Resources::instance().load<Font>(fontPath);
+		HFont font = gResources().load<Font>(fontPath);
 
 		// Blank entry
 		GUIElementStyle blankStyle;
@@ -1196,6 +1196,10 @@ namespace BansheeEngine
 		GUIElementStyle objectDropStyle;
 		objectDropStyle.normal.texture = getGUITexture(ObjectDropBtnNormalTex);
 		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.hover.textColor = TextNormalColor;
 		objectDropStyle.active.textColor = TextNormalColor;
@@ -1740,7 +1744,7 @@ namespace BansheeEngine
 		texturePath.append(EditorSkinFolder);
 		texturePath.append(L"sprite_" + name + L".asset");
 
-		return Resources::instance().load<SpriteTexture>(texturePath);
+		return gResources().load<SpriteTexture>(texturePath);
 	}
 
 	HSpriteTexture BuiltinEditorResources::getGUIIcon(const WString& name)
@@ -1749,7 +1753,7 @@ namespace BansheeEngine
 		texturePath.append(EditorIconFolder);
 		texturePath.append(L"sprite_" + name + L".asset");
 
-		return Resources::instance().load<SpriteTexture>(texturePath);
+		return gResources().load<SpriteTexture>(texturePath);
 	}
 
 	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
 		};
 
-		Vector<ProjectLibrary::LibraryEntry*> libraryEntries = ProjectLibrary::instance().search(L"*", scriptTypeIds);
+		Vector<ProjectLibrary::LibraryEntry*> libraryEntries = gProjectLibrary().search(L"*", scriptTypeIds);
 		
 		PlatformType activePlatform = BuildManager::instance().getActivePlatform();
 		Vector<WString> frameworkAssemblies = BuildManager::instance().getFrameworkAssemblies(activePlatform);

+ 4 - 4
BansheeEditor/Source/BsEditorApplication.cpp

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

+ 7 - 7
BansheeEditor/Source/BsGUIResourceTreeView.cpp

@@ -39,10 +39,10 @@ namespace BansheeEngine
 	{
 		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.mElementName = mRootElement.mFullPath.getWTail();
@@ -99,14 +99,14 @@ namespace BansheeEngine
 		Path newPath = oldPath.getParent();
 		newPath.append(name);
 
-		ProjectLibrary::instance().moveEntry(oldPath, findUniquePath(newPath));
+		gProjectLibrary().moveEntry(oldPath, findUniquePath(newPath));
 	}
 
 	void GUIResourceTreeView::deleteTreeElement(TreeElement* 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)
@@ -256,7 +256,7 @@ namespace BansheeEngine
 		ResourceTreeElement* newElement = addTreeElement(parentElement, path);
 		sortTreeElement(parentElement);
 
-		ProjectLibrary::LibraryEntry* libEntry = ProjectLibrary::instance().findEntry(path);
+		ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(path);
 		
 		assert(libEntry != nullptr);
 		updateFromProjectLibraryEntry(newElement, libEntry);
@@ -432,7 +432,7 @@ namespace BansheeEngine
 					Path newPath = destDir;
 					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)
 			{
-				ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(path);
+				ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 
 				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);
 		String resUUID = resEntry->meta->getUUID();
 
-		return Resources::instance().loadFromUUID(resUUID);
+		return gResources().loadFromUUID(resUUID);
 	}
 
 	void ProjectLibrary::createInternalParentHierarchy(const Path& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)
@@ -1280,4 +1280,9 @@ namespace BansheeEngine
 		for (auto& dependency : iterFind->second)
 			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;
 		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)
 			{
 				ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
@@ -73,7 +73,7 @@ namespace BansheeEngine
 		mSelectedResourcePaths.clear();
 		for (auto& uuid : UUIDs)
 		{
-			Path path = ProjectLibrary::instance().uuidToPath(uuid);
+			Path path = gProjectLibrary().uuidToPath(uuid);
 			if (path != Path::BLANK)
 				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));
 		}
 
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(path);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 		{
 			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(DefaultFontFilename + L".asset");
 
-		HFont font = Resources::instance().load<Font>(fontPath);
+		HFont font = gResources().load<Font>(fontPath);
 		HGUISkin skin = GUISkin::create();
 
 		// Label
@@ -798,7 +798,7 @@ namespace BansheeEngine
 		texturePath.append(EngineSkinFolder);
 		texturePath.append(L"sprite_" + name + L".asset");
 
-		return Resources::instance().load<SpriteTexture>(texturePath);
+		return gResources().load<SpriteTexture>(texturePath);
 	}
 
 	HShader BuiltinResources::getShader(const Path& path)
@@ -816,7 +816,7 @@ namespace BansheeEngine
 		cursorPath.append(EngineCursorFolder);
 		cursorPath.append(name + L".asset");
 
-		return Resources::instance().load<Texture>(cursorPath);
+		return gResources().load<Texture>(cursorPath);
 	}
 
 	const PixelData& BuiltinResources::getCursorArrow(Vector2I& hotSpot)
@@ -903,7 +903,7 @@ namespace BansheeEngine
 			break;
 		}
 
-		return Resources::instance().load<Mesh>(meshPath);
+		return gResources().load<Mesh>(meshPath);
 	}
 
 	GUIMaterialInfo BuiltinResources::createSpriteTextMaterial() const

+ 31 - 26
MBansheeEditor/GUI/GUIArray.cs

@@ -161,7 +161,7 @@ namespace BansheeEditor
         /// <summary>
         /// 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 
-        /// 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>
         /// <param name="index">Sequential index of the element in the list to clone.</param>
         protected internal abstract void OnCloneButtonClicked(int index);
@@ -193,6 +193,11 @@ namespace BansheeEditor
         /// </summary>
         public Action<Array> OnChanged;
 
+        /// <summary>
+        /// Triggered when an element in the array has been changed.
+        /// </summary>
+        public Action OnValueChanged;
+
         /// <summary>
         /// Array object whose contents are displayed.
         /// </summary>
@@ -207,29 +212,16 @@ namespace BansheeEditor
         /// <summary>
         /// Creates a new GUI array.
         /// </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="array">Object containing the list data. Cannot be null.</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();
-            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;
         }
@@ -276,7 +268,8 @@ namespace BansheeEditor
         {
             list = Array.CreateInstance(listType.GetElementType(), 0);
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
 
         /// <inheritdoc/>
@@ -293,7 +286,8 @@ namespace BansheeEditor
 
             list = newArray;
 
-            OnChanged((Array)list);
+            if(OnChanged != null)
+                OnChanged((Array)list);
         }
 
         /// <inheritdoc/>
@@ -301,7 +295,8 @@ namespace BansheeEditor
         {
             list = null;
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
 
         /// <inheritdoc/>
@@ -314,6 +309,9 @@ namespace BansheeEditor
         protected internal override void SetValue(int seqIndex, object value)
         {
             list[seqIndex] = value;
+
+            if (OnValueChanged != null)
+                OnValueChanged();
         }
 
         /// <inheritdoc/>
@@ -334,7 +332,8 @@ namespace BansheeEditor
 
             list = newArray;
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
 
         /// <inheritdoc/>
@@ -347,7 +346,6 @@ namespace BansheeEditor
             for (int i = 0; i < list.Count; i++)
             {
                 object value = list[i];
-
                 newArray.SetValue(value, i);
 
                 if (i == index)
@@ -366,7 +364,7 @@ namespace BansheeEditor
                             if (cloneable != null)
                                 clonedEntry = cloneable.Clone();
                             else
-                                clonedEntry = Activator.CreateInstance(value.GetType());
+                                clonedEntry = null;
                         }
                     }
                 }
@@ -376,7 +374,8 @@ namespace BansheeEditor
 
             list = newArray;
 
-            OnChanged((Array)list);
+            if (OnChanged != null)
+                OnChanged((Array)list);
         }
 
         /// <inheritdoc/>
@@ -388,6 +387,9 @@ namespace BansheeEditor
 
                 list[index - 1] = list[index];
                 list[index] = previousEntry;
+
+                if (OnValueChanged != null)
+                    OnValueChanged();
             }
         }
 
@@ -400,6 +402,9 @@ namespace BansheeEditor
 
                 list[index + 1] = list[index];
                 list[index] = nextEntry;
+
+                if (OnValueChanged != null)
+                    OnValueChanged();
             }
         }
     }

+ 4 - 2
MBansheeEditor/Inspectors/FontInspector.cs

@@ -71,7 +71,8 @@ namespace BansheeEditor
         {
             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 =>
             {
                 int[] newFontSizes = x as int[];
@@ -80,7 +81,8 @@ namespace BansheeEditor
                 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 =>
             {
                 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;
 
 namespace BansheeEditor
@@ -36,8 +37,13 @@ namespace BansheeEditor
             layout.AddElement(layersField);
 
             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;
             layersField.OnSelectionChanged += x =>
             {
@@ -56,7 +62,10 @@ namespace BansheeEditor
                 for (int i = 0; i < materials.Length; i++)
                 {
                     if (materials[i] == null)
+                    {
+                        materialParams.Add(new MaterialParamGUI[0]);
                         continue;
+                    }
 
                     layout.AddSpace(10);
 
@@ -130,12 +139,15 @@ namespace BansheeEditor
                 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;
 
-                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;
                 if (mesh != null)
                     meshPtr = mesh.GetCachedPtr();
@@ -53,7 +44,7 @@ namespace BansheeEngine
             set { Internal_SetLayers(mCachedPtr, value); }
         }
 
-        private Material[] materials = new Material[0];
+        private Material[] materials = new Material[1];
         private Mesh mesh;
         
         public NativeRenderable(SceneObject sceneObject)
@@ -68,19 +59,7 @@ namespace BansheeEngine
         internal Material[] 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)

+ 2 - 2
SBansheeEditor/Include/BsScriptGUIResourceField.h

@@ -18,9 +18,9 @@ namespace BansheeEngine
 		 * @brief	Triggered when the value in the native resource field changes.
 		 *
 		 * @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.

+ 2 - 2
SBansheeEditor/Source/BsEditorResourceLoader.cpp

@@ -8,7 +8,7 @@ namespace BansheeEngine
 {
 	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)
 			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.");
 		}
 
-		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
 	{
-		return Resources::instance().loadFromUUID(mUUID);
+		if (!mUUID.empty())
+			return gResources().loadFromUUID(mUUID);
+
+		return HResource();
 	}
 
 	void GUIResourceField::setValue(const HResource& value)
@@ -193,10 +196,10 @@ namespace BansheeEngine
 	{ 
 		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)));
 		}
 		else
@@ -230,7 +233,7 @@ namespace BansheeEngine
 		if (mUUID == "")
 			return;
 
-		Path resPath = ProjectLibrary::instance().uuidToPath(mUUID);
+		Path resPath = gProjectLibrary().uuidToPath(mUUID);
 		Selection::instance().ping(resPath);
 	}
 
@@ -248,7 +251,7 @@ namespace BansheeEngine
 		{
 			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)
 				continue;
 

+ 3 - 3
SBansheeEditor/Source/BsGUITextureField.cpp

@@ -188,7 +188,7 @@ namespace BansheeEngine
 
 		Path filePath;
 		if (Resources::instance().getFilePathFromUUID(mUUID, filePath))
-			texture = Resources::instance().load<Texture>(filePath);
+			texture = gResources().load<Texture>(filePath);
 		
 		if (texture != nullptr)
 		{
@@ -230,7 +230,7 @@ namespace BansheeEngine
 		if (mUUID == "")
 			return;
 
-		Path resPath = ProjectLibrary::instance().uuidToPath(mUUID);
+		Path resPath = gProjectLibrary().uuidToPath(mUUID);
 		Selection::instance().ping(resPath);
 	}
 
@@ -246,7 +246,7 @@ namespace BansheeEngine
 		{
 			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)
 				continue;
 

+ 4 - 4
SBansheeEditor/Source/BsScriptEditorApplication.cpp

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

+ 10 - 3
SBansheeEditor/Source/BsScriptGUIResourceField.cpp

@@ -16,6 +16,7 @@
 #include "BsScriptGUIContent.h"
 #include "BsScriptResource.h"
 #include "BsScriptResourceManager.h"
+#include "BsResources.h"
 
 using namespace std::placeholders;
 
@@ -100,9 +101,11 @@ namespace BansheeEngine
 		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);
 	}
 
@@ -116,7 +119,11 @@ namespace BansheeEngine
 		{
 			ScriptResourceBase* scriptResource = ScriptResourceManager::instance().getScriptResource(instance.getUUID());
 			if (scriptResource == nullptr)
-				return nullptr;
+			{
+				ScriptResourceManager::instance().createScriptResource(instance, &scriptResource);
+
+				return scriptResource->getManagedInstance();
+			}
 			else
 				return scriptResource->getManagedInstance();
 		}

+ 34 - 34
SBansheeEditor/Source/BsScriptProjectLibrary.cpp

@@ -60,10 +60,10 @@ namespace BansheeEngine
 		Path nativePath = MonoUtil::monoToWString(path);
 
 		if (!nativePath.isAbsolute())
-			nativePath.makeAbsolute(ProjectLibrary::instance().getResourcesFolder());
+			nativePath.makeAbsolute(gProjectLibrary().getResourcesFolder());
 
 		Vector<Path> dirtyResources;
-		ProjectLibrary::instance().checkForModifications(nativePath, import, dirtyResources);
+		gProjectLibrary().checkForModifications(nativePath, import, dirtyResources);
 
 		ScriptArray output = ScriptArray::create<WString>((UINT32)dirtyResources.size());
 		for (UINT32 i = 0; i < (UINT32)dirtyResources.size(); i++)
@@ -79,7 +79,7 @@ namespace BansheeEngine
 		ScriptResource* scrResource = ScriptResource::toNative(resource);
 		Path resourcePath = MonoUtil::monoToWString(path);
 
-		ProjectLibrary::instance().createEntry(scrResource->getNativeHandle(), resourcePath);
+		gProjectLibrary().createEntry(scrResource->getNativeHandle(), resourcePath);
 	}
 
 	MonoObject* ScriptProjectLibrary::internal_Load(MonoString* path)
@@ -87,11 +87,11 @@ namespace BansheeEngine
 		Path resourcePath = MonoUtil::monoToWString(path);
 
 		HResource resource;
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(resourcePath);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(resourcePath);
 		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 		{
 			ProjectLibrary::ResourceEntry* resEntry = static_cast <ProjectLibrary::ResourceEntry*>(entry);
-			resource = Resources::instance().loadFromUUID(resEntry->meta->getUUID());
+			resource = gResources().loadFromUUID(resEntry->meta->getUUID());
 		}
 		
 		if (!resource)
@@ -108,12 +108,12 @@ namespace BansheeEngine
 		ScriptResource* srcResource = ScriptResource::toNative(resource);
 
 		if (srcResource != nullptr)
-			ProjectLibrary::instance().saveEntry(srcResource->getNativeHandle());
+			gProjectLibrary().saveEntry(srcResource->getNativeHandle());
 	}
 
 	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)
@@ -127,14 +127,14 @@ namespace BansheeEngine
 			nativeOptions = scriptOptions->getImportOptions();
 		}
 
-		ProjectLibrary::instance().reimport(assetPath, nativeOptions, force);
+		gProjectLibrary().reimport(assetPath, nativeOptions, force);
 	}
 
 	MonoObject* ScriptProjectLibrary::internal_GetEntry(MonoString* path)
 	{
 		Path assetPath = MonoUtil::monoToWString(path);
 
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(assetPath);
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(assetPath);
 		if (entry == nullptr)
 			return nullptr;
 
@@ -147,7 +147,7 @@ namespace BansheeEngine
 	MonoString* ScriptProjectLibrary::internal_GetPathFromUUID(MonoString* 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());
 	}
@@ -158,8 +158,8 @@ namespace BansheeEngine
 
 		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());
 		}
@@ -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;
 		ScriptArray outArray = ScriptArray::create<ScriptLibraryEntry>((UINT32)foundEntries.size());
@@ -205,13 +205,13 @@ namespace BansheeEngine
 	void ScriptProjectLibrary::internal_Delete(MonoString* path)
 	{
 		Path pathToDelete = MonoUtil::monoToWString(path);
-		ProjectLibrary::instance().deleteEntry(pathToDelete);
+		gProjectLibrary().deleteEntry(pathToDelete);
 	}
 
 	void ScriptProjectLibrary::internal_CreateFolder(MonoString* path)
 	{
 		Path folderToCreate = MonoUtil::monoToWString(path);
-		ProjectLibrary::instance().createFolderEntry(folderToCreate);
+		gProjectLibrary().createFolderEntry(folderToCreate);
 	}
 
 	void ScriptProjectLibrary::internal_Rename(MonoString* path, MonoString* name, bool overwrite)
@@ -219,7 +219,7 @@ namespace BansheeEngine
 		Path oldPath = MonoUtil::monoToWString(path);
 		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)
@@ -227,7 +227,7 @@ namespace BansheeEngine
 		Path oldPathNative = MonoUtil::monoToWString(oldPath);
 		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)
@@ -235,25 +235,25 @@ namespace BansheeEngine
 		Path oldPathNative = MonoUtil::monoToWString(source);
 		Path newPathNative = MonoUtil::monoToWString(destination);
 
-		ProjectLibrary::instance().copyEntry(oldPathNative, newPathNative, overwrite);
+		gProjectLibrary().copyEntry(oldPathNative, newPathNative, overwrite);
 	}
 
 	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)
 	{
 		Path pathNative = MonoUtil::monoToWString(path);
 
-		ProjectLibrary::instance().setIncludeInBuild(pathNative, include);
+		gProjectLibrary().setIncludeInBuild(pathNative, include);
 	}
 
 	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()
@@ -266,7 +266,7 @@ namespace BansheeEngine
 	{
 		Path relativePath = path;
 		if (relativePath.isAbsolute())
-			relativePath.makeRelative(ProjectLibrary::instance().getResourcesFolder());
+			relativePath.makeRelative(gProjectLibrary().getResourcesFolder());
 
 		MonoString* pathStr = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 		MonoUtil::invokeThunk(OnEntryAddedThunk, pathStr);
@@ -276,7 +276,7 @@ namespace BansheeEngine
 	{
 		Path relativePath = path;
 		if (relativePath.isAbsolute())
-			relativePath.makeRelative(ProjectLibrary::instance().getResourcesFolder());
+			relativePath.makeRelative(gProjectLibrary().getResourcesFolder());
 
 		MonoString* pathStr = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 		MonoUtil::invokeThunk(OnEntryRemovedThunk, pathStr);
@@ -296,19 +296,19 @@ namespace BansheeEngine
 
 	MonoString* ScriptLibraryEntry::internal_GetPath(ScriptLibraryEntryBase* thisPtr)
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr)
 			return nullptr;
 
 		Path relativePath = entry->path;
-		relativePath.makeRelative(ProjectLibrary::instance().getResourcesFolder());
+		relativePath.makeRelative(gProjectLibrary().getResourcesFolder());
 
 		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 	}
 
 	MonoString* ScriptLibraryEntry::internal_GetName(ScriptLibraryEntryBase* thisPtr)
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr)
 			return nullptr;
 
@@ -317,7 +317,7 @@ namespace BansheeEngine
 
 	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)
 			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)
 	{
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(thisPtr->getAssetPath());
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->parent == nullptr)
 			return nullptr;
 
@@ -354,7 +354,7 @@ namespace BansheeEngine
 
 	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)
 			return ScriptArray::create<ScriptLibraryEntry>(0).getInternal();
 
@@ -401,7 +401,7 @@ namespace BansheeEngine
 
 	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)
 			return nullptr;
 
@@ -415,7 +415,7 @@ namespace BansheeEngine
 
 	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)
 			return nullptr;
 
@@ -435,7 +435,7 @@ namespace BansheeEngine
 
 	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)
 			return ScriptResourceType::Undefined;
 
@@ -449,7 +449,7 @@ namespace BansheeEngine
 
 	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)
 			return false;
 

+ 1 - 1
SBansheeEditor/Source/BsToolbarItemManager.cpp

@@ -128,7 +128,7 @@ namespace BansheeEngine
 			mIconField->getValue(toolbarItemAttrib, &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;

+ 17 - 10
SBansheeEngine/Source/BsScriptRenderable.cpp

@@ -101,19 +101,26 @@ namespace BansheeEngine
 
 	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)

+ 1 - 0
TODO.txt

@@ -67,6 +67,7 @@ Other polish:
    - 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)
    - 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)
 
 Stage 2 polish: