浏览代码

Refactoring project library so that file entries can have multiple resources (WIP)

BearishSun 10 年之前
父节点
当前提交
3c45e241c3

+ 21 - 0
BansheeCore/Include/BsImporter.h

@@ -4,6 +4,7 @@
 
 #include "BsCorePrerequisites.h"
 #include "BsModule.h"
+#include "BsSpecificImporter.h"
 
 namespace BansheeEngine
 {
@@ -112,6 +113,8 @@ namespace BansheeEngine
 		 */
 		bool supportsFileType(const UINT8* magicNumber, UINT32 magicNumSize) const;
 
+		/** @cond INTERNAL */
+
 		/**
 		 * Adds a new asset importer for the specified file extension. If an asset importer for that extension already 
 		 * exists, it is removed and replaced with the current one.
@@ -125,6 +128,24 @@ namespace BansheeEngine
 		 *			of the provided pointer and will release it. Assumes it is allocated using the general allocator.
 		 */
 		void _registerAssetImporter(SpecificImporter* importer);
+
+		/**
+		 * Imports a resource at the specified location but doesn't create resource handles. This method returns all 
+		 * imported resources, which is relevant for files that can contain multiple resources (e.g. an FBX which may
+		 * contain both a mesh and animations). 
+		 *
+		 * @param[in]	inputFilePath	Pathname of the input file.
+		 * @param[in]	importOptions	(optional) Options for controlling the import. Caller must ensure import options 
+		 *								actually match the type of the importer used for the file type.
+		 * @return						A list of all imported resources. The primary resource is always the first returned
+		 *								resource. Caller is responsible for creating resource handles for the returned 
+		 *								values.
+		 *
+		 * @see		createImportOptions
+		 */
+		Vector<SubResourceRaw> _importAllRaw(const Path& inputFilePath, ConstImportOptionsPtr importOptions = nullptr);
+
+		/** @endcond */
 	private:
 		/** 
 		 * Searches available importers and attempts to find one that can import the file of the provided type. Returns null

+ 18 - 13
BansheeCore/Source/BsImporter.cpp

@@ -83,16 +83,31 @@ namespace BansheeEngine
 	}
 
 	Vector<SubResource> Importer::importAll(const Path& inputFilePath, ConstImportOptionsPtr importOptions)
+	{
+		Vector<SubResource> output;
+
+		Vector<SubResourceRaw> importedResource = _importAllRaw(inputFilePath, importOptions);
+		for(auto& entry : importedResource)
+		{
+			HResource handle = gResources()._createResourceHandle(entry.value);
+
+			output.push_back({ entry.name, handle });
+		}
+
+		return output;
+	}
+
+	Vector<SubResourceRaw> Importer::_importAllRaw(const Path& inputFilePath, ConstImportOptionsPtr importOptions)
 	{
 		if (!FileSystem::isFile(inputFilePath))
 		{
 			LOGWRN("Trying to import asset that doesn't exists. Asset path: " + inputFilePath.toString());
-			return Vector<SubResource>();
+			return Vector<SubResourceRaw>();
 		}
 
 		SpecificImporter* importer = getImporterForFile(inputFilePath);
 		if (importer == nullptr)
-			return Vector<SubResource>();
+			return Vector<SubResourceRaw>();
 
 		if (importOptions == nullptr)
 			importOptions = importer->getDefaultImportOptions();
@@ -106,17 +121,7 @@ namespace BansheeEngine
 			}
 		}
 
-		Vector<SubResource> output;
-
-		Vector<SubResourceRaw> importedResource = importer->importAll(inputFilePath, importOptions);
-		for(auto& entry : importedResource)
-		{
-			HResource handle = gResources()._createResourceHandle(entry.value);
-
-			output.push_back({ entry.name, handle });
-		}
-
-		return output;
+		return importer->importAll(inputFilePath, importOptions);
 	}
 
 	void Importer::reimport(HResource& existingResource, const Path& inputFilePath, ConstImportOptionsPtr importOptions)

+ 8 - 10
BansheeEditor/Include/BsEditorPrerequisites.h

@@ -57,6 +57,7 @@ namespace BansheeEngine
 	class GUIResourceTreeView;
 	class GUITreeViewEditBox;
 	class EditorCommand;
+	class ProjectFileMeta;
 	class ProjectResourceMeta;
 	class SceneGrid;
 	class HandleSlider;
@@ -72,6 +73,7 @@ namespace BansheeEngine
 	class DropDownWindow;
 	class ProjectSettings;
 
+	typedef std::shared_ptr<ProjectFileMeta> ProjectFileMetaPtr;
 	typedef std::shared_ptr<ProjectResourceMeta> ProjectResourceMetaPtr;
 	typedef std::shared_ptr<DockManagerLayout> DockManagerLayoutPtr;
 	typedef std::shared_ptr<EditorWidgetLayout> EditorWidgetLayoutPtr;
@@ -83,10 +85,7 @@ namespace BansheeEngine
 	static const Path PROJECT_INTERNAL_DIR = L"Internal\\";
 	static const Path INTERNAL_ASSEMBLY_PATH = PROJECT_INTERNAL_DIR + "Assemblies\\";
 
-	/**
-	 * @brief	Types of drag and drop operations. Different types specify
-	 *			different types of dragged data.
-	 */
+	/** Types of drag and drop operations. Different types specify different types of dragged data. */
 	enum class DragAndDropType
 	{
 		EditorWidget = 10000,
@@ -95,7 +94,7 @@ namespace BansheeEngine
 	};
 
 	/**
-	 * @brief	Recognized types of external code editors.
+	 * Recognized types of external code editors.
 	 *
 	 * @see		CodeEditorManager
 	 */
@@ -109,12 +108,10 @@ namespace BansheeEngine
 		None
 	};
 
-	/**
-	 * @brief	Type IDs used by the RTTI system for the editor library.
-	 */
+	/**	Type IDs used by the RTTI system for the editor library. */
 	enum TypeID_BansheeEditor
 	{
-		TID_ProjectResourceMeta = 40000,
+		TID_ProjectFileMeta = 40000,
 		TID_ProjectLibraryEntries = 40001,
 		TID_ProjectLibraryResEntry = 40002,
 		TID_ProjectLibraryDirEntry = 40003,
@@ -135,6 +132,7 @@ namespace BansheeEngine
 		TID_RecentProject = 40018,
 		TID_Settings = 40019,
 		TID_ProjectSettings = 40020,
-		TID_WindowFrameWidget = 40021
+		TID_WindowFrameWidget = 40021,
+		TID_ProjectResourceMeta = 40022
 	};
 }

+ 26 - 15
BansheeEditor/Include/BsProjectLibrary.h

@@ -17,7 +17,7 @@ namespace BansheeEngine
 	{
 	public:
 		struct LibraryEntry;
-		struct ResourceEntry;
+		struct FileEntry;
 		struct DirectoryEntry;
 
 		/**
@@ -47,14 +47,14 @@ namespace BansheeEngine
 		};
 
 		/**
-		 * @brief	A library entry representing a resource.
+		 * @brief	A library entry representing a file.
 		 */
-		struct ResourceEntry : public LibraryEntry
+		struct FileEntry : public LibraryEntry
 		{
-			ResourceEntry();
-			ResourceEntry(const Path& path, const WString& name, DirectoryEntry* parent);
+			FileEntry();
+			FileEntry(const Path& path, const WString& name, DirectoryEntry* parent);
 
-			ProjectResourceMetaPtr meta; /**< Meta file containing various information about the resource. */
+			ProjectFileMetaPtr meta; /**< Meta file containing various information about the resource. */
 			std::time_t lastUpdateTime; /**< Timestamp of when we last imported the resource. */
 		};
 
@@ -108,6 +108,16 @@ namespace BansheeEngine
 		 */
 		LibraryEntry* findEntry(const Path& path) const;
 
+		/**
+		 * @brief	Attempts to a find a meta information for a resource at the specified path.
+		 *
+		 * @param	path	Path to the entry, either absolute or relative to resources folder. If a sub-resource within
+		 *					a file is needed, append the name of the subresource to the path (e.g. mymesh.fbx/my_animation).
+		 *
+		 * @return	Found meta information for the resource, or null if not found. 
+		 */
+		ProjectResourceMetaPtr findResourceMeta(const Path& path) const;
+
 		/**
 		 * @brief	Searches the library for a pattern and returns all entries matching it.
 		 *
@@ -210,12 +220,13 @@ namespace BansheeEngine
 		 *			Values returned by this method are transient, they may be destroyed
 		 *			on any following ProjectLibrary call.
 		 */
-		Vector<ResourceEntry*> getResourcesForBuild() const;
+		Vector<FileEntry*> getResourcesForBuild() const;
 
 		/**
 		 * @brief	Loads a resource at the specified path, synchronously.
 		 *
-		 * @param	path	Path of the resource, absolute or relative to resources folder.
+		 * @param	path	Path of the resource, absolute or relative to resources folder. If a sub-resource within
+		 *					a file is needed, append the name of the subresource to the path (e.g. mymesh.fbx/my_animation).
 		 *
 		 * @return	Loaded resource, or null handle if one is not found.
 		 */
@@ -273,7 +284,7 @@ namespace BansheeEngine
 		 *
 		 * @return	Newly added resource entry.
 		 */
-		ResourceEntry* addResourceInternal(DirectoryEntry* parent, const Path& filePath, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false);
+		FileEntry* addResourceInternal(DirectoryEntry* parent, const Path& filePath, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false);
 
 		/**
 		 * @brief	Common code for adding a new folder entry to the library.
@@ -291,7 +302,7 @@ namespace BansheeEngine
 		 *
 		 * @param	resource	Entry to delete.
 		 */
-		void deleteResourceInternal(ResourceEntry* resource);
+		void deleteResourceInternal(FileEntry* resource);
 
 		/**
 		 * @brief	Common code for deleting a directory from the library. This code only removes
@@ -311,7 +322,7 @@ namespace BansheeEngine
 		 * @param	forceReimport	Should the resource be reimported even if we detect no changes. This should be true
 		 *							if import options changed since last import.
 		 */
-		void reimportResourceInternal(ResourceEntry* resource, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false);
+		void reimportResourceInternal(FileEntry* resource, const ImportOptionsPtr& importOptions = nullptr, bool forceReimport = false);
 
 		/**
 		 * @brief	Creates a full hierarchy of directory entries up to the provided directory, if any are needed.
@@ -325,7 +336,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Checks has the resource been modified since the last import.
 		 */
-		bool isUpToDate(ResourceEntry* resource) const;
+		bool isUpToDate(FileEntry* resource) const;
 
 		/**
 		 * @brief	Checks is the resource a native engine resource that doesn't require importing.
@@ -350,17 +361,17 @@ namespace BansheeEngine
 		 * @brief	Returns a set of resource paths that are dependent on the provided
 		 *			resource entry. (e.g. a shader file might be dependent on shader include file).
 		 */
-		Vector<Path> getImportDependencies(const ResourceEntry* entry);
+		Vector<Path> getImportDependencies(const FileEntry* entry);
 
 		/**
 		 * @brief	Registers any import dependencies for the specified resource.
 		 */
-		void addDependencies(const ResourceEntry* entry);
+		void addDependencies(const FileEntry* entry);
 
 		/**
 		 * @brief	Removes any import dependencies for the specified resource.
 		 */
-		void removeDependencies(const ResourceEntry* entry);
+		void removeDependencies(const FileEntry* entry);
 
 		/**
 		 * @brief	Finds dependants resource for the specified resource entry and reimports them.

+ 7 - 7
BansheeEditor/Include/BsProjectLibraryEntriesRTTI.h

@@ -40,11 +40,11 @@ namespace BansheeEngine
 
 namespace BansheeEngine
 {
-	template<> struct RTTIPlainType<BansheeEngine::ProjectLibrary::ResourceEntry>
+	template<> struct RTTIPlainType<BansheeEngine::ProjectLibrary::FileEntry>
 	{	
 		enum { id = BansheeEngine::TID_ProjectLibraryResEntry }; enum { hasDynamicSize = 1 };
 
-		static void toMemory(const BansheeEngine::ProjectLibrary::ResourceEntry& data, char* memory)
+		static void toMemory(const BansheeEngine::ProjectLibrary::FileEntry& data, char* memory)
 		{ 
 			UINT32 size = 0;
 			char* memoryStart = memory;
@@ -60,7 +60,7 @@ namespace BansheeEngine
 			memcpy(memoryStart, &size, sizeof(UINT32));
 		}
 
-		static UINT32 fromMemory(BansheeEngine::ProjectLibrary::ResourceEntry& data, char* memory)
+		static UINT32 fromMemory(BansheeEngine::ProjectLibrary::FileEntry& data, char* memory)
 		{ 
 			UINT32 size = 0;
 			memcpy(&size, memory, sizeof(UINT32));
@@ -77,7 +77,7 @@ namespace BansheeEngine
 			return size;
 		}
 
-		static UINT32 getDynamicSize(const BansheeEngine::ProjectLibrary::ResourceEntry& data)	
+		static UINT32 getDynamicSize(const BansheeEngine::ProjectLibrary::FileEntry& data)	
 		{ 
 			UINT64 dataSize = sizeof(UINT32) + rttiGetElemSize(data.type) + rttiGetElemSize(data.path) + rttiGetElemSize(data.elementName) +
 				rttiGetElemSize(data.lastUpdateTime);
@@ -115,7 +115,7 @@ namespace BansheeEngine
 			{
 				if(child->type == BansheeEngine::ProjectLibrary::LibraryEntryType::File)
 				{
-					BansheeEngine::ProjectLibrary::ResourceEntry* childResEntry = static_cast<BansheeEngine::ProjectLibrary::ResourceEntry*>(child);
+					BansheeEngine::ProjectLibrary::FileEntry* childResEntry = static_cast<BansheeEngine::ProjectLibrary::FileEntry*>(child);
 					memory = rttiWriteElem(*childResEntry, memory, size);
 				}
 				else if(child->type == BansheeEngine::ProjectLibrary::LibraryEntryType::Directory)
@@ -148,7 +148,7 @@ namespace BansheeEngine
 
 				if(childType == BansheeEngine::ProjectLibrary::LibraryEntryType::File)
 				{
-					BansheeEngine::ProjectLibrary::ResourceEntry* childResEntry = bs_new<BansheeEngine::ProjectLibrary::ResourceEntry>(); // Note: Assumes that ProjectLibrary takes care of the cleanup
+					BansheeEngine::ProjectLibrary::FileEntry* childResEntry = bs_new<BansheeEngine::ProjectLibrary::FileEntry>(); // Note: Assumes that ProjectLibrary takes care of the cleanup
 					memory = rttiReadElem(*childResEntry, memory);
 
 					childResEntry->parent = &data;
@@ -177,7 +177,7 @@ namespace BansheeEngine
 			{
 				if(child->type == BansheeEngine::ProjectLibrary::LibraryEntryType::File)
 				{
-					BansheeEngine::ProjectLibrary::ResourceEntry* childResEntry = static_cast<BansheeEngine::ProjectLibrary::ResourceEntry*>(child);
+					BansheeEngine::ProjectLibrary::FileEntry* childResEntry = static_cast<BansheeEngine::ProjectLibrary::FileEntry*>(child);
 					dataSize += rttiGetElemSize(*childResEntry);
 				}
 				else if(child->type == BansheeEngine::ProjectLibrary::LibraryEntryType::Directory)

+ 74 - 31
BansheeEditor/Include/BsProjectResourceMeta.h

@@ -7,9 +7,7 @@
 
 namespace BansheeEngine
 {
-	/**
-	 * @brief	Contains meta-data for a resource stored in the ProjectLibrary.
-	 */
+	/**	Contains meta-data for a resource stored in the ProjectLibrary. */
 	class ProjectResourceMeta : public IReflectable
 	{
 	private:
@@ -19,58 +17,103 @@ namespace BansheeEngine
 		explicit ProjectResourceMeta(const ConstructPrivately&);
 
 		/**
-		 * @brief	Creates a new project library resource meta-data entry.
+		 * Creates a new project library resource meta-data entry.
 		 *
+		 * @param	name				Name of the resource, unique within the file containing the resource.
 		 * @param	uuid				UUID of the resource.
 		 * @param	typeId				RTTI type id of the resource.
 		 * @param	resourceMetaData	Non-project library specific meta-data.
-		 * @param	importOptions		Import options used for importing the resource.
 		 *
 		 * @return	New project library resource meta data instance.
 		 */
-		static ProjectResourceMetaPtr create(const String& uuid, UINT32 typeId, const ResourceMetaDataPtr& resourceMetaData,
-			const ImportOptionsPtr& importOptions);
+		static ProjectResourceMetaPtr create(const WString name, const String& uuid, UINT32 typeId, 
+			const ResourceMetaDataPtr& resourceMetaData);
 
-		/**
-		 * @brief	Returns the UUID of the resource this meta data belongs to.
-		 */
+		/** Returns the name of the resource, unique within the file containing the resource. */
+		const WString& getUniqueName() const { return mName; }
+
+		/**	Returns the UUID of the resource this meta data belongs to. */
 		const String& getUUID() const { return mUUID; }
 
-		/**
-		 * @brief	Returns the non-project library specific meta-data,
-		 */
+		/**	Returns the non-project library specific meta-data. */
 		ResourceMetaDataPtr getResourceMetaData() const { return mResourceMeta; }
 
-		/**
-		 * @brief	Returns the import options used for importing the resource this
-		 *			object is referencing.
-		 */
-		const ImportOptionsPtr& getImportOptions() const { return mImportOptions; }
+		/**	Returns the RTTI type ID of the resource this object is referencing. */
+		UINT32 getTypeID() const { return mTypeId; }
+
+	private:
+		friend class ProjectLibrary;
+
+		WString mName;
+		String mUUID;
+		ResourceMetaDataPtr mResourceMeta;
+		UINT32 mTypeId;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
 
 		/**
-		 * @brief	Returns the RTTI type ID of the resource this object is referencing.
+		 * @brief	Creates a new empty meta-data instance. Used only for serialization purposes.
 		 */
-		UINT32 getTypeID() const { return mTypeId; }
+		static ProjectResourceMetaPtr createEmpty();
+
+	public:
+		friend class ProjectResourceMetaRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;	
+	};
+
+	/**	
+	 * Contains meta-data for a file stored in the ProjectLibrary. A single file meta-data can contain one or multiple
+	 * ProjectResourceMeta instances.
+	 */
+	class ProjectFileMeta : public IReflectable
+	{
+	private:
+		struct ConstructPrivately {};
+
+	public:
+		explicit ProjectFileMeta(const ConstructPrivately&);
 
 		/**
-		 * @brief	Checks should this resource always be included in the build, regardless if
-		 *			it's being referenced or not.
+		 * Creates a new project library file meta-data entry.
+		 *
+		 * @param	importOptions		Import options used for importing the resource.
+		 *
+		 * @return	New project library file meta data instance.
 		 */
+		static ProjectFileMetaPtr create(const ImportOptionsPtr& importOptions);
+
+		/** Registers a new resource in the file meta-data. */
+		void add(const ProjectResourceMetaPtr& resourceMeta);
+
+		/** Removes a resource with the specified UUID from the file meta-data. */
+		void remove(const String& UUID);
+
+		/** Returns meta-data for all resources contained in the file represented by this meta-data object. */
+		const Vector<ProjectResourceMetaPtr>& getResourceMetaData() const { return mResourceMetaData; }
+
+		/**	Returns the import options used for importing the resource this object is referencing. */
+		const ImportOptionsPtr& getImportOptions() const { return mImportOptions; }
+
+		/** Checks should this resource always be included in the build, regardless if it's being referenced or not. */
 		bool getIncludeInBuild() const { return mIncludeInBuild; }
 
-		/**
-		 * @brief	Determines if this resource will always be included in the build, regardless if
-		 *			it's being referenced or not.
-		 */
+		/** Determines if this resource will always be included in the build, regardless if it's being referenced or not. */
 		void setIncludeInBuild(bool include) { mIncludeInBuild = include; }
 
+		/** Checks does the file contain a resource with the specified type id. */
+		bool hasTypeId(UINT32 typeId) const;
+
+		/** Checks does the file contain a resource with the specified UUID. */
+		bool hasUUID(const String& uuid) const;
+
 	private:
 		friend class ProjectLibrary;
 
-		String mUUID;
-		ResourceMetaDataPtr mResourceMeta;
+		Vector<ProjectResourceMetaPtr> mResourceMetaData;
 		ImportOptionsPtr mImportOptions;
-		UINT32 mTypeId;
 		bool mIncludeInBuild;
 
 		/************************************************************************/
@@ -80,10 +123,10 @@ namespace BansheeEngine
 		/**
 		 * @brief	Creates a new empty meta-data instance. Used only for serialization purposes.
 		 */
-		static ProjectResourceMetaPtr createEmpty();
+		static ProjectFileMetaPtr createEmpty();
 
 	public:
-		friend class ProjectResourceMetaRTTI;
+		friend class ProjectFileMetaRTTI;
 		static RTTITypeBase* getRTTIStatic();
 		virtual RTTITypeBase* getRTTI() const override;	
 	};

+ 43 - 22
BansheeEditor/Include/BsProjectResourceMetaRTTI.h

@@ -13,45 +13,66 @@ namespace BansheeEngine
 	class ProjectResourceMetaRTTI : public RTTIType<ProjectResourceMeta, IReflectable, ProjectResourceMetaRTTI>
 	{
 	private:
-		String& getUUID(ProjectResourceMeta* obj) { return obj->mUUID; }
-		void setUUID(ProjectResourceMeta* obj, String& val) { obj->mUUID = val; } 
+		BS_PLAIN_MEMBER(mName)
+		BS_PLAIN_MEMBER(mUUID)
+		BS_PLAIN_MEMBER(mTypeId)
+		BS_REFLPTR_MEMBER(mResourceMeta)
+			
+	public:
+		ProjectResourceMetaRTTI()
+		{
+			BS_ADD_PLAIN_FIELD(mName, 0);
+			BS_ADD_PLAIN_FIELD(mUUID, 1);
+			BS_ADD_PLAIN_FIELD(mTypeId, 2);
+			BS_ADD_REFLPTR_FIELD(mResourceMeta, 3);
+		}
 
-		UINT32& getTypeId(ProjectResourceMeta* obj) { return obj->mTypeId; }
-		void setTypeId(ProjectResourceMeta* obj, UINT32& val) { obj->mTypeId = val; }
+		const String& getRTTIName() override
+		{
+			static String name = "ProjectResourceMeta";
+			return name;
+		}
 
-		ResourceMetaDataPtr getResourceMeta(ProjectResourceMeta* obj) { return obj->mResourceMeta; }
-		void setResourceMeta(ProjectResourceMeta* obj, ResourceMetaDataPtr val) { obj->mResourceMeta = val; }
+		UINT32 getRTTIId() override
+		{
+			return TID_ProjectResourceMeta;
+		}
 
-		ImportOptionsPtr getImportOptions(ProjectResourceMeta* obj) { return obj->mImportOptions; }
-		void setImportOptions(ProjectResourceMeta* obj, ImportOptionsPtr val) { obj->mImportOptions = val; }
+		SPtr<IReflectable> newRTTIObject() override
+		{
+			return ProjectResourceMeta::createEmpty();
+		}
+	};
 
-		bool& getIncludeInBuild(ProjectResourceMeta* obj) { return obj->mIncludeInBuild; }
-		void setIncludeInBuild(ProjectResourceMeta* obj, bool& val) { obj->mIncludeInBuild = val; }
+	class ProjectFileMetaRTTI : public RTTIType<ProjectFileMeta, IReflectable, ProjectFileMetaRTTI>
+	{
+	private:
+		BS_REFLPTR_MEMBER(mImportOptions)
+		BS_PLAIN_MEMBER(mIncludeInBuild)
+		BS_REFLPTR_MEMBER_VEC(mResourceMetaData)
 
 	public:
-		ProjectResourceMetaRTTI()
+		ProjectFileMetaRTTI()
 		{
-			addPlainField("mUUID", 0, &ProjectResourceMetaRTTI::getUUID, &ProjectResourceMetaRTTI::setUUID);
-			addReflectablePtrField("mImportOptions", 1, &ProjectResourceMetaRTTI::getImportOptions, &ProjectResourceMetaRTTI::setImportOptions);
-			addReflectablePtrField("mResourceMeta", 2, &ProjectResourceMetaRTTI::getResourceMeta, &ProjectResourceMetaRTTI::setResourceMeta);
-			addPlainField("mTypeId", 3, &ProjectResourceMetaRTTI::getTypeId, &ProjectResourceMetaRTTI::setTypeId);
-			addPlainField("mIncludeInBuild", 4, &ProjectResourceMetaRTTI::getIncludeInBuild, &ProjectResourceMetaRTTI::setIncludeInBuild);
+			BS_ADD_REFLPTR_FIELD(mImportOptions, 1);
+			BS_ADD_PLAIN_FIELD(mIncludeInBuild, 4);
+			BS_ADD_REFLPTR_FIELD_ARR(mResourceMetaData, 5);
 		}
 
-		virtual const String& getRTTIName() override
+		const String& getRTTIName() override
 		{
-			static String name = "ProjectResourceMeta";
+			static String name = "ProjectFileMeta";
 			return name;
 		}
 
-		virtual UINT32 getRTTIId() override
+		UINT32 getRTTIId() override
 		{
-			return TID_ProjectResourceMeta;
+			return TID_ProjectFileMeta;
 		}
 
-		virtual std::shared_ptr<IReflectable> newRTTIObject() override
+		SPtr<IReflectable> newRTTIObject() override
 		{
-			return ProjectResourceMeta::createEmpty();
+			return ProjectFileMeta::createEmpty();
 		}
 	};
 }

+ 2 - 2
BansheeEditor/Source/BsCodeEditor.cpp

@@ -119,8 +119,8 @@ namespace BansheeEngine
 			if (entry->type != ProjectLibrary::LibraryEntryType::File)
 				continue;
 
-			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
-			if (resEntry->meta->getTypeID() == TID_ScriptCode)
+			ProjectLibrary::FileEntry* resEntry = static_cast<ProjectLibrary::FileEntry*>(entry);
+			if (resEntry->meta->hasTypeId(TID_ScriptCode))
 			{
 				SPtr<ScriptCodeImportOptions> scriptIO = std::static_pointer_cast<ScriptCodeImportOptions>(resEntry->meta->getImportOptions());
 

+ 10 - 3
BansheeEditor/Source/BsEditorShaderIncludeHandler.cpp

@@ -25,10 +25,17 @@ namespace BansheeEngine
 		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
 		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
 		{
-			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+			ProjectLibrary::FileEntry* fileEntry = static_cast<ProjectLibrary::FileEntry*>(entry);
 
-			if (resEntry->meta != nullptr)
-				return static_resource_cast<ShaderInclude>(Resources::instance().loadFromUUID(resEntry->meta->getUUID()));
+			if (fileEntry->meta != nullptr)
+			{
+				auto& resourceMetas = fileEntry->meta->getResourceMetaData();
+				for(auto& resMeta : resourceMetas)
+				{
+					if(resMeta->getTypeID() == TID_ShaderInclude)
+						return static_resource_cast<ShaderInclude>(Resources::instance().loadFromUUID(resMeta->getUUID()));
+				}
+			}
 		}
 
 		return HShaderInclude();

+ 198 - 80
BansheeEditor/Source/BsProjectLibrary.cpp

@@ -35,11 +35,11 @@ namespace BansheeEngine
 		:path(path), parent(parent), type(type), elementName(name)
 	{ }
 
-	ProjectLibrary::ResourceEntry::ResourceEntry()
+	ProjectLibrary::FileEntry::FileEntry()
 		: lastUpdateTime(0)
 	{ }
 
-	ProjectLibrary::ResourceEntry::ResourceEntry(const Path& path, const WString& name, DirectoryEntry* parent)
+	ProjectLibrary::FileEntry::FileEntry(const Path& path, const WString& name, DirectoryEntry* parent)
 		: LibraryEntry(path, name, parent, LibraryEntryType::File), lastUpdateTime(0)
 	{ }
 
@@ -128,7 +128,7 @@ namespace BansheeEngine
 		{
 			if(FileSystem::isFile(entry->path))
 			{
-				ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
+				FileEntry* resEntry = static_cast<FileEntry*>(entry);
 
 				if (import)
 					reimportResourceInternal(resEntry);
@@ -138,7 +138,7 @@ namespace BansheeEngine
 			}
 			else
 			{
-				deleteResourceInternal(static_cast<ResourceEntry*>(entry));
+				deleteResourceInternal(static_cast<FileEntry*>(entry));
 			}
 		}
 		else if(entry->type == LibraryEntryType::Directory) // Check folder and all subfolders for modifications
@@ -188,14 +188,14 @@ namespace BansheeEngine
 						}
 						else
 						{
-							ResourceEntry* existingEntry = nullptr;
+							FileEntry* existingEntry = nullptr;
 							UINT32 idx = 0;
 							for(auto& child : currentDir->mChildren)
 							{
 								if(child->type == LibraryEntryType::File && child->path == filePath)
 								{
 									existingEntries[idx] = true;
-									existingEntry = static_cast<ResourceEntry*>(child);
+									existingEntry = static_cast<FileEntry*>(child);
 									break;
 								}
 
@@ -254,7 +254,7 @@ namespace BansheeEngine
 							if(child->type == LibraryEntryType::Directory)
 								deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
 							else if(child->type == LibraryEntryType::File)
-								deleteResourceInternal(static_cast<ResourceEntry*>(child));
+								deleteResourceInternal(static_cast<FileEntry*>(child));
 						}
 
 						toDelete.clear();
@@ -270,10 +270,10 @@ namespace BansheeEngine
 		}
 	}
 
-	ProjectLibrary::ResourceEntry* ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const Path& filePath, 
+	ProjectLibrary::FileEntry* ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const Path& filePath, 
 		const ImportOptionsPtr& importOptions, bool forceReimport)
 	{
-		ResourceEntry* newResource = bs_new<ResourceEntry>(filePath, filePath.getWTail(), parent);
+		FileEntry* newResource = bs_new<FileEntry>(filePath, filePath.getWTail(), parent);
 		parent->mChildren.push_back(newResource);
 
 		reimportResourceInternal(newResource, importOptions, forceReimport);
@@ -291,22 +291,26 @@ namespace BansheeEngine
 		return newEntry;
 	}
 
-	void ProjectLibrary::deleteResourceInternal(ResourceEntry* resource)
+	void ProjectLibrary::deleteResourceInternal(FileEntry* resource)
 	{
 		if(resource->meta != nullptr)
 		{
-			String uuid = resource->meta->getUUID();
-
-			Path path;
-			if (mResourceManifest->uuidToFilePath(uuid, path))
+			auto& resourceMetas = resource->meta->getResourceMetaData();
+			for(auto& entry : resourceMetas)
 			{
-				if(FileSystem::isFile(path))
-					FileSystem::remove(path);
+				String uuid = entry->getUUID();
 
-				mResourceManifest->unregisterResource(uuid);
-			}
+				Path path;
+				if (mResourceManifest->uuidToFilePath(uuid, path))
+				{
+					if (FileSystem::isFile(path))
+						FileSystem::remove(path);
+
+					mResourceManifest->unregisterResource(uuid);
+				}
 
-			mUUIDToPath.erase(uuid);
+				mUUIDToPath.erase(uuid);
+			}
 		}
 
 		Path metaPath = getMetaPath(resource->path);
@@ -339,7 +343,7 @@ namespace BansheeEngine
 			if(child->type == LibraryEntryType::Directory)
 				deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
 			else
-				deleteResourceInternal(static_cast<ResourceEntry*>(child));
+				deleteResourceInternal(static_cast<FileEntry*>(child));
 		}
 
 		DirectoryEntry* parent = directory->parent;
@@ -355,7 +359,7 @@ namespace BansheeEngine
 		bs_delete(directory);
 	}
 
-	void ProjectLibrary::reimportResourceInternal(ResourceEntry* resource, const ImportOptionsPtr& importOptions, bool forceReimport)
+	void ProjectLibrary::reimportResourceInternal(FileEntry* resource, const ImportOptionsPtr& importOptions, bool forceReimport)
 	{
 		Path metaPath = resource->path;
 		metaPath.setFilename(metaPath.getWFilename() + L".meta");
@@ -367,12 +371,19 @@ namespace BansheeEngine
 				FileDecoder fs(metaPath);
 				std::shared_ptr<IReflectable> loadedMeta = fs.decode();
 
-				if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectResourceMeta::getRTTIStatic()))
+				if(loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectFileMeta::getRTTIStatic()))
 				{
-					ProjectResourceMetaPtr resourceMeta = std::static_pointer_cast<ProjectResourceMeta>(loadedMeta);
-					resource->meta = resourceMeta;
+					ProjectFileMetaPtr fileMeta = std::static_pointer_cast<ProjectFileMeta>(loadedMeta);
+					resource->meta = fileMeta;
+
+					auto& resourceMetas = resource->meta->getResourceMetaData();
 
-					mUUIDToPath[resourceMeta->getUUID()] = resource->path;
+					if (resourceMetas.size() > 0)
+					{
+						mUUIDToPath[resourceMetas[0]->getUUID()] = resource->path;
+						for (auto& entry : resourceMetas)
+							mUUIDToPath[entry->getUUID()] = resource->path + entry->getUniqueName();
+					}
 				}
 			}
 		}
@@ -401,7 +412,8 @@ namespace BansheeEngine
 				// This can happen if library isn't properly saved before exiting the application.
 				if (resource->meta != nullptr)
 				{
-					mResourceManifest->registerResource(resource->meta->getUUID(), resource->path);
+					auto& resourceMetas = resource->meta->getResourceMetaData();
+					mResourceManifest->registerResource(resourceMetas[0]->getUUID(), resource->path);
 				}
 
 				// Don't load dependencies because we don't need them, but also because they might not be in the manifest
@@ -418,10 +430,10 @@ namespace BansheeEngine
 				{
 					ResourceMetaDataPtr subMeta = importedResource->getMetaData();
 					UINT32 typeId = importedResource->getTypeId();
-					resource->meta = ProjectResourceMeta::create(importedResource.getUUID(), typeId, subMeta, curImportOptions);
+					resource->meta = ProjectFileMeta::create(importedResource.getUUID(), typeId, subMeta, curImportOptions);
 				}
 				else
-					resource->meta = ProjectResourceMeta::create(importedResource.getUUID(), 0, nullptr, curImportOptions);
+					resource->meta = ProjectFileMeta::create(importedResource.getUUID(), 0, nullptr, curImportOptions);
 
 				FileEncoder fs(metaPath);
 				fs.encode(resource->meta.get());
@@ -467,17 +479,21 @@ namespace BansheeEngine
 		}
 	}
 
-	bool ProjectLibrary::isUpToDate(ResourceEntry* resource) const
+	bool ProjectLibrary::isUpToDate(FileEntry* resource) const
 	{
 		if(resource->meta == nullptr)
 			return false;
 
-		Path internalPath;
-		if(!mResourceManifest->uuidToFilePath(resource->meta->getUUID(), internalPath))
-			return false;
+		auto& resourceMetas = resource->meta->getResourceMetaData();
+		for (auto& resMeta : resourceMetas)
+		{
+			Path internalPath;
+			if (!mResourceManifest->uuidToFilePath(resMeta->getUUID(), internalPath))
+				return false;
 
-		if(!FileSystem::isFile(internalPath))
-			return false;
+			if (!FileSystem::isFile(internalPath))
+				return false;
+		}
 
 		std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(resource->path);
 		return lastModifiedTime <= resource->lastUpdateTime;
@@ -519,13 +535,25 @@ namespace BansheeEngine
 					{
 						if (child->type == LibraryEntryType::File)
 						{
-							ResourceEntry* childResEntry = static_cast<ResourceEntry*>(child);
-							for (auto& typeId : typeIds)
+							FileEntry* childFileEntry = static_cast<FileEntry*>(child);
+							if (childFileEntry->meta != nullptr)
 							{
-								if (childResEntry->meta != nullptr && childResEntry->meta->getTypeID() == typeId)
+								auto& resourceMetas = childFileEntry->meta->getResourceMetaData();
+								for (auto& typeId : typeIds)
 								{
-									foundEntries.push_back(child);
-									break;
+									bool found = false;
+									for (auto& resMeta : resourceMetas)
+									{
+										if (resMeta->getTypeID() == typeId)
+										{
+											foundEntries.push_back(child);
+											found = true;
+											break;
+										}
+									}
+
+									if (found)
+										break;
 								}
 							}
 						}
@@ -590,13 +618,86 @@ namespace BansheeEngine
 					}
 				}
 			}
-			else
-				break;
+			else // Found file
+			{
+				// If this is next to last element, next entry is assumed to be a sub-resource name, which we ignore
+				if (numElems >= 2 && idx == (numElems - 2))
+					return current;
+				else
+					break; // Not a valid path
+			}
 		}
 
 		return nullptr;
 	}
 
+	ProjectResourceMetaPtr ProjectLibrary::findResourceMeta(const Path& path) const
+	{
+		UINT32 numElems = path.getNumDirectories() + (path.isFile() ? 1 : 0);
+
+		// Check if it is a subresource path
+		if(numElems > 1)
+		{
+			Path filePath = path;
+			filePath.makeParent();
+
+			LibraryEntry* entry = findEntry(filePath);
+			if (entry == nullptr)
+				return nullptr;
+
+			// Entry is a subresource
+			if (entry->type == LibraryEntryType::File)
+			{
+				FileEntry* fileEntry = static_cast<FileEntry*>(entry);
+				if (fileEntry->meta == nullptr)
+					return nullptr;
+
+				auto& resourceMetas = fileEntry->meta->getResourceMetaData();
+				for(auto& resMeta : resourceMetas)
+				{
+					if (resMeta->getUniqueName() == path.getWTail())
+						return resMeta;
+				}
+
+				// Found the file but no subresource or meta information
+				return nullptr;
+			}
+			else // Entry not a subresource
+			{
+				DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
+				for (auto& child : dirEntry->mChildren)
+				{
+					if (Path::comparePathElem(path.getWTail(), child->elementName))
+					{
+						if (child->type == LibraryEntryType::File)
+						{
+							FileEntry* fileEntry = static_cast<FileEntry*>(child);
+							if (fileEntry->meta == nullptr)
+								return nullptr;
+
+							return fileEntry->meta->getResourceMetaData()[0];
+						}
+					}
+				}
+
+				return nullptr;
+			}
+		}
+
+		// Not a subresource path, load directly
+		{
+			LibraryEntry* entry = findEntry(path);
+			if (entry == nullptr || entry->type == LibraryEntryType::Directory)
+				return nullptr;
+
+			FileEntry* fileEntry = static_cast<FileEntry*>(entry);
+			if (fileEntry->meta == nullptr)
+				return nullptr;
+
+			return fileEntry->meta->getResourceMetaData()[0];
+		}
+	}
+
 	Path ProjectLibrary::uuidToPath(const String& uuid) const
 	{
 		auto iterFind = mUUIDToPath.find(uuid);
@@ -709,7 +810,7 @@ namespace BansheeEngine
 			if (!mResourcesFolder.includes(newFullPath))
 			{
 				if(oldEntry->type == LibraryEntryType::File)
-					deleteResourceInternal(static_cast<ResourceEntry*>(oldEntry));
+					deleteResourceInternal(static_cast<FileEntry*>(oldEntry));
 				else if(oldEntry->type == LibraryEntryType::Directory)
 					deleteDirectoryInternal(static_cast<DirectoryEntry*>(oldEntry));
 			}
@@ -717,18 +818,27 @@ namespace BansheeEngine
 			{
 				onEntryRemoved(oldEntry->path);
 
-				ResourceEntry* resEntry = nullptr;
+				FileEntry* fileEntry = nullptr;
 				if (oldEntry->type == LibraryEntryType::File)
 				{
-					resEntry = static_cast<ResourceEntry*>(oldEntry);
-					removeDependencies(resEntry);
+					fileEntry = static_cast<FileEntry*>(oldEntry);
+					removeDependencies(fileEntry);
 
 					// Update uuid <-> path mapping
-					if(resEntry->meta != nullptr)
+					if(fileEntry->meta != nullptr)
 					{
-						const String& UUID = resEntry->meta->getUUID();
+						auto& resourceMetas = fileEntry->meta->getResourceMetaData();
 
-						mUUIDToPath[UUID] = newFullPath;
+						if (resourceMetas.size() > 0)
+						{
+							mUUIDToPath[resourceMetas[0]->getUUID()] = newFullPath;
+
+							for (auto& resMeta : resourceMetas)
+							{
+								const String& UUID = resMeta->getUUID();
+								mUUIDToPath[UUID] = newFullPath + resMeta->getUniqueName();
+							}
+						}
 					}
 				}
 
@@ -783,7 +893,7 @@ namespace BansheeEngine
 
 				onEntryAdded(oldEntry->path);
 
-				if (resEntry != nullptr)
+				if (fileEntry != nullptr)
 				{
 					reimportDependants(oldFullPath);
 					reimportDependants(newFullPath);
@@ -838,7 +948,7 @@ namespace BansheeEngine
 		if (FileSystem::isFile(newFullPath))
 		{
 			assert(oldEntry->type == LibraryEntryType::File);
-			ResourceEntry* oldResEntry = static_cast<ResourceEntry*>(oldEntry);
+			FileEntry* oldResEntry = static_cast<FileEntry*>(oldEntry);
 
 			ImportOptionsPtr importOptions;
 			if (oldResEntry->meta != nullptr)
@@ -872,7 +982,7 @@ namespace BansheeEngine
 
 					if (child->type == LibraryEntryType::File)
 					{
-						ResourceEntry* childResEntry = static_cast<ResourceEntry*>(child);
+						FileEntry* childResEntry = static_cast<FileEntry*>(child);
 
 						ImportOptionsPtr importOptions;
 						if (childResEntry->meta != nullptr)
@@ -905,7 +1015,7 @@ namespace BansheeEngine
 		if(entry != nullptr)
 		{
 			if(entry->type == LibraryEntryType::File)
-				deleteResourceInternal(static_cast<ResourceEntry*>(entry));
+				deleteResourceInternal(static_cast<FileEntry*>(entry));
 			else if(entry->type == LibraryEntryType::Directory)
 				deleteDirectoryInternal(static_cast<DirectoryEntry*>(entry));
 		}
@@ -918,7 +1028,7 @@ namespace BansheeEngine
 		{
 			if (entry->type == LibraryEntryType::File)
 			{
-				ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
+				FileEntry* resEntry = static_cast<FileEntry*>(entry);
 				reimportResourceInternal(resEntry, importOptions, forceReimport);
 			}
 		}
@@ -931,7 +1041,7 @@ namespace BansheeEngine
 		if (entry == nullptr || entry->type == LibraryEntryType::Directory)
 			return;
 
-		ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
+		FileEntry* resEntry = static_cast<FileEntry*>(entry);
 		if (resEntry->meta == nullptr)
 			return;
 
@@ -944,9 +1054,9 @@ namespace BansheeEngine
 		fs.encode(resEntry->meta.get());
 	}
 
-	Vector<ProjectLibrary::ResourceEntry*> ProjectLibrary::getResourcesForBuild() const
+	Vector<ProjectLibrary::FileEntry*> ProjectLibrary::getResourcesForBuild() const
 	{
-		Vector<ResourceEntry*> output;
+		Vector<FileEntry*> output;
 
 		Stack<DirectoryEntry*> todo;
 		todo.push(mRootEntry);
@@ -960,7 +1070,7 @@ namespace BansheeEngine
 			{
 				if (child->type == LibraryEntryType::File)
 				{
-					ResourceEntry* resEntry = static_cast<ResourceEntry*>(child);
+					FileEntry* resEntry = static_cast<FileEntry*>(child);
 					if (resEntry->meta != nullptr && resEntry->meta->getIncludeInBuild())
 						output.push_back(resEntry);
 				}
@@ -976,17 +1086,11 @@ namespace BansheeEngine
 
 	HResource ProjectLibrary::load(const Path& path)
 	{
-		LibraryEntry* entry = findEntry(path);
-
-		if (entry == nullptr || entry->type == LibraryEntryType::Directory)
-			return HResource();
-
-		ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
-		if (resEntry->meta == nullptr)
+		ProjectResourceMetaPtr meta = findResourceMeta(path);
+		if (meta == nullptr)
 			return HResource();
 
-		String resUUID = resEntry->meta->getUUID();
-
+		String resUUID = meta->getUUID();
 		return gResources().loadFromUUID(resUUID);
 	}
 
@@ -1192,7 +1296,7 @@ namespace BansheeEngine
 			{
 				if(child->type == LibraryEntryType::File)
 				{
-					ResourceEntry* resEntry = static_cast<ResourceEntry*>(child);
+					FileEntry* resEntry = static_cast<FileEntry*>(child);
 					
 					if (FileSystem::isFile(resEntry->path))
 					{
@@ -1206,16 +1310,26 @@ namespace BansheeEngine
 								FileDecoder fs(metaPath);
 								std::shared_ptr<IReflectable> loadedMeta = fs.decode();
 
-								if (loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectResourceMeta::getRTTIStatic()))
+								if (loadedMeta != nullptr && loadedMeta->isDerivedFrom(ProjectFileMeta::getRTTIStatic()))
 								{
-									ProjectResourceMetaPtr resourceMeta = std::static_pointer_cast<ProjectResourceMeta>(loadedMeta);
-									resEntry->meta = resourceMeta;
+									ProjectFileMetaPtr fileMeta = std::static_pointer_cast<ProjectFileMeta>(loadedMeta);
+									resEntry->meta = fileMeta;
 								}
 							}
 						}
 
 						if (resEntry->meta != nullptr)
-							mUUIDToPath[resEntry->meta->getUUID()] = resEntry->path;
+						{
+							auto& resourceMetas = resEntry->meta->getResourceMetaData();
+
+							if (resourceMetas.size() > 0)
+							{
+								mUUIDToPath[resourceMetas[0]->getUUID()] = resEntry->path;
+
+								for (auto& entry : resourceMetas)
+									mUUIDToPath[entry->getUUID()] = resEntry->path + entry->getUniqueName();
+							}
+						}
 
 						addDependencies(resEntry);
 					}
@@ -1237,7 +1351,7 @@ namespace BansheeEngine
 		{
 			if (deletedEntry->type == LibraryEntryType::File)
 			{
-				ResourceEntry* resEntry = static_cast<ResourceEntry*>(deletedEntry);
+				FileEntry* resEntry = static_cast<FileEntry*>(deletedEntry);
 				deleteResourceInternal(resEntry);
 			}
 			else
@@ -1295,32 +1409,36 @@ namespace BansheeEngine
 		mRootEntry = nullptr;
 	}
 
-	Vector<Path> ProjectLibrary::getImportDependencies(const ResourceEntry* entry)
+	Vector<Path> ProjectLibrary::getImportDependencies(const FileEntry* entry)
 	{
 		Vector<Path> output;
 
 		if (entry->meta == nullptr)
 			return output;
 
-		if (entry->meta->getTypeID() == TID_Shader)
+		auto& resourceMetas = entry->meta->getResourceMetaData();
+		for(auto& resMeta : resourceMetas)
 		{
-			SPtr<ShaderMetaData> metaData = std::static_pointer_cast<ShaderMetaData>(entry->meta->getResourceMetaData());
+			if (resMeta->getTypeID() == TID_Shader)
+			{
+				SPtr<ShaderMetaData> metaData = std::static_pointer_cast<ShaderMetaData>(resMeta->getResourceMetaData());
 
-			for (auto& include : metaData->includes)
-				output.push_back(include);
+				for (auto& include : metaData->includes)
+					output.push_back(include);
+			}
 		}
 
 		return output;
 	}
 
-	void ProjectLibrary::addDependencies(const ResourceEntry* entry)
+	void ProjectLibrary::addDependencies(const FileEntry* entry)
 	{
 		Vector<Path> dependencies = getImportDependencies(entry);
 		for (auto& dependency : dependencies)
 			mDependencies[dependency].push_back(entry->path);
 	}
 
-	void ProjectLibrary::removeDependencies(const ResourceEntry* entry)
+	void ProjectLibrary::removeDependencies(const FileEntry* entry)
 	{
 		Vector<Path> dependencies = getImportDependencies(entry);
 		for (auto& dependency : dependencies)
@@ -1349,7 +1467,7 @@ namespace BansheeEngine
 			LibraryEntry* entry = findEntry(dependency);
 			if (entry != nullptr && entry->type == LibraryEntryType::File)
 			{
-				ResourceEntry* resEntry = static_cast<ResourceEntry*>(entry);
+				FileEntry* resEntry = static_cast<FileEntry*>(entry);
 
 				ImportOptionsPtr importOptions;
 				if (resEntry->meta != nullptr)

+ 74 - 5
BansheeEditor/Source/BsProjectResourceMeta.cpp

@@ -6,20 +6,19 @@
 namespace BansheeEngine
 {
 	ProjectResourceMeta::ProjectResourceMeta(const ConstructPrivately& dummy)
-		:mIncludeInBuild(false), mTypeId(0)
+		:mTypeId(0)
 	{
 
 	}
 
-	ProjectResourceMetaPtr ProjectResourceMeta::create(const String& uuid, UINT32 typeId, const ResourceMetaDataPtr& resourceMetaData,
-		const ImportOptionsPtr& importOptions)
+	ProjectResourceMetaPtr ProjectResourceMeta::create(const WString name, const String& uuid, UINT32 typeId,
+		const ResourceMetaDataPtr& resourceMetaData)
 	{
 		ProjectResourceMetaPtr meta = bs_shared_ptr_new<ProjectResourceMeta>(ConstructPrivately());
+		meta->mName = name;
 		meta->mUUID = uuid;
 		meta->mTypeId = typeId;
 		meta->mResourceMeta = resourceMetaData;
-		meta->mImportOptions = importOptions;
-		meta->mIncludeInBuild = false;
 
 		return meta;
 	}
@@ -42,4 +41,74 @@ namespace BansheeEngine
 	{
 		return ProjectResourceMeta::getRTTIStatic();
 	}
+
+	ProjectFileMeta::ProjectFileMeta(const ConstructPrivately& dummy)
+		:mIncludeInBuild(false)
+	{
+
+	}
+
+	ProjectFileMetaPtr ProjectFileMeta::create(const ImportOptionsPtr& importOptions)
+	{
+		ProjectFileMetaPtr meta = bs_shared_ptr_new<ProjectFileMeta>(ConstructPrivately());
+		meta->mImportOptions = importOptions;
+		meta->mIncludeInBuild = false;
+
+		return meta;
+	}
+
+	void ProjectFileMeta::add(const ProjectResourceMetaPtr& resourceMeta)
+	{
+		mResourceMetaData.push_back(resourceMeta);
+	}
+
+	void ProjectFileMeta::remove(const String& UUID)
+	{
+		auto iterFind = std::find_if(mResourceMetaData.begin(), mResourceMetaData.end(),
+			[&](auto& x) { return x.mUUID == UUID; });
+
+		if (iterFind != mResourceMetaData.end())
+			mResourceMetaData.erase(iterFind);
+	}
+
+	bool ProjectFileMeta::hasTypeId(UINT32 typeId) const
+	{
+		for(auto& entry : mResourceMetaData)
+		{
+			if (entry->getTypeID() == typeId)
+				return true;
+		}
+
+		return false;
+	}
+
+	bool ProjectFileMeta::hasUUID(const String& uuid) const
+	{
+		for (auto& entry : mResourceMetaData)
+		{
+			if (entry->getUUID() == uuid)
+				return true;
+		}
+
+		return false;
+	}
+
+	ProjectFileMetaPtr ProjectFileMeta::createEmpty()
+	{
+		return bs_shared_ptr_new<ProjectFileMeta>(ConstructPrivately());
+	}
+
+	/************************************************************************/
+	/* 								RTTI		                     		*/
+	/************************************************************************/
+
+	RTTITypeBase* ProjectFileMeta::getRTTIStatic()
+	{
+		return ProjectFileMetaRTTI::instance();
+	}
+
+	RTTITypeBase* ProjectFileMeta::getRTTI() const
+	{
+		return ProjectFileMeta::getRTTIStatic();
+	}
 }

+ 3 - 8
BansheeEditor/Source/BsSelection.cpp

@@ -59,14 +59,9 @@ namespace BansheeEngine
 		Vector<String> UUIDs;
 		for (auto& path : mSelectedResourcePaths)
 		{
-			ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(path);
-			if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
-			{
-				ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
-
-				if (resEntry->meta != nullptr)
-					UUIDs.push_back(resEntry->meta->getUUID());
-			}
+			ProjectResourceMetaPtr meta = gProjectLibrary().findResourceMeta(path);
+			if (meta != nullptr)
+				UUIDs.push_back(meta->getUUID());
 		}
 
 		return UUIDs;

+ 25 - 11
MBansheeEditor/ProjectLibrary.cs

@@ -478,6 +478,31 @@ namespace BansheeEditor
         /// </summary>
         public ImportOptions Options { get { return Internal_GetImportOptions(mCachedPtr); } }
 
+        /// <summary>
+        /// Returns meta-data for all resources part of the file represented by this object.
+        /// </summary>
+        public ResourceMeta[] ResourceMetas { get { return Internal_GetResourceMetas(mCachedPtr); } }
+
+        /// <summary>
+        /// Determines will the resource be included in the project build.
+        /// </summary>
+        public bool IncludeInBuild { get { return Internal_GetIncludeInBuild(mCachedPtr); } }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern ImportOptions Internal_GetImportOptions(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern ResourceMeta[] Internal_GetResourceMetas(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_GetIncludeInBuild(IntPtr thisPtr);
+    }
+
+    /// <summary>
+    /// Contains meta-data for a resource in the ProjectLibrary.
+    /// </summary>
+    public class ResourceMeta : ScriptObject
+    {
         /// <summary>
         /// Unique identifier of the resource.
         /// </summary>
@@ -493,14 +518,6 @@ namespace BansheeEditor
         /// </summary>
         public ResourceType ResType { get { return Internal_GetResourceType(mCachedPtr); } }
 
-        /// <summary>
-        /// Determines will the resource be included in the project build.
-        /// </summary>
-        public bool IncludeInBuild { get { return Internal_GetIncludeInBuild(mCachedPtr); } }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern ImportOptions Internal_GetImportOptions(IntPtr thisPtr);
-
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern string Internal_GetUUID(IntPtr thisPtr);
 
@@ -509,8 +526,5 @@ namespace BansheeEditor
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern ResourceType Internal_GetResourceType(IntPtr thisPtr);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern bool Internal_GetIncludeInBuild(IntPtr thisPtr);
     }
 }

+ 4 - 2
MBansheeEngine/Resources.cs

@@ -20,7 +20,9 @@ namespace BansheeEngine
         /// </summary>
         /// <typeparam name="T">Type of the resource.</typeparam>
         /// <param name="path">Path of the resource, relative to game directory. If running from editor this will be
-        ///                    the same location as resource location in the project library.</param>
+        ///                    the same location as resource location in the project library. If a sub-resource within
+        ///                    a file is needed, append the name of the subresource to the path (e.g. 
+        ///                    mymesh.fbx/my_animation).</param>
         /// <param name="keepLoaded">If true the system will keep the resource loaded even when it goes out of scope.
         ///                          You must call <see cref="Release(Resource)"/> in order to allow the resource to be
         ///                          unloaded (it must be called once for each corresponding load). </param>
@@ -85,7 +87,7 @@ namespace BansheeEngine
 
         /// <summary>
         /// Unloads all resources that are no longer referenced. This only applies to resources loaded with "keepLoaded"
-        /// parameter, as all other resources will be loaded when they go out of scope.
+        /// parameter, as all other resources will be unloaded when they go out of scope.
         /// </summary>
         public static void UnloadUnused()
         {

+ 38 - 23
SBansheeEditor/Include/BsScriptProjectLibrary.h

@@ -9,45 +9,39 @@
 
 namespace BansheeEngine
 {
-	/**
-	 * @brief	Interop class between C++ & CLR for ProjectLibrary.
-	 */
+	/**	Interop class between C++ & CLR for ProjectLibrary. */
 	class BS_SCR_BED_EXPORT ScriptProjectLibrary : public ScriptObject<ScriptProjectLibrary>
 	{
 	public:
 		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "ProjectLibrary")
 
-		/**
-		 * @brief	Initializes the project library callbacks. Must be called on library load.
-		 */
+		/**	Initializes the project library callbacks. Must be called on library load. */
 		void static startUp();
 
-		/**
-		 * @brief	Cleans up project library callbacks. Must be called before library shutdown.
-		 */
+		/**	Cleans up project library callbacks. Must be called before library shutdown. */
 		void static shutDown();
 
 	private:
 		ScriptProjectLibrary(MonoObject* instance);
 
 		/**
-		 * @brief	Triggered when a new entry has been added to the library.
+		 * Triggered when a new entry has been added to the library.
 		 *
-		 * @param	path	Absolute path to the new entry.
+		 * @param[in]	path	Absolute path to the new entry.
 		 */
 		static void onEntryAdded(const Path& path);
 
 		/**
-		 * @brief	Triggered when a new entry has been removed to the library.
+		 * Triggered when a new entry has been removed to the library.
 		 *
-		 * @param	path	Absolute path to the removed entry.
+		 * @param[in]	path	Absolute path to the removed entry.
 		 */
 		static void onEntryRemoved(const Path& path);
 
 		/**
-		 * @brief	Triggered when an entry was (re) imported in the library.
+		 * Triggered when an entry was (re) imported in the library.
 		 *
-		 * @param	path	Absolute path to the imported entry.
+		 * @param[in]	path	Absolute path to the imported entry.
 		 */
 		static void onEntryImported(const Path& path);
 
@@ -83,10 +77,7 @@ namespace BansheeEngine
 		static void internal_SetIncludeInBuild(MonoString* path, bool include);
 	};
 
-	/**
-	 * @brief	Base class for C++/CLR interop objects used for wrapping
-	 *			LibraryEntry implementations.
-	 */
+	/**	Base class for C++/CLR interop objects used for wrapping LibraryEntry implementations. */
 	class BS_SCR_BED_EXPORT ScriptLibraryEntryBase : public ScriptObjectBase
 	{
 	public:
@@ -157,16 +148,40 @@ namespace BansheeEngine
 		 * @brief	Creates a new interop object that wraps the provided
 		 *			native resource entry object.
 		 */
-		static MonoObject* create(const ProjectLibrary::ResourceEntry* entry);
+		static MonoObject* create(const ProjectLibrary::FileEntry* entry);
 
 	private:
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static MonoObject* internal_GetImportOptions(ScriptFileEntry* thisPtr);
-		static MonoString* internal_GetUUID(ScriptFileEntry* thisPtr);
-		static MonoObject* internal_GetIcon(ScriptFileEntry* thisPtr);
-		static ScriptResourceType internal_GetResourceType(ScriptFileEntry* thisPtr);
+		static MonoArray* internal_GetResourceMetas(ScriptFileEntry* thisPtr);
 		static bool internal_GetIncludeInBuild(ScriptFileEntry* thisPtr);
 	};
+
+	/**
+	 * @brief	Interop class between C++ & CLR for ResourceMeta.
+	 */
+	class BS_SCR_BED_EXPORT ScriptResourceMeta : public ScriptObject <ScriptResourceMeta>
+	{
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "ResourceMeta")
+
+		ScriptResourceMeta(MonoObject* instance, const Path& assetPath);
+
+		/**
+		 * Creates a new interop object that wraps the native resource meta object for the resource at the specified path.
+		 */
+		static MonoObject* create(const Path& assetPath);
+
+	private:
+		Path mAssetPath;
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoString* internal_GetUUID(ScriptResourceMeta* thisPtr);
+		static MonoObject* internal_GetIcon(ScriptResourceMeta* thisPtr);
+		static ScriptResourceType internal_GetResourceType(ScriptResourceMeta* thisPtr);
+	};
 }

+ 8 - 2
SBansheeEditor/Source/BsEditorResourceLoader.cpp

@@ -15,14 +15,20 @@ namespace BansheeEngine
 		if (entry == nullptr || entry->type == ProjectLibrary::LibraryEntryType::Directory)
 			return HResource();
 
-		ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+		ProjectLibrary::FileEntry* resEntry = static_cast<ProjectLibrary::FileEntry*>(entry);
 		if (resEntry->meta == nullptr)
 		{
 			LOGWRN("Missing .meta file for resource at path: \"" + path.toString() + "\".");
 			return HResource();
 		}
 
-		String resUUID = resEntry->meta->getUUID();
+		// Note: Calling both findEntry and findResourceMeta is a bit redundant since they do a lot of the same work, and 
+		// this could be optimized so only one of them is called.
+		ProjectResourceMetaPtr meta = gProjectLibrary().findResourceMeta(path);
+		if (meta == nullptr)
+			LOGWRN("Unable to load resource at path: \"" + path.toString() + "\". File not found. ");
+
+		String resUUID = meta->getUUID();
 
 		if (resEntry->meta->getIncludeInBuild())
 		{

+ 1 - 7
SBansheeEditor/Source/BsGUIResourceField.cpp

@@ -288,13 +288,7 @@ namespace BansheeEngine
 		{
 			Path path = draggedResources->resourcePaths[i];
 
-			ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(path);
-			if (libEntry == nullptr || libEntry->type == ProjectLibrary::LibraryEntryType::Directory)
-				continue;
-
-			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(libEntry);
-
-			ProjectResourceMetaPtr meta = resEntry->meta;
+			ProjectResourceMetaPtr meta = gProjectLibrary().findResourceMeta(path);
 			if (meta == nullptr)
 				continue;
 

+ 1 - 7
SBansheeEditor/Source/BsGUITextureField.cpp

@@ -266,13 +266,7 @@ namespace BansheeEngine
 		{
 			Path path = draggedResources->resourcePaths[i];
 
-			ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(draggedResources->resourcePaths[i]);
-			if (libEntry == nullptr || libEntry->type == ProjectLibrary::LibraryEntryType::Directory)
-				continue;
-
-			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(libEntry);
-
-			ProjectResourceMetaPtr meta = resEntry->meta;
+			ProjectResourceMetaPtr meta = gProjectLibrary().findResourceMeta(draggedResources->resourcePaths[i]);
 			if (meta == nullptr || meta->getTypeID() != TID_Texture)
 				continue;
 

+ 13 - 11
SBansheeEditor/Source/BsScriptBuildManager.cpp

@@ -225,7 +225,7 @@ namespace BansheeEngine
 		UnorderedSet<Path> usedResources;
 
 		// Get all resources manually included in build
-		Vector<ProjectLibrary::ResourceEntry*> buildResources = gProjectLibrary().getResourcesForBuild();
+		Vector<ProjectLibrary::FileEntry*> buildResources = gProjectLibrary().getResourcesForBuild();
 		for (auto& entry : buildResources)
 		{
 			if (entry->meta == nullptr)
@@ -234,11 +234,15 @@ namespace BansheeEngine
 				continue;
 			}
 
-			Path resourcePath;
-			if (gResources().getFilePathFromUUID(entry->meta->getUUID(), resourcePath))
-				usedResources.insert(resourcePath);
-			else
-				LOGWRN("Cannot include resource in build, missing imported asset for: " + entry->path.toString());
+			auto& resourceMetas = entry->meta->getResourceMetaData();
+			for(auto& resMeta : resourceMetas)
+			{
+				Path resourcePath;
+				if (gResources().getFilePathFromUUID(resMeta->getUUID(), resourcePath))
+					usedResources.insert(resourcePath);
+				else
+					LOGWRN("Cannot include resource in build, missing imported asset for: " + entry->path.toString());
+			}
 		}
 
 		// Include main scene
@@ -300,16 +304,14 @@ namespace BansheeEngine
 			if (sourcePath.isEmpty()) // Resource not part of library, meaning its built-in and we don't need to copy those here
 				continue;
 
-			ProjectLibrary::LibraryEntry* libEntry = gProjectLibrary().findEntry(sourcePath);
-			assert(libEntry != nullptr && libEntry->type == ProjectLibrary::LibraryEntryType::File);
-
-			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(libEntry);
+			ProjectResourceMetaPtr resMeta = gProjectLibrary().findResourceMeta(sourcePath);
+			assert(resMeta != nullptr);
 
 			Path destPath = outputPath;
 			destPath.setFilename(entry.getFilename());
 
 			// If resource is prefab make sure to update it in case any of the prefabs it is referencing changed
-			if (resEntry->meta->getTypeID() == TID_Prefab)
+			if (resMeta->getTypeID() == TID_Prefab)
 			{
 				bool reload = gResources().isLoaded(uuid);
 

+ 3 - 7
SBansheeEditor/Source/BsScriptEditorApplication.cpp

@@ -219,15 +219,11 @@ namespace BansheeEngine
 		Path nativePath = MonoUtil::monoToWString(path);
 		HSceneObject sceneRoot = gSceneManager().getRootNode();
 		
-		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(nativePath);
+		ProjectResourceMetaPtr resMeta = gProjectLibrary().findResourceMeta(nativePath);
 		HPrefab scene;
-		if (entry != nullptr)
+		if (resMeta != nullptr)
 		{
-			if (entry->type == ProjectLibrary::LibraryEntryType::Directory)
-				return nullptr;
-
-			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
-			if (resEntry->meta == nullptr || resEntry->meta->getTypeID() != TID_Prefab)
+			if (resMeta->getTypeID() != TID_Prefab)
 				return nullptr;
 
 			scene = static_resource_cast<Prefab>(gProjectLibrary().load(nativePath));

+ 76 - 44
SBansheeEditor/Source/BsScriptProjectLibrary.cpp

@@ -90,17 +90,8 @@ namespace BansheeEngine
 	MonoObject* ScriptProjectLibrary::internal_Load(MonoString* path)
 	{
 		Path resourcePath = MonoUtil::monoToWString(path);
+		HResource resource = gProjectLibrary().load(resourcePath);
 
-		HResource resource;
-		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(resourcePath);
-		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
-		{
-			ProjectLibrary::ResourceEntry* resEntry = static_cast <ProjectLibrary::ResourceEntry*>(entry);
-
-			if (resEntry->meta != nullptr)
-				resource = gResources().loadFromUUID(resEntry->meta->getUUID());
-		}
-		
 		if (!resource)
 			return nullptr;
 
@@ -146,7 +137,7 @@ namespace BansheeEngine
 			return nullptr;
 
 		if (entry->type == ProjectLibrary::LibraryEntryType::File)
-			return ScriptFileEntry::create(static_cast<ProjectLibrary::ResourceEntry*>(entry));
+			return ScriptFileEntry::create(static_cast<ProjectLibrary::FileEntry*>(entry));
 		else
 			return ScriptDirectoryEntry::create(static_cast<ProjectLibrary::DirectoryEntry*>(entry));
 	}
@@ -198,7 +189,7 @@ namespace BansheeEngine
 			MonoObject* managedEntry = nullptr;
 
 			if (entry->type == ProjectLibrary::LibraryEntryType::File)
-				managedEntry = ScriptFileEntry::create(static_cast<ProjectLibrary::ResourceEntry*>(entry));
+				managedEntry = ScriptFileEntry::create(static_cast<ProjectLibrary::FileEntry*>(entry));
 			else
 				managedEntry = ScriptDirectoryEntry::create(static_cast<ProjectLibrary::DirectoryEntry*>(entry));
 
@@ -385,7 +376,7 @@ namespace BansheeEngine
 			MonoObject* managedChildEntry = nullptr;
 
 			if (childEntry->type == ProjectLibrary::LibraryEntryType::File)
-				managedChildEntry = ScriptFileEntry::create(static_cast<ProjectLibrary::ResourceEntry*>(childEntry));
+				managedChildEntry = ScriptFileEntry::create(static_cast<ProjectLibrary::FileEntry*>(childEntry));
 			else
 				managedChildEntry = ScriptDirectoryEntry::create(static_cast<ProjectLibrary::DirectoryEntry*>(childEntry));
 
@@ -401,7 +392,7 @@ namespace BansheeEngine
 		mAssetPath = assetPath;
 	}
 
-	MonoObject* ScriptFileEntry::create(const ProjectLibrary::ResourceEntry* entry)
+	MonoObject* ScriptFileEntry::create(const ProjectLibrary::FileEntry* entry)
 	{
 		MonoObject* managedInstance = metaData.scriptClass->createInstance();
 		bs_new<ScriptFileEntry>(managedInstance, entry->path);
@@ -412,9 +403,7 @@ namespace BansheeEngine
 	void ScriptFileEntry::initRuntimeData()
 	{
 		metaData.scriptClass->addInternalCall("Internal_GetImportOptions", &ScriptFileEntry::internal_GetImportOptions);
-		metaData.scriptClass->addInternalCall("Internal_GetUUID", &ScriptFileEntry::internal_GetUUID);
-		metaData.scriptClass->addInternalCall("Internal_GetIcon", &ScriptFileEntry::internal_GetIcon);
-		metaData.scriptClass->addInternalCall("Internal_GetResourceType", &ScriptFileEntry::internal_GetResourceType);
+		metaData.scriptClass->addInternalCall("Internal_GetResourceMetas", &ScriptFileEntry::internal_GetResourceMetas);
 		metaData.scriptClass->addInternalCall("Internal_GetIncludeInBuild", &ScriptFileEntry::internal_GetIncludeInBuild);
 	}
 
@@ -424,7 +413,7 @@ namespace BansheeEngine
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
 			return nullptr;
 
-		ProjectLibrary::ResourceEntry* fileEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+		ProjectLibrary::FileEntry* fileEntry = static_cast<ProjectLibrary::FileEntry*>(entry);
 
 		if (fileEntry->meta != nullptr)
 			return ScriptImportOptions::create(fileEntry->meta->getImportOptions());
@@ -432,51 +421,94 @@ namespace BansheeEngine
 			return nullptr;
 	}
 
-	MonoString* ScriptFileEntry::internal_GetUUID(ScriptFileEntry* thisPtr)
+	MonoArray* ScriptFileEntry::internal_GetResourceMetas(ScriptFileEntry* thisPtr)
+	{
+		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
+		if (entry != nullptr && entry->type == ProjectLibrary::LibraryEntryType::File)
+		{
+			ProjectLibrary::FileEntry* fileEntry = static_cast<ProjectLibrary::FileEntry*>(entry);
+
+			if (fileEntry->meta != nullptr)
+			{
+				auto& resourceMetas = fileEntry->meta->getResourceMetaData();
+				UINT32 numElements = (UINT32)resourceMetas.size();
+
+				ScriptArray output = ScriptArray::create<ScriptResourceMeta>(numElements);
+				if (numElements > 0)
+				{
+					// Don't give the primary resource a subresource name
+					output.set(0, ScriptResourceMeta::create(thisPtr->getAssetPath()));
+
+					for (UINT32 i = 1; i < numElements; i++)
+					{
+						Path assetPath = thisPtr->getAssetPath() + resourceMetas[i]->getUniqueName();
+						output.set(i, ScriptResourceMeta::create(assetPath));
+					}
+				}
+
+				return output.getInternal();
+			}
+		}
+			
+		return ScriptArray::create<ScriptResourceMeta>(0).getInternal();
+	}
+
+	bool ScriptFileEntry::internal_GetIncludeInBuild(ScriptFileEntry* thisPtr)
 	{
 		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
 		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
-			return nullptr;
+			return false;
 
-		ProjectLibrary::ResourceEntry* fileEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+		ProjectLibrary::FileEntry* fileEntry = static_cast<ProjectLibrary::FileEntry*>(entry);
 
 		if (fileEntry->meta != nullptr)
-			return MonoUtil::stringToMono(fileEntry->meta->getUUID());
-		else
-			return nullptr;
+			return fileEntry->meta->getIncludeInBuild();
+
+		return false;
 	}
 
-	MonoObject* ScriptFileEntry::internal_GetIcon(ScriptFileEntry* thisPtr)
+	ScriptResourceMeta::ScriptResourceMeta(MonoObject* instance, const Path& assetPath)
+		:ScriptObject(instance)
 	{
-		// TODO - Icons not supported yet
-		return nullptr;
+		mAssetPath = assetPath;
 	}
 
-	ScriptResourceType ScriptFileEntry::internal_GetResourceType(ScriptFileEntry* thisPtr)
+	MonoObject* ScriptResourceMeta::create(const Path& assetPath)
 	{
-		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
-		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
-			return ScriptResourceType::Undefined;
-
-		ProjectLibrary::ResourceEntry* fileEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+		MonoObject* managedInstance = metaData.scriptClass->createInstance();
+		bs_new<ScriptResourceMeta>(managedInstance, assetPath);
 
-		if (fileEntry->meta != nullptr)
-			return ScriptResource::getTypeFromTypeId(fileEntry->meta->getTypeID());
+		return managedInstance;
+	}
 
-		return ScriptResourceType::Undefined;
+	void ScriptResourceMeta::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_GetUUID", &ScriptResourceMeta::internal_GetUUID);
+		metaData.scriptClass->addInternalCall("Internal_GetIcon", &ScriptResourceMeta::internal_GetIcon);
+		metaData.scriptClass->addInternalCall("Internal_GetResourceType", &ScriptResourceMeta::internal_GetResourceType);
 	}
 
-	bool ScriptFileEntry::internal_GetIncludeInBuild(ScriptFileEntry* thisPtr)
+	MonoString* ScriptResourceMeta::internal_GetUUID(ScriptResourceMeta* thisPtr)
 	{
-		ProjectLibrary::LibraryEntry* entry = gProjectLibrary().findEntry(thisPtr->getAssetPath());
-		if (entry == nullptr || entry->type != ProjectLibrary::LibraryEntryType::File)
-			return false;
+		ProjectResourceMetaPtr meta = gProjectLibrary().findResourceMeta(thisPtr->mAssetPath);
+		if (meta == nullptr)
+			return nullptr;
 
-		ProjectLibrary::ResourceEntry* fileEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+		return MonoUtil::stringToMono(meta->getUUID());
+	}
 
-		if (fileEntry->meta != nullptr)
-			return fileEntry->meta->getIncludeInBuild();
+	MonoObject* ScriptResourceMeta::internal_GetIcon(ScriptResourceMeta* thisPtr)
+	{
+		// TODO - Icons not supported yet
+		return nullptr;
+	}
 
-		return false;
+	ScriptResourceType ScriptResourceMeta::internal_GetResourceType(ScriptResourceMeta* thisPtr)
+	{
+		ProjectResourceMetaPtr meta = gProjectLibrary().findResourceMeta(thisPtr->mAssetPath);
+		if (meta == nullptr)
+			return ScriptResourceType::Undefined;
+
+		return ScriptResource::getTypeFromTypeId(meta->getTypeID());
 	}
 }