summaryrefslogtreecommitdiff
path: root/Runtime/Misc
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Misc')
-rw-r--r--Runtime/Misc/Allocator.cpp18
-rw-r--r--Runtime/Misc/Allocator.h17
-rw-r--r--Runtime/Misc/AllocatorLabelNames.h107
-rw-r--r--Runtime/Misc/AllocatorLabels.cpp63
-rw-r--r--Runtime/Misc/AllocatorLabels.h72
-rw-r--r--Runtime/Misc/AssetBundle.cpp183
-rw-r--r--Runtime/Misc/AssetBundle.h127
-rw-r--r--Runtime/Misc/AssetBundleUtility.cpp692
-rw-r--r--Runtime/Misc/AssetBundleUtility.h76
-rw-r--r--Runtime/Misc/AsyncOperation.cpp47
-rw-r--r--Runtime/Misc/AsyncOperation.h38
-rw-r--r--Runtime/Misc/BatchDeleteObjects.cpp221
-rw-r--r--Runtime/Misc/BatchDeleteObjects.h31
-rw-r--r--Runtime/Misc/BuildSettings.cpp188
-rw-r--r--Runtime/Misc/BuildSettings.h80
-rw-r--r--Runtime/Misc/CPUInfo.cpp120
-rw-r--r--Runtime/Misc/CPUInfo.h117
-rw-r--r--Runtime/Misc/CachingManager.cpp1207
-rw-r--r--Runtime/Misc/CachingManager.h215
-rw-r--r--Runtime/Misc/CaptureScreenshot.cpp605
-rw-r--r--Runtime/Misc/CaptureScreenshot.h30
-rw-r--r--Runtime/Misc/ComponentRequirement.cpp451
-rw-r--r--Runtime/Misc/ComponentRequirement.h33
-rw-r--r--Runtime/Misc/DebugUtility.cpp156
-rw-r--r--Runtime/Misc/DebugUtility.h30
-rw-r--r--Runtime/Misc/DeveloperConsole.cpp475
-rw-r--r--Runtime/Misc/DeveloperConsole.h154
-rw-r--r--Runtime/Misc/GOCreation.cpp95
-rw-r--r--Runtime/Misc/GOCreation.h18
-rw-r--r--Runtime/Misc/GOCreationTests.cpp54
-rw-r--r--Runtime/Misc/GameObjectUtility.cpp1276
-rw-r--r--Runtime/Misc/GameObjectUtility.h91
-rw-r--r--Runtime/Misc/GameObjectUtilityTests.cpp118
-rw-r--r--Runtime/Misc/GarbageCollectSharedAssets.cpp1188
-rw-r--r--Runtime/Misc/GarbageCollectSharedAssets.h31
-rw-r--r--Runtime/Misc/GraphicsDevicesDB.cpp1322
-rw-r--r--Runtime/Misc/GraphicsDevicesDB.h18
-rw-r--r--Runtime/Misc/GraphicsScriptingUtility.cpp36
-rw-r--r--Runtime/Misc/GraphicsScriptingUtility.h12
-rw-r--r--Runtime/Misc/GuiManager.cpp605
-rw-r--r--Runtime/Misc/InputEvent.cpp162
-rw-r--r--Runtime/Misc/InputEvent.h146
-rw-r--r--Runtime/Misc/MeshWelding.cpp249
-rw-r--r--Runtime/Misc/MeshWelding.h17
-rw-r--r--Runtime/Misc/MessageParameters.h35
-rw-r--r--Runtime/Misc/Player.cpp2228
-rw-r--r--Runtime/Misc/Player.h153
-rw-r--r--Runtime/Misc/PlayerSettings.cpp436
-rw-r--r--Runtime/Misc/PlayerSettings.h458
-rw-r--r--Runtime/Misc/Plugins.cpp231
-rw-r--r--Runtime/Misc/Plugins.h25
-rw-r--r--Runtime/Misc/PreloadManager.cpp1013
-rw-r--r--Runtime/Misc/PreloadManager.h209
-rw-r--r--Runtime/Misc/QualitySettings.cpp644
-rw-r--r--Runtime/Misc/QualitySettings.h133
-rw-r--r--Runtime/Misc/RegisterAllClasses.h6
-rw-r--r--Runtime/Misc/ReproductionLog.cpp706
-rw-r--r--Runtime/Misc/ReproductionLog.h80
-rw-r--r--Runtime/Misc/ResourceManager.cpp1292
-rw-r--r--Runtime/Misc/ResourceManager.h224
-rw-r--r--Runtime/Misc/ResourceManagerGUIDs.h5
-rw-r--r--Runtime/Misc/ResourceManagerUtility.cpp55
-rw-r--r--Runtime/Misc/ResourceManagerUtility.h21
-rw-r--r--Runtime/Misc/SaveAndLoadHelper.cpp1106
-rw-r--r--Runtime/Misc/SaveAndLoadHelper.h58
-rw-r--r--Runtime/Misc/SceneUnloading.cpp69
-rw-r--r--Runtime/Misc/SceneUnloading.h1
-rw-r--r--Runtime/Misc/SystemInfo.h320
-rw-r--r--Runtime/Misc/UTF8.cpp376
-rw-r--r--Runtime/Misc/UTF8.h12
-rw-r--r--Runtime/Misc/UserList.cpp195
-rw-r--r--Runtime/Misc/UserList.h63
-rw-r--r--Runtime/Misc/WWWCached.cpp319
-rw-r--r--Runtime/Misc/WWWCached.h59
74 files changed, 21523 insertions, 0 deletions
diff --git a/Runtime/Misc/Allocator.cpp b/Runtime/Misc/Allocator.cpp
new file mode 100644
index 0000000..0c1f3c0
--- /dev/null
+++ b/Runtime/Misc/Allocator.cpp
@@ -0,0 +1,18 @@
+#include "UnityPrefix.h"
+#if defined(UNIT_TEST)
+#include <cassert>
+#define AssertIf(x) assert(!(x))
+#else
+#include "Configuration/UnityConfigure.h"
+#endif
+
+#include <map>
+
+#if UNITY_WIN || UNITY_ANDROID
+#include <stdlib.h>
+#endif
+
+#include "Allocator.h"
+#if UNITY_WII
+#include <rvlaux/clib.h>
+#endif
diff --git a/Runtime/Misc/Allocator.h b/Runtime/Misc/Allocator.h
new file mode 100644
index 0000000..88480ca
--- /dev/null
+++ b/Runtime/Misc/Allocator.h
@@ -0,0 +1,17 @@
+#ifndef UNITY_CUSTOM_ALLOCATOR_H_
+#define UNITY_CUSTOM_ALLOCATOR_H_
+
+
+
+#if UNITY_WII
+# define MemPool1 0
+# define MemPool2 1
+#elif UNITY_XENON
+# define MemPool1 0
+# define MemPool2 0
+#else
+# define MemPool1 0
+# define MemPool2 1
+#endif
+
+#endif // UNITY_CUSTOM_ALLOCATOR_H_
diff --git a/Runtime/Misc/AllocatorLabelNames.h b/Runtime/Misc/AllocatorLabelNames.h
new file mode 100644
index 0000000..6f95101
--- /dev/null
+++ b/Runtime/Misc/AllocatorLabelNames.h
@@ -0,0 +1,107 @@
+
+////////////////////////////////////////////////////////////////////////////////
+////// This has to be in sync with ENUM WiiMemory in playerSettings.txt ////////
+////// There is also a Wii area map in AllocatorLabels.cpp ////////
+////////////////////////////////////////////////////////////////////////////////
+
+DO_LABEL(Default)
+DO_LABEL(Permanent)
+DO_LABEL(NewDelete)
+DO_LABEL(MallocFree)
+DO_LABEL(Thread)
+DO_LABEL(PVS)
+DO_LABEL(Manager)
+DO_LABEL(DynamicGeometry)
+DO_LABEL(VertexData)
+DO_LABEL(ImmediateGeometry)
+DO_LABEL(Geometry)
+DO_LABEL(BatchedGeometry)
+DO_LABEL(Particles)
+DO_LABEL(Texture)
+DO_LABEL(Shader)
+DO_LABEL(TextureCache)
+DO_LABEL(GfxDevice)
+DO_LABEL(GfxThread)
+DO_LABEL(Animation)
+DO_LABEL(Audio)
+DO_LABEL(AudioData)
+DO_LABEL(AudioProcessing)
+DO_LABEL(FMOD)
+DO_LABEL(Font)
+DO_LABEL(Physics)
+DO_LABEL(Serialization)
+DO_LABEL(IO)
+DO_LABEL(IO2)
+DO_LABEL(ThreadStack)
+DO_LABEL(TextAsset)
+DO_LABEL(GarbageCollector)
+DO_LABEL(GLib)
+DO_LABEL(GLibImage)
+DO_LABEL(Mono)
+DO_LABEL(MonoCode)
+DO_LABEL(BaseObject)
+DO_LABEL(Resource)
+DO_LABEL(Renderer)
+DO_LABEL(Transform)
+DO_LABEL(File)
+DO_LABEL(TempOverflow)
+DO_LABEL(Network)
+DO_LABEL(WebCam)
+DO_LABEL(Profiler)
+DO_LABEL(MemoryProfiler)
+DO_LABEL(MemoryProfilerString)
+DO_LABEL(Culling)
+DO_LABEL(Skinning)
+DO_LABEL(Terrain)
+DO_LABEL(Shadow)
+DO_LABEL(STL)
+DO_LABEL(String)
+DO_LABEL(StaticString)
+DO_LABEL(DynamicArray)
+DO_LABEL(UTF16String)
+DO_LABEL(Utility)
+DO_LABEL(Curl)
+DO_LABEL(PoolAlloc)
+DO_LABEL(Navigation)
+DO_LABEL(AssetServerCache)
+DO_LABEL(TypeTree)
+DO_LABEL(ScriptManager)
+DO_LABEL(Substance)
+DO_LABEL(Sprites)
+DO_LABEL(ClusterRenderer)
+
+// Editor Specific
+DO_LABEL(EditorGui)
+DO_LABEL(EditorUtility)
+DO_LABEL(VersionControl)
+DO_LABEL(UndoBuffer)
+DO_LABEL(Undo)
+DO_LABEL(AssetDatabase)
+DO_LABEL(PreviewImage)
+DO_LABEL(AssetImporter)
+DO_LABEL(FBXImporter)
+DO_LABEL(TempAlloc)
+
+// Wii Specific
+DO_LABEL(WiiDefault1)
+DO_LABEL(WiiDefault2)
+DO_LABEL(WiiRVLAux1)
+DO_LABEL(WiiRVLAux2)
+DO_LABEL(WiiRenderTexture)
+DO_LABEL(WiiStrapReminder)
+DO_LABEL(WiiHBM)
+DO_LABEL(WiiMovie)
+DO_LABEL(WiiInput)
+DO_LABEL(WiiNand)
+DO_LABEL(WiiVI)
+DO_LABEL(WiiSkinning)
+DO_LABEL(WiiPThreads)
+
+// PS3 Specific
+DO_LABEL(PS3VideoMemory)
+DO_LABEL(PS3RingBuffers)
+DO_LABEL(PS3RSXBuffers)
+DO_LABEL(PS3DelayedRelease)
+
+// Metro Specific
+DO_LABEL(WinRTTLS)
diff --git a/Runtime/Misc/AllocatorLabels.cpp b/Runtime/Misc/AllocatorLabels.cpp
new file mode 100644
index 0000000..c778296
--- /dev/null
+++ b/Runtime/Misc/AllocatorLabels.cpp
@@ -0,0 +1,63 @@
+#include "UnityPrefix.h"
+#include "AllocatorLabels.h"
+#include "Allocator.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+
+void InitializeMemoryLabels()
+{
+#define INITIALIZE_LABEL_STRUCT(Name) Name.Initialize(Name##Id);
+#define DO_LABEL(Name) INITIALIZE_LABEL_STRUCT(kMem##Name)
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+#undef INITIALIZE_LABEL_STRUCT
+}
+
+const char* const MemLabelName[] =
+{
+#define DO_LABEL(Name) #Name ,
+ #include "AllocatorLabelNames.h"
+#undef DO_LABEL
+};
+
+#define DO_LABEL_STRUCT(Name) EXPORT_COREMODULE Name##Struct Name;
+#define DO_LABEL(Name) DO_LABEL_STRUCT(kMem##Name)
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+#undef DO_LABEL_STRUCT
+
+#if ENABLE_MEM_PROFILER
+
+MemLabelId::MemLabelId( const MemLabelId& other ) : label(other.label), rootReference(NULL)
+{
+ rootReference = copy_root_reference(other.rootReference);
+}
+
+void MemLabelId::SetRootHeader( ProfilerAllocationHeader* rootHeader )
+{
+ if (rootReference)
+ ReleaseReference();
+ rootReference = get_root_reference_from_header(rootHeader);
+}
+
+ProfilerAllocationHeader* MemLabelId::GetRootHeader() const
+{
+ return rootReference ? get_root_header_from_reference(rootReference) : NULL;
+}
+
+void MemLabelId::ReleaseReference()
+{
+ release_root_reference(rootReference);
+ rootReference = NULL;
+}
+
+const MemLabelId& MemLabelId::operator=( const MemLabelId& other )
+{
+ label = other.label;
+ if (rootReference)
+ ReleaseReference();
+ rootReference = copy_root_reference(other.rootReference);
+ return *this;
+}
+
+#endif
+
diff --git a/Runtime/Misc/AllocatorLabels.h b/Runtime/Misc/AllocatorLabels.h
new file mode 100644
index 0000000..1c89d28
--- /dev/null
+++ b/Runtime/Misc/AllocatorLabels.h
@@ -0,0 +1,72 @@
+#ifndef ALLOCATORLABELS_H
+#define ALLOCATORLABELS_H
+
+#include "Runtime/Modules/ExportModules.h"
+
+#define ENABLE_MEM_PROFILER (ENABLE_MEMORY_MANAGER && ENABLE_PROFILER)
+#if ENABLE_MEM_PROFILER
+#define IF_MEMORY_PROFILER_ENABLED(x) x
+#else
+#define IF_MEMORY_PROFILER_ENABLED(x)
+#endif
+
+// Must be in Sync with ENUM WiiMemory in PlayerSettings.txt
+enum MemLabelIdentifier
+{
+#define DO_LABEL(Name) kMem##Name##Id ,
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+ kMemLabelCount
+};
+
+struct AllocationRootReference;
+struct ProfilerAllocationHeader;
+
+
+struct EXPORT_COREMODULE MemLabelId {
+ MemLabelId() { IF_MEMORY_PROFILER_ENABLED( rootReference = NULL ); }
+ explicit MemLabelId ( MemLabelIdentifier id, ProfilerAllocationHeader* root ) : label(id) { IF_MEMORY_PROFILER_ENABLED( rootReference = NULL; SetRootHeader(root) ); }
+ void Initialize ( MemLabelIdentifier id ) { label = id; IF_MEMORY_PROFILER_ENABLED( rootReference = NULL ); }
+
+ MemLabelIdentifier label;
+
+#if ENABLE_MEM_PROFILER
+ MemLabelId ( const MemLabelId& other );
+ ~MemLabelId () { if(rootReference) ReleaseReference(); }
+
+ const MemLabelId& operator= (const MemLabelId& other);
+ void ReleaseReference();
+ ProfilerAllocationHeader* GetRootHeader () const;
+ void SetRootHeader ( ProfilerAllocationHeader* rootHeader );
+ bool UseAutoRoot () const { return rootReference == NULL; };
+private:
+ // root reference = NULL: use stack to get root
+ AllocationRootReference* rootReference;
+#else
+ ProfilerAllocationHeader* GetRootHeader () const {return NULL;}
+ void SetRootHeader ( ProfilerAllocationHeader* rootHeader ) {}
+#endif
+};
+
+#if ENABLE_MEM_PROFILER
+typedef const MemLabelId& MemLabelRef;
+#else
+typedef MemLabelId MemLabelRef;
+#endif
+
+#define DO_LABEL_STRUCT(Name) struct EXPORT_COREMODULE Name##Struct : public MemLabelId { }; extern EXPORT_COREMODULE Name##Struct Name;
+
+#define DO_LABEL(Name) DO_LABEL_STRUCT(kMem##Name)
+#include "AllocatorLabelNames.h"
+#undef DO_LABEL
+
+#undef DO_LABEL_STRUCT
+
+void InitializeMemoryLabels();
+
+//#if UNITY_ENABLE_ACCOUNTING_ALLOCATORS
+extern const char* const MemLabelName[];
+//#endif
+
+
+#endif
diff --git a/Runtime/Misc/AssetBundle.cpp b/Runtime/Misc/AssetBundle.cpp
new file mode 100644
index 0000000..ce65283
--- /dev/null
+++ b/Runtime/Misc/AssetBundle.cpp
@@ -0,0 +1,183 @@
+#include "UnityPrefix.h"
+#include "AssetBundle.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Misc/ResourceManager.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/Utility/RuntimeClassHashing.h"
+#endif
+
+#if ENABLE_WWW
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#endif
+
+#if ENABLE_CACHING
+#include "Runtime/Misc/CachingManager.h"
+#endif
+
+using namespace std;
+
+IMPLEMENT_CLASS (AssetBundle)
+IMPLEMENT_OBJECT_SERIALIZE (AssetBundle)
+
+AssetBundle::AssetBundle (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+#if ENABLE_WWW
+ m_UnityWebStream = NULL;
+#endif
+#if ENABLE_CACHING
+ m_CachedUnityWebStream = NULL;
+#endif
+
+ m_UncompressedFileInfo = NULL;
+ m_RuntimeCompatibility = CURRENT_RUNTIME_COMPATIBILITY_VERSION;
+
+ // Mark as kDontSave, so that AssetBundles are not unloaded on level loads
+ SetHideFlags(Object::kDontSave);
+}
+
+template<class TransferFunc>
+void AssetBundle::AssetInfo::Transfer (TransferFunc& transfer)
+{
+ TRANSFER(preloadIndex);
+ TRANSFER(preloadSize);
+ TRANSFER(asset);
+}
+
+template<class TransferFunc>
+void AssetBundleScriptInfo::Transfer (TransferFunc& transfer)
+{
+ TRANSFER (className);
+ TRANSFER (nameSpace);
+ TRANSFER (assemblyName);
+ TRANSFER (hash);
+}
+
+template<class TransferFunc>
+void AssetBundle::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (3);
+ AssertIf (transfer.GetFlags() & kPerformUnloadDependencyTracking);
+
+ if (transfer.IsReading ())
+ m_RuntimeCompatibility = 0;
+
+ if (transfer.IsOldVersion(1))
+ {
+ multimap<UnityStr, PPtr<Object> > oldContainer;
+ transfer.Transfer(oldContainer, "m_Container");
+ PPtr<Object> mainAsset;
+ transfer.Transfer(mainAsset, "m_MainAsset");
+
+ m_Container.clear();
+ for (multimap<UnityStr, PPtr<Object> >::iterator i=oldContainer.begin();i != oldContainer.end();i++)
+ {
+ AssetInfo info;
+ info.preloadIndex = 0;
+ info.preloadSize = 0;
+ info.asset = i->second;
+ m_Container.insert(make_pair(i->first, info));
+ }
+
+ m_MainAsset.preloadIndex = 0;
+ m_MainAsset.preloadSize = 0;
+ m_MainAsset.asset = mainAsset;
+ }
+ else
+ {
+ transfer.Transfer(m_PreloadTable, "m_PreloadTable");
+ transfer.Transfer(m_Container, "m_Container");
+ transfer.Transfer(m_MainAsset, "m_MainAsset");
+ transfer.Transfer(m_ScriptCompatibility, "m_ScriptCompatibility");
+ transfer.Transfer(m_ClassCompatibility, "m_ClassCompatibility");
+
+ if (!transfer.IsOldVersion (2))
+ transfer.Transfer (m_RuntimeCompatibility, "m_RuntimeCompatibility");
+ }
+}
+
+void AssetBundle::DebugPrintContents ()
+{
+ for (iterator i=m_Container.begin();i != m_Container.end();i++)
+ {
+ printf_console("- %s\n", i->first.c_str());
+ }
+}
+
+AssetBundle::range AssetBundle::GetPathRange (const string& path)
+{
+// if (m_Container.equal_range(ToLower(path)).first == m_Container.equal_range(ToLower(path)).second)
+// {
+// printf_console(("Failed loading " + path + "\n***********\n").c_str());
+// DebugPrintContents();
+// }
+
+ return m_Container.equal_range(ToLower(path));
+}
+
+AssetBundle::range AssetBundle::GetAll ()
+{
+ return make_pair (m_Container.begin(), m_Container.end());
+}
+
+Object* AssetBundle::GetImpl (int classID, const string& path)
+{
+ range r = GetPathRange(path);
+ for (iterator i=r.first;i != r.second;i++)
+ {
+ Object* obj = i->second.asset;
+ if (obj && obj->IsDerivedFrom(classID))
+ return obj;
+ }
+
+ //printf_console(("Failed loading " + path + "\n***********\n").c_str());
+ //DebugPrintContents();
+
+ return NULL;
+}
+
+AssetBundle::~AssetBundle ()
+{
+#if ENABLE_WWW
+ if (m_UnityWebStream)
+ m_UnityWebStream->Release();
+#endif
+#if ENABLE_CACHING
+ UNITY_DELETE(m_CachedUnityWebStream, kMemFile);
+#endif
+ // Don't kill of m_UncompressedFileInfo here due to weird
+ // logic in UnloadAssetBundle() that will access the UncompressedFileInfo
+ // even after the AssetBundle has been deleted through DestroyAllAtPath().
+}
+
+bool AssetBundle::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+#if UNITY_EDITOR
+
+void AssetBundle::AddScriptCompatibilityInfo (std::string const& className, std::string const& nameSpace, std::string const& assembly, UInt32 hash)
+{
+ m_ScriptCompatibility.push_back (AssetBundleScriptInfo (className, nameSpace, assembly, hash));
+}
+
+void AssetBundle::FillHashTableForRuntimeClasses (std::vector<SInt32> const& classIds, TransferInstructionFlags transferFlags)
+{
+ vector_map<int, UInt32> hashes;
+ CalculateHashForClasses (hashes, classIds, transferFlags);
+ m_ClassCompatibility.resize (hashes.size ());
+ std::copy (hashes.begin (), hashes.end (), m_ClassCompatibility.begin ());
+}
+
+AssetBundle* GetEditorAssetBundle ()
+{
+ int instanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID(kEditorResourcePath, 1);
+ PPtr<AssetBundle> res (instanceID);
+ return res;
+}
+
+#endif
diff --git a/Runtime/Misc/AssetBundle.h b/Runtime/Misc/AssetBundle.h
new file mode 100644
index 0000000..803c8b7
--- /dev/null
+++ b/Runtime/Misc/AssetBundle.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include "Runtime/BaseClasses/NamedObject.h"
+
+#include <map>
+#include <utility>
+
+class UnityWebStream;
+class CachedUnityWebStream;
+
+class AssetBundleScriptInfo
+{
+public:
+ DECLARE_SERIALIZE(AssetBundleScriptInfo)
+
+ AssetBundleScriptInfo () {}
+ AssetBundleScriptInfo (const UnityStr& name, const UnityStr& ns, const UnityStr& assembly, UInt32 h) : className (name), nameSpace (ns), hash (h), assemblyName(assembly) {}
+ UnityStr className;
+ UnityStr nameSpace;
+ UnityStr assemblyName;
+ UInt32 hash;
+};
+
+
+class AssetBundle : public NamedObject
+{
+public:
+
+ typedef std::vector<AssetBundleScriptInfo> ScriptCompatibility;
+ typedef std::vector<std::pair<int, UInt32> > ClassCompatibility;
+
+ enum
+ {
+ /// A simple integer version count to keep track of changes to the
+ /// runtime that cause asset bundles to no longer work as intended.
+ /// Increase this version whenever you need to break backwards-compatibility.
+ /// An example of this is when we started to no longer package all default
+ /// resources with every player and thus broke asset bundles that were
+ /// referencing these resources.
+ ///
+ /// @note Runtime compatibility checks are bypassed for the webplayer where
+ /// breaking backwards-compatibility is not allowed at this point.
+ ///
+ /// @see TestAssetBundleCompatiblity
+ CURRENT_RUNTIME_COMPATIBILITY_VERSION = 1
+ };
+
+ struct AssetInfo
+ {
+ DECLARE_SERIALIZE(AssetInfo)
+
+ int preloadIndex;
+ int preloadSize;
+
+ PPtr<Object> asset;
+
+ AssetInfo () { preloadIndex = 0; preloadSize = 0; }
+ };
+ struct UncompressedFileInfo
+ {
+ std::string fileName;
+ UInt32 offset, size;
+
+ bool operator < (UncompressedFileInfo const& a) const { return fileName < a.fileName; }
+ };
+ typedef std::vector<UncompressedFileInfo> UncompressedFileInfoContainer;
+
+ DECLARE_OBJECT_SERIALIZE (AssetBundle)
+ REGISTER_DERIVED_CLASS (AssetBundle, NamedObject)
+
+ AssetBundle (MemLabelId label, ObjectCreationMode mode);
+ // ~AssetBundle (); declared-by-macro
+
+ typedef std::multimap<UnityStr, AssetInfo> AssetMap;
+ typedef AssetMap::iterator iterator;
+ typedef std::pair<iterator, iterator> range;
+
+ range GetAll ();
+ range GetPathRange (const string& path);
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ void DebugPrintContents ();
+
+ // AssetBundle* file = GetEditorAssetBundle();
+ // if (file) {
+ // // Path MUST omit the extension
+ // MonoBehaviour* be = file->Get<MonoBehaviour>(MySkin);
+ // }
+ template<class T>
+ T* Get (const string& path)
+ {
+ Object* res = GetImpl (T::GetClassIDStatic (), path);
+ return static_cast<T*> (res);
+ }
+
+ Object* GetImpl (int classID, const string& path);
+
+ void AddScriptCompatibilityInfo (std::string const& className, std::string const& nameSpace, std::string const& assembly, UInt32 hash);
+ void FillHashTableForRuntimeClasses (std::vector<SInt32> const& classIds, TransferInstructionFlags transferFlags);
+
+ UInt32 m_RuntimeCompatibility;
+ ScriptCompatibility m_ScriptCompatibility;
+ ClassCompatibility m_ClassCompatibility;
+
+ /// AssetInfo for the main asset. Has no associated name.
+ AssetInfo m_MainAsset;
+
+ /// Table of objects that need to be pulled from the bundle by the preload
+ /// manager when a specific asset is loaded from the bundle. Each AssetInfo
+ /// entry has an associated range of entries in the preload table.
+ std::vector<PPtr<Object> > m_PreloadTable;
+
+ /// Map of named assets contained in the bundle. Multiple objects may
+ /// have the same name.
+ AssetMap m_Container;
+
+#if ENABLE_WWW
+ UnityWebStream* m_UnityWebStream;
+#endif
+#if ENABLE_CACHING
+ CachedUnityWebStream* m_CachedUnityWebStream;
+#endif
+ UncompressedFileInfoContainer* m_UncompressedFileInfo;
+};
+
+AssetBundle* GetEditorAssetBundle ();
diff --git a/Runtime/Misc/AssetBundleUtility.cpp b/Runtime/Misc/AssetBundleUtility.cpp
new file mode 100644
index 0000000..4c5b851
--- /dev/null
+++ b/Runtime/Misc/AssetBundleUtility.cpp
@@ -0,0 +1,692 @@
+#include "UnityPrefix.h"
+#include "AssetBundleUtility.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Configuration/UnityConfigureOther.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+
+#if ENABLE_WWW
+#include "Runtime/Serialize/TypeTree.h"
+#include "Runtime/Serialize/SwapEndianBytes.h"
+#include "PlatformDependent/CommonWebPlugin/FileStream.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Export/WWW.h"
+#include "Runtime/Misc/WWWCached.h"
+#endif
+
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Mono/MonoScriptManager.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/GetComponent.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Runtime/Serialize/BuildTargetVerification.h"
+#endif
+
+static char const* kIncompatibleScriptsMsg = "The asset bundle '%s' could not be loaded because it references scripts that are not compatible with the currently loaded ones. Rebuild the AssetBundle to fix this error.";
+static char const* kIncompatibleRuntimeClassMsg = "The asset bundle '%s' could not be loaded because it contains run-time classes of incompatible version. Rebuild the AssetBundle to fix this error.";
+static char const* kIncompatibleRuntimeMsg = "The asset bundle '%s' could not be loaded because it is not compatible with this newer version of the Unity runtime. Rebuild the AssetBundle to fix this error.";
+
+static bool TestScriptCompatibility (std::vector<AssetBundleScriptInfo> const& scripts)
+{
+ bool result = true;
+
+ #if ENABLE_SCRIPTING
+ MonoScriptManager& sm = GetMonoScriptManager();
+
+#if 0 // debug
+ {
+ MonoScriptManager::AllScripts existing = sm.GetAllRuntimeScripts ();
+ printf_console ("LOADED SCRIPTS: %d\n", existing.size ());
+ for (MonoScriptManager::AllScripts::iterator it = existing.begin (), end = existing.end (); it != end; ++it)
+ {
+ printf_console (" %s (%s) hash: %08x\n", (*it)->GetName(), (*it)->GetClassName().c_str(), (*it)->GetPropertiesHash());
+ }
+ }
+
+ {
+ printf_console ("TESTING SCRIPTS: %d\n", scripts.size ());
+ for (size_t i=0, size = scripts.size (); i<size; ++i)
+ printf_console (" [%s.]%s hash: %08x\n", scripts[i].nameSpace.c_str(), scripts[i].className.c_str(), scripts[i].hash);
+ }
+#endif
+
+ for (std::vector<AssetBundleScriptInfo>::const_iterator it = scripts.begin (), end = scripts.end (); it != end; ++it)
+ {
+ if (MonoScript* script = sm.FindRuntimeScript (it->className, it->nameSpace, it->assemblyName))
+ {
+ UInt32 supported = script->GetPropertiesHash ();
+ UInt32 loading = it->hash;
+
+ if (supported != loading)
+ {
+ WarningStringMsg ("AssetBundle loading failed because the %s script serialization hash does not match. Supported: %08x, loading: %08x\n", script->GetScriptFullClassName ().c_str(), (unsigned int)supported, (unsigned int)loading);
+ result = false;
+ }
+ }
+ }
+ #endif
+
+ return result;
+}
+
+static bool TestRuntimeClassCompatibility (std::vector<std::pair<int, UInt32> > const& classes)
+{
+ bool result = true;
+ for (size_t i=0, size = classes.size (); i<size; ++i)
+ {
+ int classID = classes[i].first;
+ UInt32 loading = classes[i].second;
+
+ UInt32 supported = GetBuildSettings ().GetHashOfClass (classID);
+ if (supported != 0 && loading != supported)
+ {
+ WarningStringMsg ("AssetBundle loading failed because the %s class serialization hash does not match. Supported: %08x, loading: %08x\n", Object::ClassIDToString(classID).c_str (), (unsigned int)supported, (unsigned int)loading);
+ result = false;
+ }
+ }
+
+ return result;
+}
+
+bool TestAssetBundleCompatibility (AssetBundle& bundle, const std::string& bundleName, std::string& error)
+{
+ error = string();
+
+ // We only report a single type of incompatibility here even when there are multiple
+ // incompatibilities present in the bundle. However, since the solution to each
+ // incompatibility is rebuilding the asset bundle which will also get rid of
+ // whatever other incompatibilities are present in the bundle, we don't need to
+ // worry about that.
+
+ // Test script class compatibility.
+ if (!TestScriptCompatibility (bundle.m_ScriptCompatibility))
+ {
+ error = Format (kIncompatibleScriptsMsg, bundleName.c_str ());
+ return false;
+ }
+
+ // Test runtime class compatibility.
+ if (!TestRuntimeClassCompatibility (bundle.m_ClassCompatibility))
+ {
+ error = Format (kIncompatibleRuntimeClassMsg, bundleName.c_str ());
+ return false;
+ }
+
+#if !UNITY_WEBPLAYER // We do not allow breaking backwards-compatibility in the webplayer at this point.
+
+ bool isEditorTargetingWebPlayer = false;
+ #if UNITY_EDITOR
+ isEditorTargetingWebPlayer =
+ GetEditorUserBuildSettingsPtr ()
+ && IsWebPlayerTargetPlatform (GetEditorUserBuildSettings ().GetActiveBuildTarget ());
+ #endif
+
+ // Check against our runtime compatibility version to see if there's
+ // been some more profound changes to the runtime that prevent old
+ // asset bundles from working.
+ //
+ // In the case of being in the editor and targeting the webplayer, we
+ // suppress the check given that the webplayer will load the bundle
+ // correctly.
+ if (!isEditorTargetingWebPlayer && bundle.m_RuntimeCompatibility < AssetBundle::CURRENT_RUNTIME_COMPATIBILITY_VERSION)
+ {
+ error = Format (kIncompatibleRuntimeMsg, bundleName.c_str ());
+ return false;
+ }
+
+#endif
+
+ return true;
+}
+
+static AssetBundle* FindAssetBundleObject (std::string const& assetBundlePath)
+{
+ PersistentManager& pm = GetPersistentManager();
+
+ // An AssetBundle can be the first (AssetBundle) or the second object (StreamedLevel assets file)
+ const int fileID1 = 1, fileID2 = 2;
+
+ int fileID = 0;
+ if (pm.GetClassIDFromPathAndFileID(assetBundlePath, fileID1) == ClassID(AssetBundle))
+ fileID = fileID1;
+ else if (pm.GetClassIDFromPathAndFileID(assetBundlePath, fileID2) == ClassID(AssetBundle))
+ fileID = fileID2;
+ else
+ {
+ // Old streamed scene asset bundle that has no AssetBundle object.
+ return NULL;
+ }
+
+ int instanceID = pm.GetInstanceIDFromPathAndFileID (assetBundlePath, fileID);
+ return dynamic_instanceID_cast<AssetBundle*> (instanceID);
+}
+
+static AssetBundle* InitializeAssetBundle (const std::string& assetBundleName,
+ const std::string& assetBundlePath,
+ AssetBundle::UncompressedFileInfoContainer* uncompressedFiles,
+ UnityWebStream* webStream,
+ bool performCompatibilityChecks = true)
+{
+ PersistentManager& pm = GetPersistentManager();
+ UNUSED(pm);
+
+ // Locate AssetBundle object.
+ AssetBundle* assetBundle = FindAssetBundleObject (assetBundlePath);
+ if (!assetBundle)
+ {
+ // We used to not include AssetBundle objects in streamed scene
+ // asset bundles that had type trees. This is no longer the case
+ // but to handle this case, we create an AssetBundle object on
+ // demand (like before).
+
+ assetBundle = NEW_OBJECT (AssetBundle);
+ assetBundle->Reset ();
+ assetBundle->AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad);
+
+ // Set to incompatible runtime version. Will only load on
+ // webplayer.
+ assetBundle->m_RuntimeCompatibility = 0;
+ }
+
+ if (assetBundle->m_UncompressedFileInfo)
+ UNITY_DELETE (assetBundle->m_UncompressedFileInfo, kMemFile);
+
+ // Associate uncompressed files with asset bundle, if we have them
+ // (will only be the case for local asset bundles).
+ if (uncompressedFiles)
+ {
+ UNITY_TRANSFER_OWNERSHIP (uncompressedFiles, kMemFile, assetBundle);
+ assetBundle->m_UncompressedFileInfo = uncompressedFiles;
+ }
+
+ // Associate webstream with asset bundle, if we have one
+ // (will only be the case for downloaded asset bundles).
+#if ENABLE_WWW
+ if (webStream)
+ {
+ assetBundle->m_UnityWebStream = webStream;
+ assetBundle->m_UnityWebStream->Retain ();
+ webStream->SetCachedAssetBundle (assetBundle->GetInstanceID());
+ }
+#endif
+
+ // Make sure the asset bundle can be loaded into this
+ // build of the player.
+ if (performCompatibilityChecks)
+ {
+ string errorMessage;
+ bool isCompatible = TestAssetBundleCompatibility (*assetBundle, assetBundleName, errorMessage);
+ if (!isCompatible)
+ {
+ ErrorString (errorMessage);
+ UnloadAssetBundle (*assetBundle, true);
+ return NULL;
+ }
+ }
+
+ return assetBundle;
+}
+
+#if ENABLE_WWW
+static bool LooksLikeStreamedSceneBundle (FileStream* data)
+{
+ if (data->m_Files.size () == 0)
+ return false;
+
+ // If first or second file name starts with "BuildPlayer-", it's
+ // probably a streamed scene bundle. In case it has extra resources,
+ // that will be the first file.
+ const bool firstOrSecondFileIsSceneData =
+ data->m_Files[0].name.find ("BuildPlayer-") == 0 ||
+ (data->m_Files.size () > 1 && data->m_Files[1].name.find ("BuildPlayer-") == 0);
+
+ return firstOrSecondFileIsSceneData;
+}
+
+static bool LooksLikeCustomAssetBundle (FileStream* data)
+{
+ if (data->m_Files.size () == 0)
+ return false;
+
+ // If the first file name starts with "CustomAssetBundle" or "CAB",
+ // it's probably a custom asset bundle.
+ const bool isCustomAssetBundle =
+ data->m_Files[0].name.find ("CustomAssetBundle") == 0 ||
+ data->m_Files[0].name.find ("CAB" ) == 0;
+
+ return isCustomAssetBundle;
+}
+
+static bool LooksLikeValidAssetBundle (FileStream* data)
+{
+ return LooksLikeStreamedSceneBundle (data)
+ || LooksLikeCustomAssetBundle( data );
+}
+
+static AssetBundle* ExtractAssetBundle(UnityWebStream* unityweb, const char* url, bool performCompatibilityChecks = true)
+{
+ if (!unityweb || !unityweb->GetFileStream())
+ return NULL;
+
+ FileStream* data = (FileStream*)unityweb->GetFileStream();
+
+ // Return last asset bundle instance if already accessed.
+ if (data->m_Files.size() >= 1 && dynamic_instanceID_cast<AssetBundle*> (unityweb->GetCachedAssetBundle()))
+ return dynamic_instanceID_cast<AssetBundle*> (unityweb->GetCachedAssetBundle());
+
+ ///@TODO: the UnityWebStream should contain information on if the file is an asset bundle instead
+ if (!LooksLikeValidAssetBundle (data))
+ {
+ ErrorString("The unity3d file is not a valid asset bundle.");
+ return NULL;
+ }
+
+ PersistentManager& pm = GetPersistentManager();
+ pm.Lock();
+
+ for (FileStream::iterator i=data->begin();i != data->end();i++)
+ {
+ if (pm.IsStreamLoaded(i->name))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because another asset bundle with the same files are already loaded", url));
+ return NULL;
+ }
+ }
+
+ // Load all memory streams
+ for (FileStream::iterator i=data->begin();i != data->end();i++)
+ {
+ if (!pm.LoadMemoryBlockStream(i->name, i->blocks, i->offset, i->end, url))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because it was not built with the right version or build target.", url));
+ return NULL;
+ }
+ }
+
+ pm.Unlock();
+
+ ////WORKAROUND!
+ //// There was a bug in 4.2 where script compatibility information was *always* written
+ //// out into streamed scene asset bundles which in turn makes us always check these
+ //// hashes on loading and reject bundles if they don't match. As we *always* build
+ //// streamed scene bundles with type trees when targeting the webplayer, this makes us
+ //// not load bundles we can actually load. Work around this issue here by disabling
+ //// compatibility checks altogether when loading a streamed scene bundle into the webplayer.
+ #if UNITY_WEBPLAYER
+ if (LooksLikeStreamedSceneBundle (data))
+ performCompatibilityChecks = false;
+ #endif
+
+ const string &path = data->begin ()->name;
+ return InitializeAssetBundle (url, path, NULL, unityweb, performCompatibilityChecks);
+}
+
+AssetBundle *ExtractAssetBundle(WWW &www)
+{
+ if (!www.HasDownloadedOrMayBlock ())
+ return NULL;
+ www.BlockUntilDone();
+
+ #if ENABLE_CACHING
+ if (www.GetType() == kWWWTypeCached)
+ return static_cast<WWWCached&>(www).GetAssetBundle();
+ #endif
+
+ return ExtractAssetBundle(www.GetUnityWebStream(), www.GetUrl());
+}
+
+AssetBundleCreateRequest::AssetBundleCreateRequest( const UInt8* dataPtr, int dataSize )
+ : m_UnityWebStream(0)
+ , m_AssetBundle(0)
+ , m_EnableCompatibilityChecks (true)
+{
+ UnityWebStreamHeader header;
+
+ int result = ParseStreamHeader (header, dataPtr, dataPtr + dataSize);
+ if (result)
+ {
+ if (result == 1)
+ {
+ ErrorString("Asset bundle is incomplete");
+ }
+ else if(result == 2)
+ {
+ ErrorString("Error parsing asset bundle");
+ }
+
+ m_Progress = 1.f;
+ UnityMemoryBarrier();
+ m_Complete = true;
+ return;
+ }
+
+ m_UnityWebStream = UNITY_NEW_AS_ROOT(UnityWebStream(NULL, 0, 0), kMemFile, "WebStream", "");
+#if SUPPORT_THREADS
+ m_UnityWebStream->SetDecompressionPriority(GetPreloadManager().GetThreadPriority());
+#endif
+ m_UnityWebStream->Retain();
+
+ m_UnityWebStream->FeedDownloadData(dataPtr, dataSize, true);
+
+ GetPreloadManager().AddToQueue(this);
+}
+
+AssetBundleCreateRequest::~AssetBundleCreateRequest()
+{
+ if (m_UnityWebStream != NULL)
+ m_UnityWebStream->Release();
+}
+
+void AssetBundleCreateRequest::Perform ()
+{
+ if (m_UnityWebStream == NULL)
+ return;
+ m_UnityWebStream->WaitForThreadDecompression();
+}
+void AssetBundleCreateRequest::IntegrateMainThread ()
+{
+ m_AssetBundle = ExtractAssetBundle(m_UnityWebStream, "<unknown>", m_EnableCompatibilityChecks);
+ UnityMemoryBarrier();
+ m_Complete = true;
+}
+
+float AssetBundleCreateRequest::GetProgress ()
+{
+ if (m_UnityWebStream == NULL)
+ return 1.f;
+ m_UnityWebStream->UpdateProgress();
+ m_Progress = m_UnityWebStream->GetProgressUntilLoadable();
+ return m_Progress;
+}
+
+#endif
+
+static void ForcePreload (AssetBundle& bundle, const AssetBundle::AssetInfo& info)
+{
+ for (int i=0;i<info.preloadSize;i++)
+ {
+ PPtr<Object> preload = bundle.m_PreloadTable[i + info.preloadIndex];
+ preload.IsValid();
+ }
+}
+
+///@TODO: For 4.0 we should remove this function and make AssetBundle.Load use the LoadAsync code path and wait for completion.
+
+static void ProcessAssetBundleEntries(AssetBundle& bundle, AssetBundle::range entries, ScriptingObjectPtr systemTypeInstance, vector<Object*>& output, bool stopAfterOne)
+{
+#if ENABLE_SCRIPTING
+ ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType(systemTypeInstance);
+
+ for (AssetBundle::iterator i=entries.first;i != entries.second;i++)
+ {
+ Object* obj = i->second.asset;
+ if (obj == NULL)
+ continue;
+
+ ScriptingObjectPtr o = Scripting::ScriptingWrapperFor(obj);
+ if (o && scripting_class_is_subclass_of(scripting_object_get_class(o, GetScriptingTypeRegistry()), klass))
+ {
+ ForcePreload(bundle, i->second);
+ output.push_back(obj);
+ if (stopAfterOne) return;
+ }
+
+ Unity::GameObject* go = dynamic_pptr_cast<GameObject*> (obj);
+ if (go != NULL)
+ {
+ o = ScriptingGetComponentOfType(*go, systemTypeInstance, false);
+ if (o != SCRIPTING_NULL)
+ {
+ ForcePreload(bundle, i->second);
+ output.push_back(ScriptingObjectToObject<Object>(o));
+ if (stopAfterOne) return;
+ }
+ }
+ }
+#endif
+}
+
+Object* LoadNamedObjectFromAssetBundle (AssetBundle& bundle, const std::string& name, ScriptingObjectPtr type)
+{
+
+ string lowerName = ToLower(name);
+ AssetBundle::range found = bundle.GetPathRange(lowerName);
+
+ vector<Object*> result;
+ ProcessAssetBundleEntries(bundle,found,type,result,true);
+ if (result.empty())
+ return NULL;
+
+ return result[0];
+}
+
+Object* LoadMainObjectFromAssetBundle (AssetBundle& bundle)
+{
+ PreloadLevelOperation::PreloadBundleSync (bundle, "");
+ return bundle.m_MainAsset.asset;
+}
+
+void LoadAllFromAssetBundle (AssetBundle& assetBundle, ScriptingObjectPtr type, vector<Object* >& output)
+{
+ AssetBundle::range found = assetBundle.GetAll();
+ ProcessAssetBundleEntries(assetBundle,found,type,output,false);
+}
+
+namespace UnityConsoleStream_Static
+{
+
+ static bool ReadBigEndian (const UInt8*& cur, const UInt8* dataEnd, UInt32& data)
+ {
+ if (cur + sizeof(SInt32) > dataEnd)
+ return false;
+ data = *reinterpret_cast<const SInt32*> (cur);
+#if UNITY_LITTLE_ENDIAN
+ SwapEndianBytes(data);
+#endif
+ cur += sizeof(SInt32);
+ return true;
+ }
+
+ static bool ReadString (const UInt8*& cur, const UInt8* dataEnd, std::string& data)
+ {
+ int length = 0;
+ while (true)
+ {
+ if (cur + length >= dataEnd)
+ return false;
+
+ if (cur[length] == '\0')
+ break;
+
+ length++;
+ }
+
+ data.assign (cur, cur + length);
+ cur += length + 1;
+ return true;
+ }
+
+} // namespace UnityWebStream_Static
+
+
+bool ParseUncompressedFileHeader(AssetBundle::UncompressedFileInfoContainer* files, const UInt8* cur, const UInt8* headerEnd, int offset)
+{
+ using namespace UnityConsoleStream_Static;
+ UInt32 fileCount;
+ if (!ReadBigEndian(cur, headerEnd, fileCount))
+ return false;
+
+ files->reserve (fileCount);
+
+ for (int i = 0; i < fileCount; i++)
+ {
+ AssetBundle::UncompressedFileInfo fileInfo;
+
+ if (!ReadString(cur, headerEnd, fileInfo.fileName)) return false;
+ if (!ReadBigEndian(cur, headerEnd, fileInfo.offset)) return false;
+ else
+ {
+ fileInfo.offset += offset;
+ }
+ if (!ReadBigEndian(cur, headerEnd, fileInfo.size)) return false;
+
+ files->push_back(fileInfo);
+ }
+
+ return true;
+}
+
+#if SUPPORT_SERIALIZATION_FROM_DISK
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName)
+{
+ using namespace UnityConsoleStream_Static;
+ File file;
+ if (!file.Open (assetBundlePathName, File::kReadPermission, File::kSilentReturnOnOpenFail))
+ return NULL;
+
+#define ExitAndCloseFile() {file.Close(); return NULL;}
+
+ const int kBundleHeaderSize = sizeof("UnityRaw") +
+ sizeof(SInt32) +
+ sizeof(UNITY_WEB_BUNDLE_VERSION) +
+ sizeof(UNITY_WEB_MINIMUM_REVISION) +
+ sizeof(SInt32) * 4;
+
+ UInt8 prefix[kBundleHeaderSize];
+ int bytesRead = file.Read(0, prefix, kBundleHeaderSize);
+ if (bytesRead != kBundleHeaderSize)
+ {
+ ErrorString("Error while reading asset bundle header!");
+ ExitAndCloseFile();
+ }
+
+ // read prefix
+ std::string streamId, unityVersion, minimumRevision;
+ UInt32 streamVersion;
+
+ UInt8 const* it = prefix, *end = prefix + kBundleHeaderSize;
+ if (!ReadString(it, end, streamId))
+ ExitAndCloseFile()
+ if (streamId != "UnityRaw")
+ {
+ ErrorStringMsg("This asset bundle was not created with UncompressedAssetBundle flag, expected id 'UnityRaw', got '%s'", streamId.c_str());
+ ExitAndCloseFile();
+ }
+ if (!ReadBigEndian(it, end, streamVersion))
+ ExitAndCloseFile();
+ if (!ReadString(it, end, unityVersion))
+ ExitAndCloseFile();
+ if (!ReadString(it, end, minimumRevision))
+ ExitAndCloseFile();
+
+ UInt32 byteStart, headerSize, numberOfLevelsToDownloadBeforeStreaming, levelCount, completeFileSize, fileInfoHeaderSize;
+
+ if (!ReadBigEndian(it, end, byteStart))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, headerSize))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, numberOfLevelsToDownloadBeforeStreaming))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, levelCount))
+ ExitAndCloseFile();
+
+ UInt8* fullHeader = (UInt8*)alloca (headerSize);
+ file.Read(0, fullHeader, headerSize);
+
+ it = fullHeader + kBundleHeaderSize;
+ end = fullHeader + headerSize;
+
+ UInt32 dummyCompressed, dummyUncompressed;
+ for (UInt32 i = 0; i < levelCount; i++)
+ {
+ if (!ReadBigEndian(it, end, dummyCompressed))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, dummyUncompressed))
+ ExitAndCloseFile();
+ }
+ if (!ReadBigEndian(it, end, completeFileSize))
+ ExitAndCloseFile();
+ if (!ReadBigEndian(it, end, fileInfoHeaderSize))
+ ExitAndCloseFile();
+
+ void* fileContainerRoot = UNITY_NEW_AS_ROOT(int,kMemFile,"Temp","");
+ AssetBundle::UncompressedFileInfoContainer* files;
+ {
+ SET_ALLOC_OWNER(fileContainerRoot);
+ files = UNITY_NEW(AssetBundle::UncompressedFileInfoContainer, kMemFile);
+ }
+ UInt8* fileInfoHeader = (UInt8*)alloca (fileInfoHeaderSize);
+ file.Read (headerSize, fileInfoHeader, fileInfoHeaderSize);
+ file.Close();
+
+#undef ExitAndCloseFile
+
+ if (ParseUncompressedFileHeader(files, fileInfoHeader, fileInfoHeader + fileInfoHeaderSize, headerSize) == false)
+ {
+ ErrorString("Failed to parsed asset bundle header");
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+
+ if (files->size() == 0 || (files->begin()->fileName.find("BuildPlayer-") != 0 && files->begin()->fileName.find("CustomAssetBundle") != 0 && files->begin()->fileName.find("CAB") != 0))
+ {
+ ErrorString("The unity3d file is not a valid asset bundle.");
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+
+ PersistentManager& pm = GetPersistentManager();
+ pm.Lock();
+
+ for (AssetBundle::UncompressedFileInfoContainer::iterator i = files->begin(); i != files->end(); ++i)
+ {
+ if (pm.IsStreamLoaded(i->fileName))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because another asset bundle with the same files are already loaded.", assetBundlePathName.c_str()));
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+ }
+
+ // Load all memory streams
+ for (AssetBundle::UncompressedFileInfoContainer::iterator i=files->begin(); i != files->end(); ++i)
+ {
+ // TODO: check flags
+ if (!pm.LoadExternalStream(i->fileName, assetBundlePathName, kSerializeGameRelease, i->offset))
+ {
+ pm.Unlock();
+ ErrorString(Format("The asset bundle '%s' can't be loaded because it was not built with the right version or build target.", assetBundlePathName.c_str()));
+ UNITY_DELETE(files, kMemFile);
+ return NULL;
+ }
+ }
+
+ pm.Unlock();
+
+ const string &path = files->begin ()->fileName;
+ return InitializeAssetBundle (assetBundlePathName, path, files, NULL);
+}
+#else
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName)
+{
+ ErrorString("Failed to load asset bundle (not supported).");
+ return NULL;
+}
+#endif
diff --git a/Runtime/Misc/AssetBundleUtility.h b/Runtime/Misc/AssetBundleUtility.h
new file mode 100644
index 0000000..593db95
--- /dev/null
+++ b/Runtime/Misc/AssetBundleUtility.h
@@ -0,0 +1,76 @@
+#ifndef ASSETBUNDLEUTILITY_H
+#define ASSETBUNDLEUTILITY_H
+
+#include "Runtime/Misc/PreloadManager.h"
+#include "Runtime/Misc/AssetBundle.h"
+
+#if ENABLE_WWW
+class WWW;
+
+AssetBundle* ExtractAssetBundle(WWW &www);
+
+
+class AssetBundleCreateRequest : public PreloadManagerOperation
+{
+public:
+ AssetBundleCreateRequest( const UInt8* dataPtr, int dataSize );
+ virtual ~AssetBundleCreateRequest();
+
+ virtual void Perform ();
+ virtual bool HasIntegrateMainThread () { return true; }
+ virtual void IntegrateMainThread ();
+
+ virtual float GetProgress ();
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName ()
+ {
+ static std::string debugName("AssetBundleCreateRequest");
+ return debugName;
+ }
+#endif
+
+ AssetBundle* GetAssetBundle()
+ {
+ return m_AssetBundle;
+ }
+
+ /// Set whether to perform runtime compatibility checks or not. Unfortunately, in the
+ /// editor we get lots of old asset bundles handed to us from the asset store to deliver
+ /// previews. Given that in the editor we can actually still run that content (unlike in
+ /// players), we allow disabling checks for those bundles.
+ ///
+ /// This is an internal feature only.
+ void SetEnableCompatibilityChecks (bool value)
+ {
+ m_EnableCompatibilityChecks = value;
+ }
+
+protected:
+ UnityWebStream* m_UnityWebStream;
+ PPtr<AssetBundle> m_AssetBundle;
+ bool m_EnableCompatibilityChecks;
+};
+#endif
+
+AssetBundle* ExtractAssetBundle(std::string const& assetBundlePathName);
+
+/// Return true if the given asset bundle can be used with the current player.
+/// If not, "error" will be set to a description of why the bundle cannot be used.
+/// The given "bundleName" is only used for printing more informative error
+/// messages.
+bool TestAssetBundleCompatibility (AssetBundle& bundle, const std::string& bundleName, std::string& error);
+
+Object* LoadNamedObjectFromAssetBundle (AssetBundle& bundle, const std::string& name, ScriptingObjectPtr type);
+Object* LoadMainObjectFromAssetBundle (AssetBundle& bundle);
+void LoadAllFromAssetBundle (AssetBundle& assetBundle, ScriptingObjectPtr type, std::vector<Object* >& output);
+
+struct AssetBundleRequestMono
+{
+ AsyncOperation* m_Result;
+ ScriptingObjectPtr m_AssetBundle;
+ ScriptingStringPtr m_Path;
+ ScriptingObjectPtr m_Type;
+};
+
+#endif // ASSETBUNDLEUTILITY_H
diff --git a/Runtime/Misc/AsyncOperation.cpp b/Runtime/Misc/AsyncOperation.cpp
new file mode 100644
index 0000000..5b05c0d
--- /dev/null
+++ b/Runtime/Misc/AsyncOperation.cpp
@@ -0,0 +1,47 @@
+#include "UnityPrefix.h"
+#include "AsyncOperation.h"
+
+void AsyncOperation::SetCoroutineCallback ( DelayedCall* func, Object* coroutineBehaviour, void* userData, CleanupUserData* cleanup)
+{
+ m_CoroutineBehaviour = coroutineBehaviour;
+ m_CoroutineDone = func;
+ m_CoroutineCleanup = cleanup;
+ m_CoroutineData = userData;
+}
+
+void AsyncOperation::InvokeCoroutine ()
+{
+ if (m_CoroutineDone != NULL)
+ {
+ Object* target = m_CoroutineBehaviour;
+ if (target)
+ m_CoroutineDone(target, m_CoroutineData);
+ m_CoroutineCleanup(m_CoroutineData);
+ m_CoroutineDone = NULL;
+ }
+}
+
+void AsyncOperation::CleanupCoroutine ()
+{
+ if (m_CoroutineDone != NULL)
+ {
+ m_CoroutineCleanup(m_CoroutineData);
+ m_CoroutineDone = NULL;
+ }
+}
+
+AsyncOperation::~AsyncOperation ()
+{
+ AssertIf(m_CoroutineDone != NULL);
+}
+
+void AsyncOperation::Retain ()
+{
+ m_RefCount.Retain();
+}
+
+void AsyncOperation::Release ()
+{
+ if (m_RefCount.Release())
+ delete this;
+}
diff --git a/Runtime/Misc/AsyncOperation.h b/Runtime/Misc/AsyncOperation.h
new file mode 100644
index 0000000..2ccb3e0
--- /dev/null
+++ b/Runtime/Misc/AsyncOperation.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "Runtime/Threads/Mutex.h"
+#include "Runtime/Threads/AtomicRefCounter.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+
+class AsyncOperation
+{
+ typedef void DelayedCall(Object* o, void* userData);
+ typedef void CleanupUserData (void* userData);
+
+ AtomicRefCounter m_RefCount;
+ DelayedCall* m_CoroutineDone;
+ CleanupUserData* m_CoroutineCleanup;
+ void* m_CoroutineData;
+ PPtr<Object> m_CoroutineBehaviour;
+public:
+
+ AsyncOperation () { m_CoroutineDone = NULL; }
+ virtual ~AsyncOperation ();
+ virtual bool IsDone () = 0;
+ virtual float GetProgress () = 0;
+
+ virtual int GetPriority () { return 0; }
+ virtual void SetPriority (int priority) { }
+
+ virtual bool GetAllowSceneActivation () { return true; }
+ virtual void SetAllowSceneActivation (bool allow) { }
+
+ void Retain ();
+ void Release ();
+
+ bool HasCoroutineCallback () { return m_CoroutineDone != NULL; }
+ void SetCoroutineCallback ( DelayedCall* func, Object* coroutineBehaviour, void* userData, CleanupUserData* cleanup);
+
+ void InvokeCoroutine ();
+ void CleanupCoroutine ();
+};
diff --git a/Runtime/Misc/BatchDeleteObjects.cpp b/Runtime/Misc/BatchDeleteObjects.cpp
new file mode 100644
index 0000000..07ab974
--- /dev/null
+++ b/Runtime/Misc/BatchDeleteObjects.cpp
@@ -0,0 +1,221 @@
+#include "UnityPrefix.h"
+#include "BatchDeleteObjects.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Threads/ThreadedStreamBuffer.h"
+
+PROFILER_INFORMATION(gBatchDeleteObjects, "BatchDeleteObjects", kProfilerLoading)
+PROFILER_INFORMATION(gBatchDeleteObjectsThread, "BatchDeleteObjectsThread", kProfilerLoading)
+
+// @TODO: On Angrybots on OS X. We spent 4ms deleting objects in singlethreaded mode on the main thread and 1ms in multithreaded mode on the main thread.
+#define MULTI_THREADED_DELETE 0
+
+// Some classes do not support being deallocated on the main thread.
+bool DoesClassRequireMainThreadDeallocation (int classID)
+{
+ if (classID == ClassID(Shader) || classID == ClassID(PhysicMaterial) || classID == ClassID(Mesh) ||
+ classID == ClassID(Texture) || classID == ClassID(Texture2D) || classID == ClassID(Texture3D) || classID == ClassID(Cubemap) )
+ return true;
+ else
+ return false;
+}
+
+#if MULTI_THREADED_DELETE
+
+#define kTerminateInstruction reinterpret_cast<Object*> (0x1)
+
+struct BatchDeleteManager;
+static BatchDeleteManager* gBatchDeleteManager = NULL;
+
+static void* BatchDeleteStep2Threaded (void* userData);
+
+struct BatchDeleteManager
+{
+ Thread thread;
+ ThreadedStreamBuffer streamBuffer;
+
+ ///////@TODO: Handle when we are out of ring buffer memory.
+
+ BatchDeleteManager ()
+ : streamBuffer (ThreadedStreamBuffer::kModeThreaded, 1024 * 256 * sizeof(Object*) )
+ {
+
+ }
+};
+
+void InitializeBatchDelete ()
+{
+ gBatchDeleteManager = UNITY_NEW(BatchDeleteManager, kMemGarbageCollector);
+ gBatchDeleteManager->thread.SetName ("BatchDeleteObjects");
+ gBatchDeleteManager->thread.Run(BatchDeleteStep2Threaded, mono_domain_get());
+}
+
+void CleanupBatchDelete ()
+{
+ // Send terminate instruction & wait for thread to complete
+ gBatchDeleteManager->streamBuffer.WriteValueType<Object*> (kTerminateInstruction);
+ gBatchDeleteManager->streamBuffer.WriteSubmitData ();
+
+ gBatchDeleteManager->thread.WaitForExit();
+
+ UNITY_DELETE(gBatchDeleteManager, kMemGarbageCollector);
+}
+
+BatchDelete CreateBatchDelete (size_t size)
+{
+ size_t allocationSize = sizeof(Object*)*size;
+ void* objectArray = gBatchDeleteManager->streamBuffer.GetWriteDataPointer (allocationSize, sizeof(Object*));
+
+ BatchDelete batchInfo;
+ batchInfo.reservedObjectCount = size;
+ batchInfo.objectCount = 0;
+ batchInfo.objects = reinterpret_cast<Object**> (objectArray);
+
+ return batchInfo;
+}
+
+static void* BatchDeleteStep2Threaded (void* userData)
+{
+ // Attach mono thread
+ MonoThread* thread = mono_thread_attach((MonoDomain*)userData);
+
+ ThreadedStreamBuffer& threadedStreamBuffer = gBatchDeleteManager->streamBuffer;
+
+ while (true)
+ {
+ Object* object = threadedStreamBuffer.ReadValueType<Object*> ();
+
+ // Terminate instruction. Stop the thread.
+ if (object == kTerminateInstruction)
+ return NULL;
+
+ // Delete the object
+ if (object != NULL)
+ delete_object_internal_step2 (object);
+
+ threadedStreamBuffer.ReadReleaseData ();
+ }
+
+ // Detach mono thread
+ mono_thread_detach(thread);
+
+ return NULL;
+}
+
+void SharkBeginRemoteProfiling ();
+void SharkEndRemoteProfiling ();
+
+/// Callbacks like ScriptableObject.OnDestroy etc must be called before invoking this function.
+void BatchDeleteObjectInternal (const SInt32* unloadObjects, int size)
+{
+ if (size == 0)
+ return;
+
+ PROFILER_AUTO(gBatchDeleteObjects, NULL)
+
+ BatchDelete batch = CreateBatchDelete (size);
+ int destroyObjectIndex = 0;
+ for (int i=0;i<size;i++)
+ {
+ Object* object = Object::IDToPointer(unloadObjects[i]);
+ batch.objects[destroyObjectIndex] = object;
+ }
+ batch.objectCount = destroyObjectIndex;
+
+ CommitBatchDelete(batch);
+}
+
+void CommitBatchDelete (BatchDelete& batchDelete)
+{
+ Assert(batchDelete.reservedObjectCount >= batchDelete.objectCount);
+
+ LockObjectCreation();
+
+ for (int i=0;i<batchDelete.objectCount;i++)
+ {
+ Object* object = batchDelete.objects[i];
+
+ if (object == NULL)
+ continue;
+
+ delete_object_internal_step1 (object);
+
+ int classID = object->GetClassID();
+ if (DoesClassRequireMainThreadDeallocation (classID))
+ {
+ bool requiresThreadCleanup = object->MainThreadCleanup ();
+ /// DoesClassRequireMainThreadDeallocation does not agree with MainThreadCleanup
+ /// Fix DoesClassRequireMainThreadDeallocation or MainThreadCleanup for that class.
+ Assert(requiresThreadCleanup);
+ }
+ }
+
+ for (int i=batchDelete.objectCount;i<batchDelete.reservedObjectCount;i++)
+ batchDelete.objects[i] = NULL;
+
+ UnlockObjectCreation();
+
+ gBatchDeleteManager->streamBuffer.WriteSubmitData ();
+}
+
+
+#else
+
+BatchDelete CreateBatchDelete (size_t size)
+{
+ BatchDelete batch;
+
+ batch.objects = (Object**)UNITY_MALLOC(kMemGarbageCollector, sizeof(Object*)*size);
+ batch.reservedObjectCount = size;
+ batch.objectCount = 0;
+
+ return batch;
+}
+
+void CommitBatchDelete (BatchDelete& batchDelete)
+{
+ PROFILER_AUTO(gBatchDeleteObjectsThread, NULL)
+
+ Assert(batchDelete.reservedObjectCount >= batchDelete.objectCount);
+
+ LockObjectCreation();
+
+ for (int i=0;i<batchDelete.objectCount;i++)
+ {
+ Object* object = batchDelete.objects[i];
+ if (object != NULL)
+ {
+ delete_object_internal_step1 (object);
+ delete_object_internal_step2 (object);
+ }
+ }
+
+ UnlockObjectCreation();
+
+ // Cleanup temp storage
+ UNITY_FREE(kMemGarbageCollector, batchDelete.objects);
+}
+
+void BatchDeleteObjectInternal (const SInt32* unloadObjects, int size)
+{
+ PROFILER_AUTO(gBatchDeleteObjectsThread, NULL)
+
+ BatchDelete batchInfo = CreateBatchDelete (size);
+ batchInfo.objectCount = size;
+ for (int i=0;i<size;i++)
+ batchInfo.objects[i] = Object::IDToPointer(unloadObjects[i]);
+
+ CommitBatchDelete (batchInfo);
+}
+
+void InitializeBatchDelete ()
+{
+}
+
+void CleanupBatchDelete ()
+{
+}
+
+#endif
diff --git a/Runtime/Misc/BatchDeleteObjects.h b/Runtime/Misc/BatchDeleteObjects.h
new file mode 100644
index 0000000..096e9a1
--- /dev/null
+++ b/Runtime/Misc/BatchDeleteObjects.h
@@ -0,0 +1,31 @@
+#pragma once
+
+class Object;
+
+struct BatchDelete
+{
+ size_t reservedObjectCount;
+ size_t objectCount;
+ Object** objects;
+};
+
+// Creates a batch delete object. When the object array has been filled (All Object* must be set. The function does not set them to null)
+// You can call CommitBatchDelete which will make the deletion thread delete the objects.
+BatchDelete CreateBatchDelete (size_t size);
+
+// Makes the batch delete
+void CommitBatchDelete (BatchDelete& batchDelete);
+
+
+/// Deletes an array of objects identified by instanceID. Deallocation is done on another thread.
+/// Callbacks like ScriptableObject.OnDestroy etc must be called before invoking this function.
+void BatchDeleteObjectInternal (const SInt32* unloadObjects, int size);
+
+
+// Used by the batch deletion to figure out if a specific class must be deallocated on the main thread. Eg. Textures need to be deallocate on the main thread
+// Object::MainThreadCleanup will be called if this per class check returns true.
+bool DoesClassRequireMainThreadDeallocation (int classID);
+
+
+void InitializeBatchDelete ();
+void CleanupBatchDelete (); \ No newline at end of file
diff --git a/Runtime/Misc/BuildSettings.cpp b/Runtime/Misc/BuildSettings.cpp
new file mode 100644
index 0000000..2c4ad74
--- /dev/null
+++ b/Runtime/Misc/BuildSettings.cpp
@@ -0,0 +1,188 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "BuildSettings.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/ErrorExit.h"
+
+int kUnityVersion3_0_0a1 = GetNumericVersion("3.0.0.a1");
+int kUnityVersion3_2_a1 = GetNumericVersion("3.2.0a1");
+int kUnityVersion3_3_a1 = GetNumericVersion("3.3.0a1");
+int kUnityVersion3_4_a1 = GetNumericVersion("3.4.0a1");
+int kUnityVersion3_5_a1 = GetNumericVersion("3.5.0a1");
+int kUnityVersion3_5_3_a1 = GetNumericVersion("3.5.3a1");
+int kUnityVersion4_0_a1 = GetNumericVersion("4.0.0a1");
+int kUnityVersion4_1_a1 = GetNumericVersion("4.1.0a1");
+int kUnityVersion4_1_1_a1 = GetNumericVersion("4.1.1a1");
+int kUnityVersion4_1_a3 = GetNumericVersion("4.1.0a3");
+int kUnityVersion4_2_a1 = GetNumericVersion("4.2.0a1");
+int kUnityVersion4_3_a1 = GetNumericVersion("4.3.0a1");
+int kUnityVersion_OldWebResourcesAdded = GetNumericVersion("4.2.0a1");
+
+BuildSettings::BuildSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ hasRenderTexture = true;
+ hasPROVersion = true;
+ hasAdvancedVersion = true;
+ hasShadows = true;
+ hasSoftShadows = true;
+ hasLocalLightShadows = true;
+ isNoWatermarkBuild = false;
+ isPrototypingBuild = false;
+ isEducationalBuild = false;
+ isEmbedded = false;
+ hasPublishingRights = true;
+ isDebugBuild = true;
+ usesOnMouseEvents = true;
+ enableDynamicBatching = true;
+ #if UNITY_EDITOR
+ m_Version = UNITY_VERSION;
+ #else
+ m_Version = "1.6.0";
+ #endif
+ m_IntVersion = GetNumericVersion(m_Version);
+}
+
+BuildSettings::~BuildSettings ()
+{
+}
+
+string BuildSettings::GetLevelPathName (int index)
+{
+ if (index < remappedLevels.size () && index >= 0)
+ return remappedLevels[index];
+ else
+ return string ();
+}
+
+string BuildSettings::GetLevelPathName (const string& name)
+{
+ return GetLevelPathName (GetLevelIndex (name));
+}
+
+string BuildSettings::GetLevelName (int index)
+{
+ if (index < levels.size () && index >= 0)
+ return DeletePathNameExtension (GetLastPathNameComponent(levels[index]));
+ else
+ return string ();
+}
+
+int BuildSettings::GetLevelIndex (const string& name)
+{
+ for (int i=0;i<levels.size ();i++)
+ {
+ string curName = levels[i];
+ curName = DeletePathNameExtension (GetLastPathNameComponent(curName));
+ if (StrICmp (name, curName) == 0)
+ return i;
+ }
+ return -1;
+}
+
+int BuildSettings::GetLevelIndexChecked (const string& name)
+{
+ int index = GetLevelIndex (name);
+ if (index == -1)
+ {
+ ErrorString (Format ("Level %s couldn't be found because it has not been added to the build settings.\nTo add a level to the build settings use the menu File->Build Settings...", name.c_str()));
+ }
+ return index;
+}
+
+UInt32 BuildSettings::GetHashOfClass (int classID) const
+{
+ ClassHashCont::const_iterator it = runtimeClassHashes.find (classID);
+ if (it == runtimeClassHashes.end ())
+ return 0;
+
+ return it->second;
+}
+
+template<class TransferFunction>
+void BuildSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+ TRANSFER (levels);
+ TRANSFER (hasRenderTexture);
+ TRANSFER (hasPROVersion);
+ TRANSFER (isNoWatermarkBuild);
+ TRANSFER (isPrototypingBuild);
+ TRANSFER (isEducationalBuild);
+ TRANSFER (isEmbedded);
+ TRANSFER (hasPublishingRights);
+ TRANSFER (hasShadows);
+ TRANSFER (hasSoftShadows);
+ TRANSFER (hasLocalLightShadows);
+ TRANSFER (hasAdvancedVersion);
+ TRANSFER (enableDynamicBatching);
+ TRANSFER (isDebugBuild);
+ TRANSFER (usesOnMouseEvents);
+ transfer.Align();
+
+ if (transfer.IsOldVersion(1))
+ {
+ hasPROVersion = hasRenderTexture;
+ }
+
+ // Only in game build mode we transfer the version,
+ // in the editor it is always the latest one
+ if (transfer.IsSerializingForGameRelease())
+ {
+ TRANSFER (m_Version);
+ TRANSFER (m_AuthToken);
+
+ if(transfer.IsReading()) {
+ if(GetNumericVersion(m_Version) < kUnityVersion3_0_0a1) {
+ // We're attempting to load a version of unity that we don't have
+ // backwards compatibility for.
+ ExitWithErrorCode(kErrorIncompatibleRuntimeVersion);
+ }
+ }
+ }
+
+ // We need hashed type trees only for the player
+ if (transfer.IsSerializingForGameRelease())
+ {
+ TRANSFER (runtimeClassHashes);
+ }
+}
+
+void BuildSettings::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ remappedLevels = levels;
+ #if GAMERELEASE
+ if (remappedLevels.empty ())
+ {
+ remappedLevels.push_back ("mainData");
+ levels.push_back ("");
+ }
+ else
+ {
+ remappedLevels[0] = "mainData";
+ for (int i=1;i<remappedLevels.size ();i++)
+ remappedLevels[i] = Format ("level%d", i - 1);
+ }
+ #endif
+
+#if UNITY_WINRT
+ // On Metro you switch between debug/release in VS solution,
+ // so we need isDebugBuild to dependant on the player, not on the settings
+# if UNITY_DEVELOPER_BUILD
+ isDebugBuild = true;
+# else
+ isDebugBuild = false;
+# endif
+#endif
+ m_IntVersion = GetNumericVersion(m_Version);
+}
+
+GET_MANAGER(BuildSettings)
+GET_MANAGER_PTR(BuildSettings)
+IMPLEMENT_CLASS (BuildSettings)
+IMPLEMENT_OBJECT_SERIALIZE (BuildSettings)
diff --git a/Runtime/Misc/BuildSettings.h b/Runtime/Misc/BuildSettings.h
new file mode 100644
index 0000000..ed8f116
--- /dev/null
+++ b/Runtime/Misc/BuildSettings.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/vector_map.h"
+
+class BuildSettings : public GlobalGameManager
+{
+ public:
+
+ REGISTER_DERIVED_CLASS (BuildSettings, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (BuildSettings)
+
+ BuildSettings (MemLabelId label, ObjectCreationMode mode);
+
+ std::vector<UnityStr> levels;
+ std::vector<UnityStr> remappedLevels;
+ typedef vector_map<int, UInt32> ClassHashCont;
+ ClassHashCont runtimeClassHashes;
+
+ bool hasRenderTexture;
+ bool hasPROVersion;
+ bool hasAdvancedVersion; // PRO version for the active platform.
+ bool enableDynamicBatching;
+
+ bool isNoWatermarkBuild;
+ bool isPrototypingBuild;
+ bool isEducationalBuild;
+ bool isEmbedded;
+ bool hasPublishingRights;
+ bool hasShadows;
+ bool hasSoftShadows;
+ bool hasLocalLightShadows;
+ bool isDebugBuild;
+ bool usesOnMouseEvents;
+
+ UnityStr m_AuthToken;
+
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ std::string GetLevelPathName (int index);
+ std::string GetLevelPathName (const std::string& name);
+ string GetLevelName (int index);
+
+ int GetLevelIndex (const std::string& name);
+ int GetLevelIndexChecked (const string& name);
+
+ int GetIntVersion () { return m_IntVersion; }
+ const UnityStr& GetVersion () { return m_Version; }
+
+ UInt32 GetHashOfClass (int classID) const;
+
+ private:
+
+ int m_IntVersion;
+ UnityStr m_Version;
+};
+
+BuildSettings& GetBuildSettings();
+BuildSettings* GetBuildSettingsPtr();
+
+extern int kUnityVersion3_2_a1;
+extern int kUnityVersion3_3_a1;
+extern int kUnityVersion3_4_a1;
+extern int kUnityVersion3_5_a1;
+extern int kUnityVersion3_5_3_a1;
+extern int kUnityVersion4_0_a1;
+extern int kUnityVersion4_1_a1;
+extern int kUnityVersion4_1_a3;
+extern int kUnityVersion4_1_1_a1;
+extern int kUnityVersion4_2_a1;
+extern int kUnityVersion4_3_a1;
+extern int kUnityVersion_OldWebResourcesAdded;
+
+// The NaCl runtime will always be included with the player files,
+// so in practice the version will always match.
+#if !WEBPLUG || (UNITY_NACL && !UNITY_NACL_WEBPLAYER)
+#define IS_CONTENT_NEWER_OR_SAME(val) true
+#else
+#define IS_CONTENT_NEWER_OR_SAME(val) (GetBuildSettingsPtr() && GetBuildSettings().GetIntVersion() >= val)
+#endif
diff --git a/Runtime/Misc/CPUInfo.cpp b/Runtime/Misc/CPUInfo.cpp
new file mode 100644
index 0000000..0d67baf
--- /dev/null
+++ b/Runtime/Misc/CPUInfo.cpp
@@ -0,0 +1,120 @@
+#include "UnityPrefix.h"
+#include "CPUInfo.h"
+#if UNITY_ANDROID
+ #include <cpu-features.h>
+#endif
+
+bool CPUInfo::m_Initialized = false;
+bool CPUInfo::m_IsSSESupported = false;
+bool CPUInfo::m_IsSSE2Supported = false;
+bool CPUInfo::m_IsSSE3Supported = false;
+bool CPUInfo::m_IsSupplementalSSE3Supported = false;
+bool CPUInfo::m_IsSSE41Supported = false;
+bool CPUInfo::m_IsSSE42Supported = false;
+bool CPUInfo::m_IsAVXSupported = false;
+bool CPUInfo::m_IsAVX2Supported = false;
+bool CPUInfo::m_IsAVX512Supported = false;
+bool CPUInfo::m_IsFP16CSupported = false;
+bool CPUInfo::m_IsFMASupported = false;
+bool CPUInfo::m_IsNEONSupported = false;
+
+unsigned int CPUInfo::m_CPUIDFeatures = 0;
+
+
+#if UNITY_SUPPORTS_SSE
+
+static inline UInt64 xgetbv_impl()
+{
+# if defined(__GNUC__) || defined(__clang__)
+ UInt32 eax, edx;
+
+ __asm __volatile (
+ ".byte 0x0f, 0x01, 0xd0" // xgetbv instruction isn't supported by some older assemblers, so just emit it raw
+ : "=a"(eax), "=d"(edx) : "c"(0)
+ );
+
+ return ((UInt64)edx << 32) | eax;
+# elif defined(_MSC_VER) && !UNITY_XENON
+ return _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
+# else
+ return 0;
+# endif
+}
+
+static void cpuid_ex_impl(UInt32 eax, UInt32 ecx, UInt32* abcd)
+{
+ #if defined(_MSC_VER)
+
+ __cpuidex((int*)abcd, eax, ecx);
+
+ #else
+ UInt32 ebx, edx;
+ #if defined(__i386__) && defined (__PIC__)
+ // for PIC under 32 bit: EBX can't be modified
+ __asm__ ("movl %%ebx, %%edi \n\t cpuid \n\t xchgl %%ebx, %%edi" : "=D" (ebx), "+a" (eax), "+c" (ecx), "=d" (edx));
+ #else
+ __asm__ ("cpuid" : "+b" (ebx), "+a" (eax), "+c" (ecx), "=d" (edx));
+ #endif
+ abcd[0] = eax; abcd[1] = ebx; abcd[2] = ecx; abcd[3] = edx;
+ #endif
+}
+
+#endif // UNITY_SUPPORTS_SSE
+
+
+CPUInfo::CPUInfo()
+{
+#if UNITY_SUPPORTS_SSE
+ int data[4] = {0};
+
+ // Add more code here to extract vendor string or what ever is needed
+ __cpuid (data, 0);
+ unsigned int cpuData0 = data[0];
+ unsigned int cpuInfo2 = 0;
+ if (cpuData0 >= 1) {
+ __cpuid(data, 1);
+ cpuInfo2 = data[2];
+ m_CPUIDFeatures = data[3];
+ }
+
+ // SSE/2 support
+ m_IsSSESupported = (m_CPUIDFeatures & CPUID_FEATURES_SSE) != 0;
+ m_IsSSE2Supported = (m_CPUIDFeatures & CPUID_FEATURES_SSE2) != 0;
+
+ // SSE 3.x
+ m_IsSSE3Supported = ((cpuInfo2 & (1<<0)) != 0);
+ m_IsSupplementalSSE3Supported = ((cpuInfo2 & (1<<9)) != 0);
+
+ // SSE 4.x support
+ m_IsSSE41Supported = ((cpuInfo2 & (1<<19)) != 0);
+ m_IsSSE42Supported = ((cpuInfo2 & (1<<20)) != 0);
+
+ // AVX support
+ m_IsAVXSupported =
+ ((cpuInfo2 & (1<<28)) != 0) && // AVX support in CPU
+ ((cpuInfo2 & (1<<27)) != 0) && // OS support for AVX (XSAVE/XRESTORE on context switches)
+ ((xgetbv_impl() & 6) == 6); // XMM & YMM registers will be preserved on context switches
+
+ if (m_IsAVXSupported)
+ {
+ if (cpuData0 >= 7)
+ {
+ UInt32 regs7[4] = {0};
+ cpuid_ex_impl(0x7, 0, regs7);
+ m_IsAVX2Supported = ((regs7[1] & (1<<5)) != 0);
+ m_IsAVX512Supported = ((regs7[1] & (1<<16)) != 0);
+ }
+ }
+
+ m_IsFP16CSupported = ((cpuInfo2 & (1<<29)) != 0);
+ m_IsFMASupported = ((cpuInfo2 & (1<<12)) != 0);
+
+#elif UNITY_ANDROID && UNITY_SUPPORTS_NEON
+ m_CPUIDFeatures = android_getCpuFeatures();
+ m_IsNEONSupported = (m_CPUIDFeatures & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+#endif
+
+ m_Initialized = true;
+}
+
+CPUInfo g_cpuInfo;
diff --git a/Runtime/Misc/CPUInfo.h b/Runtime/Misc/CPUInfo.h
new file mode 100644
index 0000000..840465a
--- /dev/null
+++ b/Runtime/Misc/CPUInfo.h
@@ -0,0 +1,117 @@
+#ifndef CPUINFO_H
+#define CPUINFO_H
+
+#define CPUID_FEATURES_SSE (1 << 25)
+#define CPUID_FEATURES_SSE2 (1 << 26)
+
+#if defined(_MSC_VER) && !UNITY_XENON
+
+// define __cpuid intrinsic
+#include <intrin.h>
+
+#elif defined(__GNUC__) && UNITY_OSX && UNITY_SUPPORTS_SSE
+
+// This has to be implemented in a PIC friendly way or osx web players will not compile
+// http://sam.zoy.org/blog/2007-04-13-shlib-with-non-pic-code-have-inlin
+
+#define __cpuid(array, func) \
+{ \
+ __asm__ __volatile__("pushl %%ebx \n\t" /* save %ebx */ \
+ "cpuid \n\t" \
+ "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */ \
+ "popl %%ebx \n\t" /* restore the old %ebx */ \
+ : "=a"(array[0]), "=r"(array[1]), "=c"(array[2]), "=d"(array[3]) \
+ : "a"(func) \
+ : "cc");\
+}
+
+#else
+#define __cpuid(a, b)
+#endif
+
+
+class CPUInfo
+{
+public:
+ CPUInfo();
+
+ inline unsigned int GetCPUIDFeatures() {return m_CPUIDFeatures;}
+
+#ifdef UNITY_SUPPORTS_SSE
+# if UNITY_AUTO_DETECT_VECTOR_UNIT
+ static __forceinline bool HasSSESupport()
+ {
+ AssertMsg(m_Initialized, "CPUInfo has not been initialized");
+ return m_IsSSESupported;
+ }
+
+ static __forceinline bool HasSSE2Support()
+ {
+ AssertMsg(m_Initialized, "CPUInfo has not been initialized");
+ return m_IsSSE2Supported;
+ }
+
+# else
+ static inline bool HasSSESupport()
+ {
+ return true;
+ }
+
+ static inline bool HasSSE2Support()
+ {
+ return true;
+ }
+# endif
+#endif
+
+ static inline bool HasSSE3Support() { return m_IsSSE3Supported; }
+ static inline bool HasSupplementalSSE3Support() { return m_IsSupplementalSSE3Supported; }
+ static inline bool HasSSE41Support() { return m_IsSSE41Supported; }
+ static inline bool HasSSE42Support() { return m_IsSSE42Supported; }
+ static inline bool HasAVXSupport() { return m_IsAVXSupported; }
+ static inline bool HasAVX2Support() { return m_IsAVX2Supported; }
+ static inline bool HasAVX512Support() { return m_IsAVX512Supported; }
+ static inline bool HasFP16CSupport() { return m_IsFP16CSupported; }
+ static inline bool HasFMASupport() { return m_IsFMASupported; }
+
+#if UNITY_SUPPORTS_NEON
+ #if UNITY_ANDROID
+ static inline bool HasNEONSupport()
+ {
+ AssertMsg(m_Initialized, "CPUInfo has not been initialized");
+ return m_IsNEONSupported;
+ }
+ #else
+ static inline bool HasNEONSupport()
+ {
+ return true;
+ }
+ #endif
+#else
+ static inline bool HasNEONSupport()
+ {
+ return false;
+ }
+#endif
+
+private:
+ static bool m_Initialized;
+
+ static bool m_IsSSESupported;
+ static bool m_IsSSE2Supported;
+ static bool m_IsSSE3Supported;
+ static bool m_IsSupplementalSSE3Supported;
+ static bool m_IsSSE41Supported; // SSE 4.1
+ static bool m_IsSSE42Supported; // SSE 4.2
+ static bool m_IsAVXSupported;
+ static bool m_IsAVX2Supported;
+ static bool m_IsAVX512Supported;
+ static bool m_IsFP16CSupported; // FP16 conversions supported
+ static bool m_IsFMASupported; // fused multiply-add instructions
+
+ static bool m_IsNEONSupported;
+
+ static unsigned int m_CPUIDFeatures;
+
+};
+#endif // CPUINFO_H
diff --git a/Runtime/Misc/CachingManager.cpp b/Runtime/Misc/CachingManager.cpp
new file mode 100644
index 0000000..9945942
--- /dev/null
+++ b/Runtime/Misc/CachingManager.cpp
@@ -0,0 +1,1207 @@
+#include "UnityPrefix.h"
+#include "CachingManager.h"
+
+#if ENABLE_CACHING
+
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "PlatformDependent/CommonWebPlugin/Verification.h"
+#include "PlayerSettings.h"
+#include "Runtime/Utilities/GUID.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/File/ApplicationSpecificPersistentDataPath.h"
+#include "Runtime/Threads/ThreadUtility.h"
+
+#include <time.h>
+
+static const char *kInfoFileName = "__info";
+
+#if WEBPLUG && !UNITY_PEPPER
+#include "Runtime/Utilities/GlobalPreferences.h"
+static const char *kCachingEnabledKey = "CachingEnabled";
+#endif
+
+#if UNITY_PS3
+#include <cell/cell_fs.h>
+extern "C" const char* GetBasePath();
+#endif
+
+#if UNITY_XENON
+#include "XenonPaths.h"
+#endif
+
+#if UNITY_OSX || UNITY_LINUX
+#include <sys/stat.h>
+#elif UNITY_IPHONE
+#include "PlatformDependent/iPhonePlayer/iPhoneMisc.h"
+namespace iphone {
+ std::string GetUserDirectory();
+}
+#elif UNITY_WINRT
+#include "PlatformDependent/MetroPlayer/MetroUtils.h"
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#elif UNITY_WIN && !UNITY_WINRT
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#include "Shlobj.h"
+
+#define kDefaultPathBufferSize MAX_PATH*4
+
+#define UNITY_DEFINE_KNOWN_FOLDER(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ EXTERN_C const GUID DECLSPEC_SELECTANY name \
+ = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+UNITY_DEFINE_KNOWN_FOLDER(FOLDERID_LocalAppDataLow, 0xA520A1A4, 0x1780, 0x4FF6, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16);
+
+static std::string gWinCacheBasePath;
+
+string GetWindowsCacheBasePath()
+{
+ if (gWinCacheBasePath.empty())
+ {
+ wchar_t *widePath = NULL;
+
+ HRESULT gotPath;
+
+ typedef HRESULT WINAPI SHGetKnownFolderPathFn(REFGUID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
+ HMODULE hShell32 = LoadLibrary(TEXT("shell32.dll"));
+ if(hShell32)
+ {
+ SHGetKnownFolderPathFn* pfnSHGetKnownFolderPath = reinterpret_cast<SHGetKnownFolderPathFn*>(GetProcAddress(hShell32, "SHGetKnownFolderPath"));
+ if(pfnSHGetKnownFolderPath)
+ {
+ gotPath = pfnSHGetKnownFolderPath(FOLDERID_LocalAppDataLow, NULL, NULL, &widePath);
+ }
+ else
+ {
+ widePath = (wchar_t*)CoTaskMemAlloc(MAX_PATH);
+ gotPath = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, widePath);
+ }
+ if (!FreeLibrary(hShell32))
+ printf_console("Error while freeing shell32 library");;
+ }
+
+ if (SUCCEEDED(gotPath))
+ {
+ ConvertWindowsPathName( widePath, gWinCacheBasePath);
+ }
+
+ if (widePath)
+ CoTaskMemFree(widePath);
+ }
+ else
+ {
+ }
+ return gWinCacheBasePath;
+}
+
+void CreateDirectoryHelper( const std::string& path )
+{
+ std::wstring widePath;
+ ConvertUnityPathName( path, widePath );
+ CreateDirectoryW( widePath.c_str(), NULL );
+}
+#endif
+
+int GetFolderSizeRecursive (const string& path)
+{
+ if (!IsDirectoryCreated (path))
+ return 0;
+
+ set<string> paths;
+ if (!GetFolderContentsAtPath (path, paths))
+ return 0;
+
+ int size = 0;
+ for (set<string>::iterator i = paths.begin(); i != paths.end(); i++)
+ {
+ if (IsDirectoryCreated(*i))
+ size += GetFolderSizeRecursive(*i);
+ else
+ size += GetFileLength (*i);
+ }
+
+ return size;
+}
+
+string Cache::URLToPath (const string& url, int version)
+{
+ string source = GetLastPathNameComponent(url);
+
+ // strip url parameters
+ source = source.substr(0, source.find("?"));
+
+ if (m_IncludePlayerURL)
+ source += GetPlayerSettings().absoluteURL;
+
+ if (version != 0)
+ source += Format("@%d", version);
+
+ return GenerateHash ((UInt8*)&(source[0]), source.size());
+}
+
+
+
+string GetPlatformCachePath()
+{
+#if UNITY_OSX
+ string result = getenv ("HOME");
+ if (result.empty())
+ return string();
+
+#if WEBPLUG || UNITY_EDITOR
+ return AppendPathName( result, "Library/Caches/Unity");
+#else
+ // In Standalones use Library/Caches/BundleID to match App Store Requirements
+ return AppendPathName( result, "Library/Caches");
+#endif
+
+#elif UNITY_LINUX
+ string result = getenv ("HOME");
+ if (!result.empty())
+ return string();
+
+ return AppendPathName( result, ".unity/cache");
+#elif UNITY_WINRT
+ Platform::String^ path = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
+ return AppendPathName(ConvertToUnityPath(path), "UnityCache");
+#elif UNITY_WIN && !UNITY_WINRT
+ std::string result = GetWindowsCacheBasePath();
+ if (result.empty())
+ return result;
+
+ result = AppendPathName( result, "Unity" );
+
+ // "Web Player" is wrong, the directory should be "WebPlayer", for consistency with other Unity usage.
+ // but if there exits an old "Web Player" directory, use that. If both, a new and old one exists,
+ // use the new one, as the old one has probably been created by a FusionFall build after running content with
+ // the fixed web player.
+ string oldCachePath = AppendPathName( result, "Web Player/Cache" );
+ string newCachePath = AppendPathName( result, "WebPlayer/Cache" );
+ if (IsDirectoryCreated (newCachePath))
+ return newCachePath;
+ else if (IsDirectoryCreated (oldCachePath))
+ return oldCachePath;
+ else
+ return newCachePath;
+
+#elif UNITY_IPHONE
+
+ // under ios5 guidelines we should use Library folder for downloaded content
+ // we don't want to use caches as they might be purged anyway
+ // check apple's Technical Q&A QA1719
+ std::string result = iphone::GetLibraryDirectory();
+ return result.empty() ? result : AppendPathName( result, "UnityCache" );
+
+#elif UNITY_PS3
+
+ return AppendPathName( GetBasePath(), "/Cache/" );
+
+#else
+ string result = systeminfo::GetPersistentDataPath();
+ if (result.empty())
+ return result;
+
+ return AppendPathName( result, "UnityCache" );
+#endif
+}
+
+std::string GetDefaultApplicationIdentifierForCache(bool broken)
+{
+ // * Webplayer defaults to the Shared cache
+ // * iOS / Android have dedicated cache folders per app managed by the OS -> thus end up in "Shared" as well.
+ if (WEBPLUG || !UNITY_EMULATE_PERSISTENT_DATAPATH)
+ return "Shared";
+ else
+ {
+ std::string companyName = GetPlayerSettings().companyName;
+ std::string productName = GetPlayerSettings().productName;
+ if (broken)
+ {
+ ConvertToLegalPathNameBroken(companyName);
+ ConvertToLegalPathNameBroken(productName);
+#if UNITY_OSX && !UNITY_EDITOR
+ // In the standalone, we changed the base path to Library/Caches without /Unity,
+ // to match app store requirements. Still find old caches in Unity folder.
+ return AppendPathName ("Unity",companyName + "_" + productName);
+#endif
+ }
+ else
+ {
+#if UNITY_OSX && !UNITY_EDITOR
+ // In the OS X standalone, return the bundle ID to match App Store requirements.
+ return CFStringToString(CFBundleGetIdentifier(CFBundleGetMainBundle()));
+#endif
+ ConvertToLegalPathNameCorrectly(companyName);
+ ConvertToLegalPathNameCorrectly(productName);
+ }
+ return companyName + "_" + productName;
+ }
+}
+
+string GetCachingManagerPath(const std::string& path, bool createPath)
+{
+ std::string cachePath = GetPlatformCachePath();
+ if (path.empty() && !createPath)
+ return cachePath;
+
+ string fullPath = AppendPathName(cachePath, path);
+ if (!createPath)
+ return fullPath;
+
+ if (CreateDirectoryRecursive(fullPath))
+ return fullPath;
+ else
+ return string();
+}
+
+bool CachingManager::MoveTempFolder (const string& from, const string& to)
+{
+#if UNITY_OSX || UNITY_IPHONE || UNITY_ANDROID || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN
+ if (rename(from.c_str(), to.c_str()))
+#elif UNITY_WIN
+ std::wstring wideFrom, wideTo;
+ ConvertUnityPathName(from, wideFrom);
+ ConvertUnityPathName(to, wideTo);
+ if (!MoveFileExW(wideFrom.c_str(), wideTo.c_str(), MOVEFILE_WRITE_THROUGH))
+#elif UNITY_PS3
+ if (cellFsRename(from.c_str(), to.c_str()) != CELL_FS_SUCCEEDED)
+#elif UNITY_XENON
+ char charFrom[kDefaultPathBufferSize];
+ char charTo[kDefaultPathBufferSize];
+
+ DeleteFileOrDirectory( to.c_str() );
+ ConvertUnityPathName( from.c_str(), charFrom, kDefaultPathBufferSize );
+ ConvertUnityPathName( to.c_str(), charTo, kDefaultPathBufferSize );
+ if (!MoveFile(charFrom, charTo))
+#elif UNITY_PEPPER
+ //todo.
+#else
+#error
+#endif
+ {
+ return false;
+ }
+ SetFileFlags(to, kFileFlagTemporary, 0);
+ return true;
+}
+
+time_t CachingManager::GenerateTimeStamp ()
+{
+ return time(NULL);
+}
+
+void AsyncCachedUnityWebStream::LoadCachedUnityWebStream ()
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (m_Url, m_Version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+ if (path.empty())
+ {
+ m_Error = "Cannot find cache folder for "+ m_Url;
+ return;
+ }
+
+ vector<string> fileNames;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileNames))
+ {
+ m_Error = "Could not read __info file.";
+ return;
+ }
+
+ // update timestamp.
+ time_t timestamp = CachingManager::GenerateTimeStamp ();
+ CachingManager::WriteInfoFile (path, fileNames, timestamp);
+ GetCachingManager().GetCurrentCache().UpdateTimestamp(path, timestamp);
+
+ if (m_RequestedCRC)
+ {
+ UInt32 crc = CRCBegin();
+ for (vector<string>::iterator i = fileNames.begin(); i != fileNames.end(); i++)
+ {
+ File fd;
+ string filepath = AppendPathName(path, *i);
+ if (fd.Open (filepath, File::kReadPermission))
+ {
+ const size_t kHashReadBufferSize = 64*1024;
+ UInt8* buffer = (UInt8*)UNITY_MALLOC(kMemTempAlloc,kHashReadBufferSize*sizeof(UInt8));
+ size_t len = GetFileLength(filepath);
+ size_t offset = 0;
+ do
+ {
+ size_t readSize = fd.Read (buffer, kHashReadBufferSize);
+ offset += readSize;
+ crc = CRCFeed (crc, buffer, readSize);
+ }
+ while (offset < len);
+ UNITY_FREE(kMemTempAlloc,buffer);
+ fd.Close ();
+ }
+ }
+ crc = CRCDone(crc);
+ if (crc != m_RequestedCRC)
+ {
+ DeleteFileOrDirectory(path);
+ m_Error = Format("CRC Mismatch. Expected %lx, got %lx. Will not load cached AssetBundle", m_RequestedCRC, crc);
+ return;
+ }
+ }
+
+ PersistentManager &pm = GetPersistentManager();
+ pm.Lock();
+ for (vector<string>::iterator i = fileNames.begin(); i != fileNames.end(); i++)
+ {
+ if (pm.IsStreamLoaded(*i))
+ {
+ pm.Unlock();
+ m_Error = "Cannot load cached AssetBundle. A file of the same name is already loaded from another AssetBundle.";
+ m_AssetBundleWithSameNameIsAlreadyLoaded = true;
+ return;
+ }
+
+ if (!pm.LoadCachedFile(*i, AppendPathName(path, *i)))
+ {
+ pm.Unlock();
+ m_Error = "Cannot load cached AssetBundle.";
+ return;
+ }
+ }
+ pm.Unlock();
+
+ // Load resource file from cached path
+ const string &firstPath = fileNames.front();
+
+ int fileID = 1;
+ Assert(m_LoadingAssetBundle == NULL);
+
+ // Check if the first object is PreloadData.
+ // If it's true, the stream is a scene assetBundle, just load the AssetBundle from the 2nd object.
+ if (pm.GetClassIDFromPathAndFileID(firstPath, fileID) == ClassID(PreloadData))
+ fileID = 2;
+
+ // Try loading the asset bundle from disk.
+ if (pm.GetClassIDFromPathAndFileID(firstPath, fileID) == ClassID(AssetBundle))
+ {
+ int instanceID = pm.GetInstanceIDFromPathAndFileID(firstPath, fileID);
+ m_LoadingAssetBundle = dynamic_pptr_cast<AssetBundle*> (InstanceIDToObjectThreadSafe(instanceID));
+ }
+
+ // Otherwise create one
+ // This is for the old scene assetBundle which doesn't contain an AssetBundle object, just for backward compatibility.
+ if (m_LoadingAssetBundle == NULL)
+ {
+ m_LoadingAssetBundle = NEW_OBJECT_FULL (AssetBundle, kCreateObjectFromNonMainThread);
+ m_LoadingAssetBundle->Reset();
+ m_LoadingAssetBundle->AwakeFromLoadThreaded();
+ }
+
+ // Create cached web stream
+ SET_ALLOC_OWNER(m_LoadingAssetBundle);
+ CachedUnityWebStream* cachedStream = UNITY_NEW(CachedUnityWebStream, kMemFile);
+ cachedStream->m_Version = m_Version;
+ cachedStream->m_Files = fileNames;
+
+ m_LoadingAssetBundle->m_CachedUnityWebStream = cachedStream;
+}
+
+void AsyncCachedUnityWebStream::IntegrateMainThread ()
+{
+ GetPersistentManager().IntegrateAllThreadedObjects();
+
+ if (m_LoadingAssetBundle != NULL)
+ {
+ if (!m_LoadingAssetBundle->IsInstanceIDCreated())
+ {
+ Object::AllocateAndAssignInstanceID(m_LoadingAssetBundle);
+ m_LoadingAssetBundle->AwakeFromLoad(kDidLoadThreaded);
+ }
+
+ m_InstanceID = m_LoadingAssetBundle->GetInstanceID();
+ m_LoadingAssetBundle = NULL;
+ }
+
+ UnityMemoryBarrier();
+ m_Complete = true;
+}
+
+void AsyncCachedUnityWebStream::Perform ()
+{
+ LoadCachedUnityWebStream();
+ m_Progress = 1.0F;
+}
+
+AsyncCachedUnityWebStream::~AsyncCachedUnityWebStream ()
+{
+ AssertIf(!m_Complete);
+}
+
+AsyncCachedUnityWebStream::AsyncCachedUnityWebStream ()
+{
+ m_InstanceID = 0;
+ m_Version = 0;
+ m_LoadingAssetBundle = NULL;
+ m_AssetBundleWithSameNameIsAlreadyLoaded = false;
+}
+
+string Cache::GetFolder (const string& path, bool create)
+{
+ return GetCachingManagerPath(AppendPathName(m_Name, path), create);
+}
+
+bool Cache::CleanCache()
+{
+ if (!m_Ready)
+ {
+ // If we are still scanning the cache folder size, we might have open
+ // file handles, preventing us from deleting the cache folder. As we are deleting the folder,
+ // we don't care about it's size any longer anyways.
+ m_IndexThread.SignalQuit();
+ m_IndexThread.WaitForExit();
+ }
+
+ // Cache clean operation needs to make sure things like cache
+ // authorization are over:
+ Mutex::AutoLock lock (m_Mutex);
+ bool cleaned = GetCachingManager().CleanCache(m_Name);
+
+ if ( cleaned )
+ {
+ m_BytesUsed = 0;
+ m_CachedFiles.clear();
+ }
+
+ return cleaned;
+}
+
+void Cache::SetExpirationDelay (int expiration)
+{
+ m_CacheExpirationDelay = expiration;
+ if (m_CacheExpirationDelay > CachingManager::kMaxCacheExpiration)
+ {
+ ErrorString(Format("Cache expiration may not be higher then %d", CachingManager::kMaxCacheExpiration));
+ m_CacheExpirationDelay = CachingManager::kMaxCacheExpiration;
+ }
+ WriteCacheInfoFile (true);
+}
+
+void Cache::AddToCache (const string &path, int bytes)
+{
+ time_t lastUsedTime = 0;
+ CachingManager::ReadInfoFile (path, &lastUsedTime, NULL);
+
+ if (lastUsedTime > 0 && lastUsedTime < time(NULL) - m_CacheExpirationDelay)
+ {
+ // the file has expired. Don't add it, kill it.
+ DeleteFileOrDirectory (path);
+ return;
+ }
+
+ Mutex::AutoLock lock (m_Mutex);
+ m_BytesUsed += bytes;
+
+ string name = GetLastPathNameComponent (path);
+ size_t seperator = name.find_last_of ('@');
+ int version = 0;
+ if (seperator != string::npos)
+ {
+ version = StringToInt(name.substr(seperator + 1));
+ name = name.substr (0, seperator);
+ }
+
+ m_CachedFiles.insert(CachedFile (path, bytes, version, lastUsedTime));
+}
+
+void Cache::UpdateTimestamp (const string &path, time_t lastAccessed)
+{
+ Mutex::AutoLock lock (m_Mutex);
+
+ for (CachedFiles::iterator i=m_CachedFiles.begin();i!=m_CachedFiles.end();i++)
+ {
+ if (i->path == path)
+ {
+ CachedFile replacement = *i;
+ replacement.lastAccessed = lastAccessed;
+ m_CachedFiles.erase(i);
+ m_CachedFiles.insert(replacement);
+ return;
+ }
+ }
+}
+
+Cache::Cache()
+{
+ m_BytesUsed = 0;
+ m_Expires = 0x7fffffff;
+ m_Ready = false;
+ m_IncludePlayerURL = true;
+ m_CacheExpirationDelay = CachingManager::kMaxCacheExpiration;
+}
+
+Cache::~Cache()
+{
+ m_Ready = true;
+ m_IndexThread.WaitForExit();
+}
+
+void *Cache::ReadCacheIndexThreaded (void *data)
+{
+ Cache *cache = (Cache*)data;
+
+ string cachePath = cache->GetFolder ("", false);
+
+ set<string> paths;
+ if (GetFolderContentsAtPath (cachePath, paths))
+ {
+ for (set<string>::iterator i = paths.begin(); i != paths.end() && !cache->m_Ready; i++)
+ {
+ if (IsDirectoryCreated(*i))
+ cache->AddToCache (*i, GetFolderSizeRecursive(*i));
+ if (cache->m_IndexThread.IsQuitSignaled())
+ break;
+ }
+ }
+
+ // Write cache index to update last used time.
+ // Set a lock on it while we write things.
+ Mutex::AutoLock lock (cache->m_Mutex);
+ cache->WriteCacheInfoFile (false);
+ cache->m_Ready = true;
+ return NULL;
+}
+
+bool Cache::FreeSpace (size_t bytes)
+{
+ Mutex::AutoLock lock (m_Mutex);
+ CachedFiles::iterator i = m_CachedFiles.begin();
+ while (m_BytesAvailable - m_BytesUsed < bytes && i != m_CachedFiles.end())
+ {
+ CachedFiles::iterator cur = i;
+ i++;
+
+ const std::string &path = cur->path;
+ if (!IsDirectoryCreated (path) || (!IsFileOrDirectoryInUse (path) && DeleteFileOrDirectory (path)))
+ {
+ m_BytesUsed -= cur->size;
+ m_CachedFiles.erase (cur);
+ }
+ };
+
+ return m_BytesAvailable - m_BytesUsed >= bytes;
+}
+
+#define kCacheInfoVersion 1
+void Cache::WriteCacheInfoFile (bool updateExpiration)
+{
+ string folderPath = GetFolder ("", false);
+ if (!IsDirectoryCreated(folderPath))
+ return;
+
+ if (updateExpiration)
+ m_Expires = time(NULL) + m_CacheExpirationDelay;
+
+ time_t oldestLastUsedTime = 0;
+ if (m_CachedFiles.begin() != m_CachedFiles.end())
+ oldestLastUsedTime = m_CachedFiles.begin()->lastAccessed;
+
+ string indexFile = Format("%llu\n%d\n%llu\n", (UInt64)m_Expires, kCacheInfoVersion, (UInt64)oldestLastUsedTime);
+ string path = AppendPathName( folderPath, kInfoFileName);
+
+ File f;
+ if (!f.Open(path, File::kWritePermission, File::kSilentReturnOnOpenFail|File::kRetryOnOpenFail))
+ {
+ return;
+ }
+
+ SetFileFlags(path, kFileFlagDontIndex, kFileFlagDontIndex);
+
+ if (!f.Write(&indexFile[0], indexFile.size()))
+ {
+ f.Close();
+ return;
+ }
+ f.Close();
+}
+
+#define NEXT_HEADER_FIELD(iterator,file,expectEOF) { (iterator)++; if ((iterator) == (file).end()) return (expectEOF); }
+bool Cache::ReadCacheInfoFile (const string& path, time_t *expires, time_t *oldestLastUsedTime)
+{
+ InputString headerData;
+ if (!ReadStringFromFile(&headerData, AppendPathName(path, kInfoFileName)))
+ return false;
+
+ vector<string> seperatedHeader = FindSeparatedPathComponents(headerData.c_str (), headerData.size (), '\n');
+ vector<string>::iterator read = seperatedHeader.begin();
+
+ if (read == seperatedHeader.end())
+ return false;
+
+ //expires
+ if (expires)
+ *expires = StringToInt(*read);
+
+ NEXT_HEADER_FIELD (read, seperatedHeader, true);
+
+ //version
+ int version = StringToInt(*read);
+ if (version < 1)
+ return false;
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ //lastUsedTime
+ if (oldestLastUsedTime)
+ *oldestLastUsedTime = StringToInt(*read);
+ NEXT_HEADER_FIELD (read, seperatedHeader, true);
+ return true;
+}
+
+bool Cache::ReadCacheIndex (const string& name, bool getSize)
+{
+ m_Name = name;
+
+ string cachePath = GetFolder ( "", false);
+ time_t curTime = time(NULL);
+ time_t oldestLastUsedTime = curTime;
+ m_Expires = curTime + m_CacheExpirationDelay;
+
+ ReadCacheInfoFile (cachePath, &m_Expires, &oldestLastUsedTime);
+
+ if (getSize)
+ {
+ m_Ready = false;
+ m_Mutex.Lock();
+ m_BytesUsed = 0;
+ m_CachedFiles.clear();
+ m_Mutex.Unlock();
+ m_IndexThread.Run (ReadCacheIndexThreaded, (void*)this);
+ }
+ else
+ m_Ready = true;
+
+ return true;
+}
+
+
+void Cache::SetMaximumDiskSpaceAvailable (long long maxUsage)
+{
+ if (maxUsage > m_MaxLicenseBytesAvailable)
+ {
+ ErrorString("Maximum disk space used exceeds what is allowed by the license");
+ return;
+ }
+
+ m_BytesAvailable = maxUsage;
+}
+
+
+string CachingManager::GetTempFolder ()
+{
+ UnityGUID guid;
+ guid.Init();
+ string guidStr = GUIDToString(guid);
+ string temp = "Temp";
+ return GetCachingManagerPath (AppendPathName(temp, guidStr), true);
+}
+
+bool CachingManager::CleanCache(string name)
+{
+ string path = GetCachingManagerPath(name, false);
+
+ if ( IsFileOrDirectoryInUse( path ) )
+ {
+ return false;
+ }
+
+ GetGlobalCachingManager().RebuildAllCaches ();
+
+ return DeleteFileOrDirectory(path);
+}
+
+#define kInfoVersion 1
+
+int CachingManager::WriteInfoFile (const string &path, const vector<string> &fileNames)
+{
+ return WriteInfoFile (path, fileNames, GenerateTimeStamp ());
+}
+
+int CachingManager::WriteInfoFile (const string &path, const vector<string> &fileNames, time_t lastAccessed)
+{
+ string info;
+ // write negative info version (positive numbers had been used by previous, unversioned cache functions)
+ info += IntToString(-kInfoVersion) + "\n";
+
+ info += IntToString(lastAccessed) + "\n";
+
+ info += IntToString(fileNames.size()) + "\n";
+
+ for (vector<string>::const_iterator i=fileNames.begin();i != fileNames.end();i++)
+ info += *i + "\n";
+
+ File headerFile;
+ string filename = AppendPathName(path, kInfoFileName);
+ if (!headerFile.Open(filename, File::kWritePermission, File::kSilentReturnOnOpenFail|File::kRetryOnOpenFail))
+ return 0;
+
+ SetFileFlags(filename, kFileFlagDontIndex, kFileFlagDontIndex);
+
+ if (!headerFile.Write(&info[0], info.size()))
+ {
+ headerFile.Close();
+ return 0;
+ }
+
+ headerFile.Close();
+ return info.size();
+}
+
+bool CachingManager::ReadInfoFile (const string &path, time_t *lastUsedTime, vector<string> *fileNames)
+{
+ InputString headerData;
+ if (!ReadStringFromFile(&headerData, AppendPathName(path, kInfoFileName)))
+ return false;
+
+ vector<string> seperatedHeader = FindSeparatedPathComponents(headerData.c_str (), headerData.size (), '\n');
+ vector<string>::iterator read = seperatedHeader.begin();
+
+ if (read == seperatedHeader.end())
+ return false;
+
+ //version
+ int version = StringToInt(*read);
+ if (version >= 0)
+ return false;
+
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ //lastUsedTime
+ if (lastUsedTime)
+ *lastUsedTime = StringToInt(*read);
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ if (fileNames != NULL)
+ {
+ //numFiles
+ int numFiles = StringToInt(*read);
+ fileNames->resize (numFiles);
+ NEXT_HEADER_FIELD (read, seperatedHeader, false);
+
+ //files
+ for (int i=0;i<numFiles;i++)
+ {
+ (*fileNames)[i] = *read;
+ NEXT_HEADER_FIELD (read, seperatedHeader, i == numFiles - 1);
+ }
+ }
+ return true;
+}
+
+AsyncCachedUnityWebStream* CachingManager::LoadCached (const std::string& url, int version, UInt32 crc)
+{
+ AsyncCachedUnityWebStream* stream = new AsyncCachedUnityWebStream ();
+ stream->m_Url = url;
+ stream->m_Version = version;
+ stream->m_RequestedCRC = crc;
+
+ GetPreloadManager().AddToQueue(stream);
+
+ return stream;
+}
+
+bool CachingManager::IsCached (const string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+ if (path.empty())
+ return false;
+
+ vector<string> fileNames;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileNames))
+ return false;
+
+ return true;
+}
+
+bool CachingManager::MarkAsUsed (const string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+ if (path.empty())
+ return false;
+
+ vector<string> fileNames;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileNames))
+ return false;
+
+ // update timestamp.
+ time_t timestamp = GenerateTimeStamp ();
+ CachingManager::WriteInfoFile (path, fileNames, timestamp);
+ GetCachingManager().GetCurrentCache().UpdateTimestamp(path, timestamp);
+
+ return true;
+}
+
+#if UNITY_IPHONE
+// TODO: copy paste
+void CachingManager::SetNoBackupFlag(const std::string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+
+ if (path.empty())
+ return;
+
+ vector<string> fileName;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileName))
+ return;
+
+ for (vector<string>::iterator i = fileName.begin(); i != fileName.end(); ++i)
+ iphone::SetNoBackupFlag(i->c_str());
+}
+
+void CachingManager::ResetNoBackupFlag(const std::string &url, int version)
+{
+ string subpath = GetCachingManager().GetCurrentCache().URLToPath (url, version);
+ string path = GetCachingManager().GetCurrentCache().GetFolder(subpath, false);
+
+ if (path.empty())
+ return;
+
+ vector<string> fileName;
+ if (!CachingManager::ReadInfoFile (path, NULL, &fileName))
+ return;
+
+ for (vector<string>::iterator i = fileName.begin(); i != fileName.end(); ++i)
+ iphone::ResetNoBackupFlag(i->c_str());
+}
+#endif
+
+
+void CachingManager::ClearTempFolder ()
+{
+ string path = GetCachingManagerPath("Temp", false);
+ if (!IsDirectoryCreated (path))
+ return;
+
+ set<string> tempFolders;
+ if (!GetFolderContentsAtPath (path, tempFolders))
+ return;
+
+ for (set<string>::iterator i = tempFolders.begin(); i != tempFolders.end(); i++)
+ {
+ string lockPath = AppendPathName( *i, "__lock" );
+ if ( !IsFileCreated( lockPath ) || !IsFileOrDirectoryInUse( *i ) )
+ {
+ // No lock file exists or the directory was not in use. Delete temp folder.
+ DeleteFileOrDirectory(*i);
+ continue;
+ }
+ }
+}
+
+void CachingManager::Reset ()
+{
+ Dispose();
+
+ m_Authorization = kAuthorizationNone;
+
+ string defaultCacheName = GetDefaultApplicationIdentifierForCache(true);
+ if (!IsDirectoryCreated (defaultCacheName))
+ defaultCacheName = GetDefaultApplicationIdentifierForCache(false);
+
+ #if WEBPLUG
+ // Webplugin has a 50mb shared cache and includes the hashed unity3d file as the name
+ SetCurrentCache (defaultCacheName, 50 * 1024 * 1024, true);
+ #else
+ // Other platforms has a 4GB shared cache
+ m_Authorization = kAuthorizationUser;
+ SetCurrentCache (defaultCacheName, std::numeric_limits<long long>::max(), false);
+ #endif
+
+ // - On webplayer and PC we are resonposible for cleaning up the cache automatically
+ // - On mobile platforms the OS is responsible for cleaning up left over cache data.
+ if (UNITY_EMULATE_PERSISTENT_DATAPATH || WEBPLUG)
+ GetGlobalCachingManager().ClearAllExpiredCaches();
+
+ ClearTempFolder ();
+#if UNITY_PEPPER
+ m_Enabled = false;
+#elif WEBPLUG
+ m_Enabled = GetGlobalBoolPreference(kCachingEnabledKey, true);
+#else
+ m_Enabled = true;
+#endif
+}
+
+CachingManager::CachingManager ()
+{
+ m_Cache = NULL;
+ Reset ();
+}
+
+void CachingManager::Dispose ()
+{
+ GetGlobalCachingManager().Dispose();
+ if (m_Cache != NULL)
+ {
+ delete m_Cache;
+ m_Cache = NULL;
+ }
+}
+
+Cache& CachingManager::GetCurrentCache()
+{
+ return *m_Cache;
+}
+
+CachingManager::~CachingManager ()
+{
+ Dispose ();
+}
+
+bool CachingManager::VerifyValidDomain (const std::string &domain)
+{
+#if WEBPLUG && !UNITY_PEPPER
+ string currentDomain = GetPlayerSettings().absoluteURL;
+ if (currentDomain.find("http://") == 0 || currentDomain.find("https://") == 0)
+ {
+ currentDomain = GetStrippedPlayerDomain();
+
+ //remove http://
+ string domainStripped = domain;
+ domainStripped.erase(0, 7);
+
+ //remove path
+ std::string::size_type pos = domainStripped.find("/", 0);
+ if (pos != std::string::npos)
+ domainStripped.erase(domainStripped.begin() + pos, domainStripped.end());
+
+ if (currentDomain == "localhost")
+ return true;
+
+ //remove subdomain
+ string currentDomainStripped = currentDomain;
+ currentDomainStripped.erase(0, currentDomainStripped.length()-domainStripped.length());
+
+ if (domainStripped == currentDomainStripped)
+ return true;
+
+ ErrorString ("You need to run this player from the "+domain+" domain to be authorized to use the caching API. (currently running from "+currentDomain+" )");
+ return false;
+ }
+ // file:// domain is always valid for testing purposes
+ else if (currentDomain.find("file://") == 0)
+ return true;
+ // absoluteUrl is not a valid url???
+ else
+ {
+ ErrorString ("You need to run this player from the "+domain+" domain to be authorized to use the caching API.");
+ return false;
+ }
+#else
+ return true;
+#endif
+}
+
+void CachingManager::SetCurrentCache (const string &name, long long size, bool includePlayerURL)
+{
+ if (m_Cache != NULL)
+ delete m_Cache;
+ m_Cache = new Cache();
+
+ if (IsDirectoryCreated (GetCachingManagerPath(name, false)))
+ {
+ //update expiration time
+ m_Cache->WriteCacheInfoFile (true);
+ }
+
+ m_Cache->m_BytesAvailable = size;
+ m_Cache->m_MaxLicenseBytesAvailable = size;
+
+ m_Cache->m_IncludePlayerURL = includePlayerURL;
+ m_Cache->ReadCacheIndex (name, true);
+}
+
+bool CachingManager::Authorize (const string &name, const string &domain, long long size, int expiration, const string &signature)
+{
+ if (!VerifyValidDomain(domain))
+ return false;
+
+ m_Authorization = kAuthorizationNone;
+
+ if (::VerifySignature ("Cache="+name+";Domain="+domain+";Admin", signature))
+ {
+ m_Authorization = kAuthorizationAdmin;
+ }
+ else
+ {
+ string authoriziationString = "Cache="+name+";Domain="+domain+Format(";Size=%lld",size);
+ if (::VerifySignature (authoriziationString, signature))
+ m_Authorization = kAuthorizationUser;
+ else if (::VerifySignature (authoriziationString+Format(";Expiration=%d",expiration), signature))
+ m_Authorization = kAuthorizationUser;
+ else
+ {
+ WarningString ("Authorization to use the caching API failed.\n" + authoriziationString);
+ return false;
+ }
+ }
+
+ Assert(m_Authorization != kAuthorizationNone);
+
+ if ( expiration > 0 )
+ {
+ // expiration in seconds from Jan 1, 1970 UTC (use `date -j mmddYYYY +%s` or `date -j -v +30d +%s` to generate)
+ if ( time(NULL) > expiration)
+ {
+ m_Authorization = kAuthorizationNone;
+ WarningString(Format("Authorization for this cache has expired. Please renew your caching authorization. It will still work in the editor, but will fail in any players you build. (%d)", expiration));
+ return false;
+ }
+ }
+
+ SetCurrentCache (name, size, false);
+
+ return true;
+}
+
+CachingManager::AuthorizationLevel CachingManager::GetAuthorizationLevel ()
+{
+ if (!m_Enabled)
+ return (m_Authorization == kAuthorizationAdmin) ? kAuthorizationAdmin : kAuthorizationNone;
+
+ return m_Authorization;
+}
+
+void CachingManager::SetEnabled (bool enabled)
+{
+ m_Enabled = enabled;
+#if WEBPLUG && !UNITY_PEPPER
+ SetGlobalBoolPreference(kCachingEnabledKey, m_Enabled);
+#endif
+}
+
+long long CachingManager::GetCachingDiskSpaceUsed ()
+{
+ return GetCurrentCache().GetCachingDiskSpaceUsed();
+}
+
+long long CachingManager::GetCachingDiskSpaceFree ()
+{
+ return GetCurrentCache().GetCachingDiskSpaceFree();
+}
+
+void CachingManager::SetMaximumDiskSpaceAvailable (long long maximumAvailable)
+{
+ return GetCurrentCache().SetMaximumDiskSpaceAvailable(maximumAvailable);
+}
+
+long long CachingManager::GetMaximumDiskSpaceAvailable ()
+{
+ return GetCurrentCache().GetMaximumDiskSpaceAvailable();
+}
+
+int CachingManager::GetExpirationDelay ()
+{
+ return GetCurrentCache().GetExpirationDelay();
+}
+
+bool CachingManager::GetIsReady ()
+{
+ return GetCurrentCache().GetIsReady();
+}
+
+CachingManager* gCachingManager = NULL;
+CachingManager& GetCachingManager()
+{
+ if (gCachingManager == NULL)
+ gCachingManager = new CachingManager();
+
+ return *gCachingManager;
+}
+
+
+GlobalCachingManager* gGlobalCachingManager = NULL;
+GlobalCachingManager &GetGlobalCachingManager()
+{
+ if (gGlobalCachingManager == NULL)
+ gGlobalCachingManager = new GlobalCachingManager();
+
+ return *gGlobalCachingManager;
+}
+
+
+void GlobalCachingManager::RebuildAllCaches ()
+{
+ // delete the cached vector of caches. Will rebuild on next call to get_index
+ if (m_CacheIndices != NULL)
+ {
+ for (vector<Cache*>::iterator i = m_CacheIndices->begin(); i!=m_CacheIndices->end(); i++)
+ delete *i;
+ delete m_CacheIndices;
+ m_CacheIndices = NULL;
+ }
+}
+
+void GlobalCachingManager::Dispose ()
+{
+ if (m_CacheIndices != NULL)
+ {
+ for (vector<Cache*>::iterator i = m_CacheIndices->begin(); i!=m_CacheIndices->end(); i++)
+ delete *i;
+ delete m_CacheIndices;
+ m_CacheIndices = NULL;
+ }
+}
+
+std::vector<Cache*>& GlobalCachingManager::GetCacheIndices ()
+{
+ if (m_CacheIndices == NULL)
+ {
+ m_CacheIndices = new std::vector<Cache*>();
+ ReadCacheIndices (*m_CacheIndices, true);
+ }
+ return *m_CacheIndices;
+}
+
+void GlobalCachingManager::ReadCacheIndices (vector<Cache*> &indices, bool getSize)
+{
+ indices.clear();
+ string path = GetCachingManagerPath("", false);
+ if (!IsDirectoryCreated (path))
+ return;
+
+ set<string> caches;
+ if (!GetFolderContentsAtPath (path, caches))
+ return;
+
+ for (set<string>::iterator i = caches.begin(); i != caches.end(); i++)
+ {
+ if (!IsDirectoryCreated (*i))
+ continue;
+
+ string name = GetLastPathNameComponent(*i);
+ if (name == "Temp")
+ continue;
+
+ Cache *cache = new Cache();
+ bool success = cache->ReadCacheIndex (name, getSize);
+
+ // Remove expired caches
+ if (cache->m_Expires < time(NULL) || !success)
+ {
+ DeleteFileOrDirectory(*i);
+ delete cache;
+ }
+ else
+ indices.push_back(cache);
+ }
+}
+
+void GlobalCachingManager::ClearAllExpiredCaches ()
+{
+ vector<Cache*> indices;
+ ReadCacheIndices (indices, false);
+ for (vector<Cache*>::iterator i = indices.begin(); i!=indices.end(); i++)
+ delete *i;
+}
+
+#endif
diff --git a/Runtime/Misc/CachingManager.h b/Runtime/Misc/CachingManager.h
new file mode 100644
index 0000000..4d78221
--- /dev/null
+++ b/Runtime/Misc/CachingManager.h
@@ -0,0 +1,215 @@
+#ifndef CACHINGMANAGER_H
+#define CACHINGMANAGER_H
+
+#include "Runtime/Misc/AssetBundle.h"
+
+class Cache;
+
+#if ENABLE_CACHING
+
+#include "PreloadManager.h"
+
+class CachedUnityWebStream
+{
+ public:
+ SInt32 m_Version;
+ std::vector<std::string> m_Files;
+};
+
+class AsyncCachedUnityWebStream : public PreloadManagerOperation
+{
+ int m_InstanceID;
+ AssetBundle* m_LoadingAssetBundle;
+ std::string m_Url;
+ std::string m_Error;
+ int m_Version;
+ UInt32 m_RequestedCRC;
+ bool m_AssetBundleWithSameNameIsAlreadyLoaded;
+
+ friend class CachingManager;
+
+ void LoadCachedUnityWebStream();
+public:
+
+ AsyncCachedUnityWebStream ();
+ ~AsyncCachedUnityWebStream ();
+
+ AssetBundle* GetAssetBundle () { return PPtr<AssetBundle> (m_InstanceID); }
+ bool DidSucceed() { return IsDone() && m_InstanceID != 0;}
+ bool AssetBundleWithSameNameIsAlreadyLoaded() { return m_AssetBundleWithSameNameIsAlreadyLoaded; }
+ virtual void Perform ();
+
+ virtual void IntegrateMainThread ();
+ virtual bool HasIntegrateMainThread () { return true; }
+ std::string &GetError() { return m_Error; }
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () { return "AsyncCachedUnityWebStream"; }
+#endif
+};
+
+class Cache : public NonCopyable
+{
+public:
+ bool m_IncludePlayerURL;
+ string m_Name;
+ time_t m_Expires;
+ long long m_BytesUsed;
+ long long m_BytesAvailable; // bytes allowed
+ long long m_MaxLicenseBytesAvailable; // Bytes allowed by the license
+
+ int GetExpirationDelay () { return m_CacheExpirationDelay; }
+ void SetExpirationDelay (int expiration);
+
+ void WriteCacheInfoFile (bool updateExpiration);
+ string GetFolder (const string& path, bool create);
+
+ void AddToCache (const string &path, int bytes);
+ void UpdateTimestamp (const std::string &path, time_t lastAccessed);
+
+ bool CleanCache ();
+
+ bool FreeSpace (size_t bytes);
+
+ void SetMaximumDiskSpaceAvailable (long long maxUsage);
+ long long GetMaximumDiskSpaceAvailable () { return m_BytesAvailable; }
+
+ long long GetCachingDiskSpaceUsed () { return m_BytesUsed; }
+ long long GetCachingDiskSpaceFree () { return m_BytesAvailable - m_BytesUsed; }
+ bool GetIsReady () { return m_Ready; }
+
+ bool ReadCacheIndex (const string& name, bool getSize);
+ string URLToPath (const string& url, int version);
+
+ Cache();
+ ~Cache();
+
+private:
+ struct CachedFile
+ {
+ string path;
+ size_t size;
+ int version;
+ time_t lastAccessed;
+
+ CachedFile()
+ {
+ size = version = lastAccessed = 0;
+ }
+ CachedFile(string _path, size_t _size, int _version, time_t _lastAccessed)
+ {
+ path = _path;
+ size = _size;
+ version = _version;
+ lastAccessed = _lastAccessed;
+ }
+ bool operator < (const CachedFile& other) const
+ {
+ return lastAccessed < other.lastAccessed;
+ }
+ };
+
+ typedef std::multiset<CachedFile> CachedFiles;
+
+ int m_CacheExpirationDelay;
+ Thread m_IndexThread;
+ Mutex m_Mutex;
+ bool m_Ready;
+ CachedFiles m_CachedFiles;
+
+ bool ReadCacheInfoFile (const string& path, time_t *expires, time_t *oldestLastUsedTime);
+ static void *ReadCacheIndexThreaded (void *data);
+};
+
+class CachingManager
+{
+public:
+ enum AuthorizationLevel
+ {
+ kAuthorizationNone = 0,
+ kAuthorizationUser,
+ kAuthorizationAdmin
+ };
+ enum
+ {
+ kMaxCacheExpiration = (150 * 86400) //150 days
+ };
+
+ CachingManager ();
+ ~CachingManager ();
+ void Reset ();
+
+ std::string GetTempFolder ();
+ static bool MoveTempFolder (const string& from, const string& to);
+ static time_t GenerateTimeStamp ();
+ static int WriteInfoFile (const std::string &path, const std::vector<std::string> &fileNames);
+ static int WriteInfoFile (const std::string &path, const std::vector<std::string> &fileNames, time_t lastAccessed);
+ static bool ReadInfoFile (const std::string &path, time_t *lastUsedTime, std::vector<std::string> *fileNames);
+
+ std::vector<Cache*> &GetCacheIndices ();
+
+ bool CleanCache (std::string name);
+
+ bool Authorize (const string &name, const string &domain, long long size, int expiration, const string &signature);
+ AuthorizationLevel GetAuthorizationLevel ();
+
+ AsyncCachedUnityWebStream* LoadCached (const std::string& url, int version, UInt32 crc);
+ bool IsCached (const std::string &url, int version);
+ bool MarkAsUsed (const std::string& url, int version);
+
+ bool GetEnabled () { return m_Enabled; }
+ void SetEnabled (bool enabled);
+
+
+ Cache& GetCurrentCache();
+
+ long long GetCachingDiskSpaceUsed ();
+ long long GetCachingDiskSpaceFree ();
+
+ void SetMaximumDiskSpaceAvailable (long long maximumAvailable);
+ long long GetMaximumDiskSpaceAvailable ();
+
+ int GetExpirationDelay ();
+ bool GetIsReady ();
+
+#if UNITY_IPHONE
+ void SetNoBackupFlag(const std::string &url, int version);
+ void ResetNoBackupFlag(const std::string &url, int version);
+#endif
+
+private:
+ AuthorizationLevel m_Authorization;
+ Cache* m_Cache;
+ bool m_Enabled;
+
+ void Dispose ();
+ void ClearTempFolder();
+ bool VerifyValidDomain (const std::string &domain);
+ void SetCurrentCache (const string &name, long long size, bool includePlayerURL);
+ void ReadCacheIndices (std::vector<Cache*> &indices, bool getSize);
+};
+
+/// Handles the cache for multiple games
+class GlobalCachingManager
+{
+ std::vector<Cache*>* m_CacheIndices;
+ void ReadCacheIndices (std::vector<Cache*> &indices, bool getSize);
+
+public:
+ GlobalCachingManager ()
+ {
+ m_CacheIndices = NULL;
+ }
+
+ std::vector<Cache*>& GetCacheIndices ();
+ void ClearAllExpiredCaches ();
+ void RebuildAllCaches ();
+ void Dispose ();
+};
+
+
+CachingManager &GetCachingManager();
+GlobalCachingManager &GetGlobalCachingManager();
+
+#endif // ENABLE_CACHING
+#endif
diff --git a/Runtime/Misc/CaptureScreenshot.cpp b/Runtime/Misc/CaptureScreenshot.cpp
new file mode 100644
index 0000000..440f1ac
--- /dev/null
+++ b/Runtime/Misc/CaptureScreenshot.cpp
@@ -0,0 +1,605 @@
+#include "UnityPrefix.h"
+#include "CaptureScreenshot.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#if ENABLE_RETAINEDGUI
+#include "Runtime/ExGUI/GUITracker.h"
+#endif
+
+#if UNITY_WII
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "External/stb/stb_image_write.h"
+#include "PlatformDependent/Wii/WiiHio2.h"
+#endif
+
+#if UNITY_FLASH
+#include "PlatformDependent/FlashSupport/cpp/FlashUtils.h"
+#include "Runtime/GfxDevice/molehill/MolehillUtils.h"
+#endif
+
+#if UNITY_WP8
+#include "Runtime/Graphics/ScreenManager.h"
+#endif
+
+
+#if UNITY_PS3
+#include <cell/codec.h>
+
+ bool ConvertImageToPNGFilePS3 (const ImageReference& inputImage, const char* path)
+ {
+ int ret;
+ static bool hasCreatedEncoder = false;
+ static CellPngEncHandle encoderHandle;
+
+ const UInt32 w = inputImage.GetWidth();
+ const UInt32 h = inputImage.GetHeight();
+ const UInt32 p = inputImage.GetRowBytes();
+ const UInt32 bpp = p / w;
+
+ if (!hasCreatedEncoder)
+ {
+ CELLCALL(cellSysmoduleLoadModule(CELL_SYSMODULE_PNGENC));
+
+ CellPngEncConfig config;
+ CellPngEncAttr attr;
+ CellPngEncResource resource;
+
+ config.maxWidth = 1920;
+ config.maxHeight = 1080;
+ config.maxBitDepth = 8;
+ config.addMemSize = 0;
+ config.enableSpu = true;
+ config.exParamList = NULL;
+ config.exParamNum = 0;
+
+ CELLCALL(cellPngEncQueryAttr(&config, &attr));
+
+ resource.memAddr = malloc(attr.memSize);
+ resource.memSize = attr.memSize;
+ resource.ppuThreadPriority = 1000;
+ resource.spuThreadPriority = 200;
+
+ CELLCALL(cellPngEncOpen(&config, &resource, &encoderHandle));
+
+ hasCreatedEncoder = true;
+ }
+
+ CellPngEncPicture picture;
+ CellPngEncEncodeParam encodeParam;
+ CellPngEncOutputParam outputParam;
+ CellPngEncStreamInfo streamInfo;
+
+ picture.width = w;
+ picture.height = h;
+ picture.pitchWidth = bpp * w;
+ picture.colorSpace = bpp == 4 ? CELL_PNGENC_COLOR_SPACE_ARGB : CELL_PNGENC_COLOR_SPACE_RGB;
+ picture.bitDepth = 8;
+ picture.packedPixel = false;
+ picture.pictureAddr = inputImage.GetImageData();
+
+ encodeParam.enableSpu = true;
+ encodeParam.encodeColorSpace = CELL_PNGENC_COLOR_SPACE_RGB;
+ encodeParam.compressionLevel = CELL_PNGENC_COMPR_LEVEL_1;
+ encodeParam.filterType = CELL_PNGENC_FILTER_TYPE_SUB;
+ encodeParam.ancillaryChunkList = NULL;
+ encodeParam.ancillaryChunkNum = 0;
+
+ outputParam.location = CELL_PNGENC_LOCATION_BUFFER;
+ outputParam.streamFileName = NULL;
+ outputParam.limitSize = w * h * 4;
+ outputParam.streamAddr = malloc(outputParam.limitSize);
+
+ CELLCALL(cellPngEncEncodePicture(encoderHandle, &picture, &encodeParam, &outputParam));
+
+ // wait for encoding to finish
+
+ uint32_t streamInfoNum;
+
+ CELLCALL(cellPngEncWaitForOutput(encoderHandle, &streamInfoNum, true));
+ CELLCALL(cellPngEncGetStreamInfo(encoderHandle, &streamInfo));
+
+ string finalPath = path;
+ ConvertSeparatorsToPlatform(finalPath);
+
+ File file;
+ if (!file.Open(finalPath.c_str(), File::kWritePermission))
+ return false;
+
+ file.Write(streamInfo.streamAddr, streamInfo.streamSize);
+ file.Close();
+
+#if ENABLE_PLAYERCONNECTION
+ TransferFileOverPlayerConnection(finalPath, streamInfo.streamAddr, streamInfo.streamSize);
+#endif
+ free(outputParam.streamAddr);
+ return true;
+ }
+#endif
+
+
+#if UNITY_XENON
+bool ConvertImageToPNGFileXbox360(const ImageReference& inputImage, const char* path)
+{
+ const UInt32 w = inputImage.GetWidth();
+ const UInt32 h = inputImage.GetHeight();
+ const UInt32 p = inputImage.GetRowBytes();
+ const UInt32 s = h * p;
+ const UInt32 bpp = p / w;
+ if (bpp != 4)
+ {
+ UNITY_TRACE("Can only save 32bpp screenshots.\n");
+ return false;
+ }
+
+ DWORD size = XGSetTextureHeader(inputImage.GetWidth(), inputImage.GetHeight(), 1, D3DUSAGE_CPU_CACHED_MEMORY, D3DFMT_LIN_A8R8G8B8, 0, 0, 0, p, NULL, NULL, NULL);
+ if (size != s)
+ {
+ UNITY_TRACE("Invalid screenshot size.\n");
+ return false;
+ }
+
+ bool success = true;
+
+ IDirect3DTexture9* tex = new IDirect3DTexture9();
+ XGSetTextureHeader(inputImage.GetWidth(), inputImage.GetHeight(), 1, D3DUSAGE_CPU_CACHED_MEMORY, D3DFMT_LIN_A8R8G8B8, 0, 0, 0, p, tex, NULL, NULL);
+ XGOffsetResourceAddress(tex, inputImage.GetImageData());
+
+ string finalPath = path;
+ ConvertSeparatorsToPlatform(finalPath);
+
+#if ENABLE_PLAYERCONNECTION
+ ID3DXBuffer* buffer = 0;
+ HRESULT hr = D3DXSaveTextureToFileInMemory(&buffer, D3DXIFF_PNG, tex, NULL);
+ success &= SUCCEEDED(hr);
+
+ if (buffer)
+ {
+ File file;
+ if (file.Open(finalPath.c_str(), File::kWritePermission))
+ {
+ success &= file.Write(buffer->GetBufferPointer(), buffer->GetBufferSize());
+ file.Close();
+ }
+ else
+ success = false;
+
+ TransferFileOverPlayerConnection(finalPath, buffer->GetBufferPointer(), buffer->GetBufferSize());
+ buffer->Release();
+ }
+#else
+ HRESULT hr = D3DXSaveTextureToFile(finalPath.c_str(), D3DXIFF_PNG, tex, NULL);
+ success &= SUCCEEDED(hr);
+#endif
+
+ delete tex;
+
+ return success;
+}
+#endif
+
+#if UNITY_XENON || UNITY_PS3
+//#define ENABLE_MULTITHREADED 1
+ bool ConvertImageToTGAFile (const ImageReference& inputImage, const char* path)
+ {
+ bool success = false;
+ const UInt32 w = inputImage.GetWidth();
+ const UInt32 h = inputImage.GetHeight();
+ const UInt32 p = inputImage.GetRowBytes();
+ const UInt32 bpp = p / w;
+
+ UInt8 tgaHeader[18] = {
+ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ (w>>0)&0xff, (w>>8)&0xff,
+ (h>>0)&0xff, (h>>8)&0xff,
+ bpp<<3, (bpp==4)?8:0
+ };
+
+ string finalPath = path;
+ ConvertSeparatorsToPlatform(finalPath);
+
+ File file;
+ if (!file.Open(finalPath.c_str(), File::kWritePermission))
+ return false;
+
+ success |= file.Write(tgaHeader, 18);
+ success |= file.Write(inputImage.GetImageData(), h*p);
+
+ file.Close();
+
+#if ENABLE_PLAYERCONNECTION
+ TransferFileOverPlayerConnection(finalPath, inputImage.GetImageData(), h*p, tgaHeader, sizeof(tgaHeader));
+#endif
+
+ return success;
+ }
+#endif
+
+// don't even compile the code in web player
+#if CAPTURE_SCREENSHOT_AVAILABLE
+
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/ImageConversion.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Misc/SystemInfo.h"
+
+using namespace std;
+
+static const char* gCaptureScreenshotPath = NULL;
+static int s_CaptureSuperSize;
+
+#if CAPTURE_SCREENSHOT_THREAD
+Thread gCaptureScreenshotThread;
+#endif
+
+void QueueScreenshot (const string& path, int superSize)
+{
+#if UNITY_IPHONE || UNITY_ANDROID
+ gCaptureScreenshotPath = strdup (AppendPathName (systeminfo::GetPersistentDataPath(), path).c_str());
+#else
+ gCaptureScreenshotPath = strdup(PathToAbsolutePath(path).c_str());
+#endif
+ s_CaptureSuperSize = clamp (superSize, 0, 16);
+}
+
+void FinishAllCaptureScreenshot ()
+{
+#if CAPTURE_SCREENSHOT_THREAD
+ gCaptureScreenshotThread.WaitForExit();
+#endif
+}
+
+struct WriteImageAsync
+{
+ std::string path;
+ Image* image;
+};
+
+void* WriteImageAsyncThread (void* data)
+{
+ WriteImageAsync* async = static_cast<WriteImageAsync*> (data);
+
+#if UNITY_XENON
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGBA32 );
+ if (!ConvertImageToPNGFileXbox360 (*async->image, async->path.c_str()))
+ {
+ ErrorString( "Failed to store screen shot" );
+ }
+#elif UNITY_PS3
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGBA32 );
+ if (!ConvertImageToPNGFilePS3 (*async->image, async->path.c_str()))
+ {
+ ErrorString( "Failed to store screen shot" );
+ }
+#elif UNITY_WII
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGBA32 );
+
+ int len;
+ UInt8* pngData = stbi_write_png_to_mem(
+ async->image->GetImageData(),
+ async->image->GetRowBytes(),
+ async->image->GetWidth(),
+ async->image->GetHeight(),
+ async->image->GetRowBytes() / async->image->GetWidth(), &len);
+ bool success = pngData != NULL;
+ if (success)
+ {
+ #if ENABLE_HIO2
+ success = wii::hio2::SendFile(async->path.c_str(), pngData, len);
+ #else
+ success = false;
+ #endif
+ free(pngData);
+ }
+
+ if (!success)
+ {
+ ErrorString(Format("Failed to write screenshot to path '%s', (remember this path is relative to *.mcp file)", async->path.c_str()).c_str());
+ }
+#elif ENABLE_PNG_JPG
+ async->image->ReformatImage( async->image->GetWidth(), async->image->GetHeight(), kTexFormatRGB24 );
+ if (!ConvertImageToPNGFile (*async->image, async->path))
+ {
+ ErrorString( "Failed to store screen shot" );
+ }
+#else
+ ErrorString("Cannot create pngs");
+#endif
+ delete async->image;
+ delete async;
+
+ return NULL;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct SavedCameraData {
+ PPtr<Camera> camera;
+ Rectf viewportRect;
+};
+typedef dynamic_array<SavedCameraData> SavedCameras;
+
+struct SavedTextureData {
+ PPtr<Texture> texture;
+ float mipBias;
+};
+typedef dynamic_array<SavedTextureData> SavedTextures;
+
+
+static void SaveCameraParams (RenderManager::CameraContainer& cameras, SavedCameras& outCameras)
+{
+ outCameras.clear();
+ for (RenderManager::CameraContainer::iterator camit = cameras.begin(); camit != cameras.end(); ++camit)
+ {
+ Camera* cam = *camit;
+ if (!cam)
+ continue;
+ SavedCameraData data;
+ data.camera = cam;
+ data.viewportRect = cam->GetNormalizedViewportRect();
+ outCameras.push_back (data);
+ }
+}
+
+static void RestoreCameraParams (SavedCameras& cameras)
+{
+ for (SavedCameras::iterator camit = cameras.begin(); camit != cameras.end(); ++camit)
+ {
+ SavedCameraData& data = *camit;
+ Camera* cam = data.camera;
+ if (!cam)
+ continue;
+ cam->SetNormalizedViewportRect (data.viewportRect);
+ cam->ResetProjectionMatrix (); ///@TODO
+ }
+}
+
+
+static void SaveTextureParams (float mipBias, SavedTextures& outTextures, int& minAniso, int& maxAniso)
+{
+ vector<Texture*> objects;
+ Object::FindObjectsOfType (&objects);
+
+ outTextures.clear();
+ outTextures.reserve (objects.size());
+
+ TextureSettings::GetAnisoLimits (minAniso, maxAniso);
+ TextureSettings::SetAnisoLimits (16, 16);
+
+ for (size_t i=0;i<objects.size ();i++)
+ {
+ Texture* t = objects[i];
+ SavedTextureData data;
+ data.texture = t;
+ data.mipBias = t->GetSettings().m_MipBias;
+ outTextures.push_back (data);
+
+ if (t->HasMipMap())
+ t->GetSettings().m_MipBias += mipBias;
+ t->ApplySettings ();
+ }
+}
+
+static void RestoreTextureParams (SavedTextures& textures, int minAniso, int maxAniso)
+{
+ TextureSettings::SetAnisoLimits (minAniso, maxAniso);
+
+ for (SavedTextures::iterator it = textures.begin(); it != textures.end(); ++it)
+ {
+ SavedTextureData& data = *it;
+ Texture* t = data.texture;
+ if (!t)
+ continue;
+ t->GetSettings().m_MipBias = data.mipBias;
+ t->ApplySettings ();
+ }
+}
+
+
+static void ShiftedCameraProjections (SavedCameras& cameras, int superSize, int x, int y, int screenWidth, int screenHeight)
+{
+ for (SavedCameras::iterator camit = cameras.begin(); camit != cameras.end(); ++camit)
+ {
+ SavedCameraData& data = *camit;
+ Camera* cam = data.camera;
+ if (!cam)
+ continue;
+ cam->ResetProjectionMatrix();
+ Rectf camRect = cam->GetScreenViewportRect();
+ float dx = (float(x)/float(superSize)-0.5f) / (camRect.width*0.5f);
+ float dy = (float(y)/float(superSize)-0.5f) / (camRect.height*0.5f);
+ Matrix4x4f proj = cam->GetProjectionMatrix ();
+ if (cam->GetOrthographic())
+ {
+ proj.Get(0,3) -= dx;
+ proj.Get(1,3) -= dy;
+ }
+ else
+ {
+ proj.Get(0,2) += dx;
+ proj.Get(1,2) += dy;
+ }
+ cam->SetProjectionMatrix (proj);
+ }
+}
+
+static void InterleaveImage (const Image& screenImage, Image& finalImage, int xo, int yo, int superSize)
+{
+ Assert (screenImage.GetWidth() * superSize == finalImage.GetWidth());
+ Assert (screenImage.GetHeight() * superSize == finalImage.GetHeight());
+ Assert (xo >= 0 && xo < superSize);
+ Assert (yo >= 0 && yo < superSize);
+
+ const UInt32* srcData = reinterpret_cast<const UInt32*>(screenImage.GetImageData());
+ UInt32* dstData = reinterpret_cast<UInt32*>(finalImage.GetImageData());
+ const int width = screenImage.GetWidth();
+ const int height = screenImage.GetHeight();
+ dstData += (width*superSize) * yo + xo;
+ for (int y = 0; y < height; ++y)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ dstData[x*superSize] = srcData[x];
+ }
+ dstData += width*superSize*superSize;
+ srcData += width;
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+static Image* DoCaptureScreenshot (int superSize)
+{
+ GfxDevice& device = GetGfxDevice();
+ RenderManager& renderMgr = GetRenderManager();
+
+#if UNITY_WP8
+ /* In WP8, D3D11 seems to always be in portrait orientation, despite program orientation,
+ * so horizontal and vertical axis swap is needed */
+ Rectf rc = renderMgr.GetWindowRect();
+ if (GetScreenManager().GetScreenOrientation() == ScreenOrientation::kLandscapeLeft || GetScreenManager().GetScreenOrientation() == ScreenOrientation::kLandscapeRight)
+ {
+ std::swap(rc.x, rc.y);
+ std::swap(rc.width, rc.height);
+ }
+#else
+ const Rectf rc = renderMgr.GetWindowRect();
+#endif
+
+ const int screenWidth = int(rc.Width ());
+ const int screenHeight = int(rc.Height ());
+
+ Image* finalImage = NULL;
+
+ if (superSize > 1)
+ {
+ finalImage = new Image (screenWidth*superSize, screenHeight*superSize, kTexFormatRGBA32);
+ if (!finalImage)
+ return NULL;
+
+ Image screenImage (screenWidth, screenHeight, kTexFormatRGBA32);
+
+ RenderManager::CameraContainer& cameras = renderMgr.GetOnscreenCameras ();
+ SavedCameras cameraData;
+ SaveCameraParams (cameras, cameraData);
+
+ float mipBias = -Log2(superSize) - 1.0f;
+ SavedTextures textureData;
+ int savedMinAniso, savedMaxAniso;
+ SaveTextureParams (mipBias, textureData, savedMinAniso, savedMaxAniso);
+
+ for (int y = 0; y < superSize; ++y)
+ {
+ for (int x = 0; x < superSize; ++x)
+ {
+ ShiftedCameraProjections (cameraData, superSize, x, y, screenWidth, screenHeight);
+ renderMgr.RenderCameras ();
+ #if ENABLE_UNITYGUI
+ GetGUIManager().Repaint ();
+ #endif
+ device.CaptureScreenshot (int(rc.x), int(rc.y), screenWidth, screenHeight, screenImage.GetImageData());
+ InterleaveImage (screenImage, *finalImage, x, y, superSize);
+ }
+ }
+
+ RestoreTextureParams (textureData, savedMinAniso, savedMaxAniso);
+ RestoreCameraParams (cameraData);
+ }
+ else
+ {
+ finalImage = new Image (screenWidth, screenHeight, kTexFormatRGBA32);
+ if (!finalImage)
+ return NULL;
+ bool ok = device.CaptureScreenshot (int(rc.x), int(rc.y), screenWidth, screenHeight, finalImage->GetImageData());
+ if (!ok)
+ {
+ delete finalImage;
+ return NULL;
+ }
+ }
+ return finalImage;
+}
+
+bool IsScreenshotQueued()
+{
+ return (gCaptureScreenshotPath!= NULL);
+}
+
+void CaptureScreenshotImmediate(string filePath, int x, int y, int width, int height)
+{
+ Rectf rc = GetRenderManager().GetWindowRect();
+ Image* buffer = new Image (width, height, kTexFormatRGBA32);
+ bool ok = GetGfxDevice().CaptureScreenshot( rc.x + x, rc.y + y, width, height, buffer->GetImageData() );
+ if( ok )
+ {
+ WriteImageAsync* asyncData = new WriteImageAsync();
+ asyncData->path = filePath;
+ asyncData->image = buffer;
+ WriteImageAsyncThread(asyncData);
+ }
+ else
+ {
+ delete buffer;
+ ErrorString( "Failed to capture screen shot" );
+ }
+}
+
+void UpdateCaptureScreenshot ()
+{
+ if (!gCaptureScreenshotPath)
+ return;
+
+ #if !UNITY_FLASH
+ Image* buffer = DoCaptureScreenshot (s_CaptureSuperSize);
+ if (buffer)
+ {
+ WriteImageAsync* asyncData = new WriteImageAsync();
+ asyncData->path = gCaptureScreenshotPath;
+ asyncData->image = buffer;
+
+ #if CAPTURE_SCREENSHOT_THREAD
+ gCaptureScreenshotThread.WaitForExit();
+ gCaptureScreenshotThread.Run(WriteImageAsyncThread, asyncData);
+ #else
+ WriteImageAsyncThread(asyncData);
+ #endif
+ }
+ else
+ {
+ delete buffer;
+ ErrorString( "Failed to capture screen shot" );
+ }
+
+ #else
+ Rectf rc = GetRenderManager().GetWindowRect();
+ DBG_LOG_MOLEHILL("\nMH: capture screenshot\n\n");
+ GetGfxDevice().CaptureScreenshot (0,0,0,0,NULL); // arguments don't matter here, just needs to ensure clear is done
+ AS3Handle ba = Ext_Stage3D_CaptureScreenshot(rc.Width(),rc.Height());
+
+ #if ENABLE_PLAYERCONNECTION
+ int baLength = Ext_Flash_GetByteArrayLength(ba);
+ void* buffer = malloc(baLength);
+ Ext_Flash_ReadByteArray(ba,buffer,baLength);
+ TransferFileOverPlayerConnection (gCaptureScreenshotPath, buffer, baLength);
+ free (buffer);
+ #endif
+
+ //Let go of bytearray.
+ //Ext_MarshalMap_Release(ba);//TODO RH : this byterarray shouldn't be in the marshalmap anyway.
+ #endif
+
+ free((void*)gCaptureScreenshotPath);
+ gCaptureScreenshotPath = NULL;
+}
+
+#endif
diff --git a/Runtime/Misc/CaptureScreenshot.h b/Runtime/Misc/CaptureScreenshot.h
new file mode 100644
index 0000000..acf318c
--- /dev/null
+++ b/Runtime/Misc/CaptureScreenshot.h
@@ -0,0 +1,30 @@
+#ifndef _CAPTURESCREENSHOT_H
+#define _CAPTURESCREENSHOT_H
+
+#include "Configuration/UnityConfigure.h"
+
+#undef CAPTURE_SCREENSHOT_AVAILABLE
+
+// In web player we never capture screenshots. Don't even compile the code in (saves whole pnglib!)
+#if (WEBPLUG && !SUPPORT_REPRODUCE_LOG && !UNITY_PEPPER)
+#define CAPTURE_SCREENSHOT_AVAILABLE 0
+#else
+#define CAPTURE_SCREENSHOT_AVAILABLE 1
+// Player connection might be used to transfer screenshots and async is a pain there
+#define CAPTURE_SCREENSHOT_THREAD (SUPPORT_THREADS && !UNITY_WII && !ENABLE_PLAYERCONNECTION)
+#endif
+
+
+#if CAPTURE_SCREENSHOT_AVAILABLE
+
+#include <string>
+
+void QueueScreenshot (const std::string& path, int superSize);
+bool IsScreenshotQueued ();
+void UpdateCaptureScreenshot ();
+void FinishAllCaptureScreenshot ();
+
+#endif
+
+
+#endif
diff --git a/Runtime/Misc/ComponentRequirement.cpp b/Runtime/Misc/ComponentRequirement.cpp
new file mode 100644
index 0000000..70ac2f8
--- /dev/null
+++ b/Runtime/Misc/ComponentRequirement.cpp
@@ -0,0 +1,451 @@
+#include "UnityPrefix.h"
+#include "ComponentRequirement.h"
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include <algorithm>
+
+#if UNITY_EDITOR
+#include "Editor/Src/LicenseInfo.h"
+#endif
+
+#if !UNITY_WIN && !UNITY_XENON && !UNITY_PS3 && !UNITY_ANDROID
+#pragma optimization_level 0
+#endif
+
+typedef std::map<int, vector_set<int> > ClassIDToClassIDsMap;
+static ClassIDToClassIDsMap* gRequiredClasses;
+static ClassIDToClassIDsMap* gConflictingClasses;
+typedef std::set<int> ClassIDSet;
+static ClassIDSet* gAllowsMultipleInclusion;
+static ClassIDSet* gDoesComponentAllowReplacement;
+#if UNITY_EDITOR
+static ComponentsHierarchy* gComponentHierarchy;
+#endif
+
+namespace ComponentRequirements
+{
+ void StaticInitialize()
+ {
+ gRequiredClasses = UNITY_NEW(ClassIDToClassIDsMap,kMemResource);
+ gConflictingClasses = UNITY_NEW(ClassIDToClassIDsMap,kMemResource);
+ gAllowsMultipleInclusion = UNITY_NEW(ClassIDSet,kMemResource);
+ gDoesComponentAllowReplacement = UNITY_NEW(ClassIDSet,kMemResource);
+#if UNITY_EDITOR
+ gComponentHierarchy = UNITY_NEW(ComponentsHierarchy,kMemResource);
+#endif
+ }
+ void StaticDestroy()
+ {
+ UNITY_DELETE(gRequiredClasses,kMemResource);
+ UNITY_DELETE(gConflictingClasses,kMemResource);
+ UNITY_DELETE(gAllowsMultipleInclusion,kMemResource);
+ UNITY_DELETE(gDoesComponentAllowReplacement,kMemResource);
+#if UNITY_EDITOR
+ UNITY_DELETE(gComponentHierarchy,kMemResource);
+#endif
+ }
+}
+static RegisterRuntimeInitializeAndCleanup s_ComponentRequirementsCallbacks(ComponentRequirements::StaticInitialize, ComponentRequirements::StaticDestroy);
+
+const vector_set<int>& FindRequiredComponentsForComponent (int componentClassID)
+{
+ InitComponentRequirements ();
+ return (*gRequiredClasses)[componentClassID];
+}
+
+void FindAllRequiredComponentsRecursive (int componentClassID, vector_set<int>& results)
+{
+ // Already added
+ if (!results.insert(componentClassID).second)
+ return;
+
+ const vector_set<int>& thisClassResult = FindRequiredComponentsForComponent (componentClassID);
+ for (int i=0;i<thisClassResult.size();i++)
+ FindAllRequiredComponentsRecursive(thisClassResult[i], results);
+}
+
+const vector_set<int>& FindConflictingComponents (int classID)
+{
+ InitComponentRequirements ();
+ return (*gConflictingClasses)[classID];
+}
+
+
+int GetAllowComponentReplacementClass (int classID)
+{
+ while (classID != ClassID (Object))
+ {
+ if (gDoesComponentAllowReplacement->count (classID))
+ return classID;
+
+ classID = Object::GetSuperClassID (classID);
+ }
+ return -1;
+}
+
+bool DoesComponentAllowMultipleInclusion (int componentClassID)
+{
+ InitComponentRequirements ();
+ return gAllowsMultipleInclusion->find(componentClassID) != gAllowsMultipleInclusion->end();
+}
+#if UNITY_EDITOR
+const ComponentsHierarchy& GetComponentsHierarchy ()
+{
+ InitComponentRequirements ();
+ return (*gComponentHierarchy);
+}
+#endif
+
+static void AddRequiredClassIMPL (const string& aClass, const string& requiredClass)
+{
+ int classID = Object::StringToClassID (aClass);
+ int requiredClassID = Object::StringToClassID (requiredClass);
+#if !SUPPORTS_NATIVE_CODE_STRIPPING
+ Assert (classID != -1 && requiredClassID != -1);
+#endif
+
+ std::vector<SInt32> derivedClasses;
+ Object::FindAllDerivedClasses (classID, &derivedClasses, false);
+
+ for (std::vector<SInt32>::const_iterator i=derivedClasses.begin ();i!=derivedClasses.end ();++i)
+ (*gRequiredClasses)[*i].insert (requiredClassID);
+
+ (*gRequiredClasses)[classID].insert (requiredClassID);
+}
+
+static void AddConflictingClassIMPL (const string& aClass, const string& otherClass)
+{
+ int classID = Object::StringToClassID (aClass);
+ int otherClassID = Object::StringToClassID (otherClass);
+ if (classID == -1 || otherClassID == -1) // might have been stripped out
+ return;
+
+ std::vector<SInt32> derivedClasses;
+ Object::FindAllDerivedClasses (classID, &derivedClasses, false);
+
+ for (std::vector<SInt32>::const_iterator i=derivedClasses.begin ();i!=derivedClasses.end ();++i)
+ (*gConflictingClasses)[*i].insert (otherClassID);
+ (*gConflictingClasses)[classID].insert (otherClassID);
+}
+
+
+#define AddRequiredClass(c, r) AddRequiredClassIMPL(#c, #r)
+#define AddConflictingClass(c, r) AddConflictingClassIMPL(#c, #r)
+
+#define AddToComponentHierarchy(className) \
+{ \
+ int classID = Object::StringToClassID (#className); \
+ AssertIf (classID == 0 || classID == -1 || gComponentHierarchy->empty ()); \
+ gComponentHierarchy->back ().second.push_back (GOComponentDescription (#className, classID)); \
+ components.erase (classID); \
+}
+
+#define AddGroup(s) gComponentHierarchy->push_back (std::make_pair (s, std::vector<GOComponentDescription> ()));
+#define AddSeparator() { gComponentHierarchy->back ().second.push_back (GOComponentDescription ("", 0)); }
+#define AddAllowsMultipleInclusion(c) { int classID = Object::StringToClassID(#c); gAllowsMultipleInclusion->insert(classID); }
+#define AddAllowComponentReplacement(c) { int classID = Object::StringToClassID(#c); AssertIf (classID == -1); gDoesComponentAllowReplacement->insert (classID); }
+
+
+
+void InitComponentRequirements ()
+{
+ static bool gIsInitialized = false;
+ if (gIsInitialized)
+ return;
+ gIsInitialized = true;
+
+ gRequiredClasses->clear ();
+ gConflictingClasses->clear ();
+ gAllowsMultipleInclusion->clear ();
+ std::vector<SInt32> componentsVec;
+ Object::FindAllDerivedClasses (Object::StringToClassID ("Component"), &componentsVec, true);
+ ClassIDSet components (componentsVec.begin (), componentsVec.end ());
+
+ ////////////////// Setup component requirements
+ AddRequiredClass (Renderer, Transform);
+ AddRequiredClass (MeshFilter, Transform);
+ AddRequiredClass (ParticleAnimator, Transform);
+ AddRequiredClass (EllipsoidParticleEmitter, Transform);
+ AddRequiredClass (WorldParticleCollider, Transform);
+ AddRequiredClass (ParticleSystem, Transform);
+ AddRequiredClass (ParticleSystemRenderer, Transform);
+ AddRequiredClass (Camera, Transform);
+ AddRequiredClass (Light, Transform);
+
+#if ENABLE_SPRITES
+ AddRequiredClass (SpriteRenderer, Transform);
+ AddConflictingClass (MeshFilter, SpriteRenderer);
+ AddConflictingClass (MeshRenderer, SpriteRenderer);
+ AddConflictingClass (SpriteRenderer, MeshFilter);
+ AddConflictingClass (SpriteRenderer, MeshRenderer);
+#endif // ENABLE_SPRITES
+
+#if ENABLE_PHYSICS
+ AddRequiredClass (Rigidbody, Transform);
+ // Do not want 2D physics components on physics objects
+ #if ENABLE_2D_PHYSICS
+ AddConflictingClass (Rigidbody, Rigidbody2D);
+ AddConflictingClass (Rigidbody, Collider2D);
+ AddConflictingClass (Rigidbody, Joint2D);
+ AddConflictingClass (Collider, Rigidbody2D);
+ AddConflictingClass (Collider, Collider2D);
+ AddConflictingClass (Collider, Joint2D);
+ AddConflictingClass (Joint, Rigidbody2D);
+ AddConflictingClass (Joint, Collider2D);
+ AddConflictingClass (Joint, Joint2D);
+ AddConflictingClass (ConstantForce, Rigidbody2D);
+ AddConflictingClass (ConstantForce, Collider2D);
+ AddConflictingClass (ConstantForce, Joint2D);
+
+ #endif // #if ENABLE_2D_PHYSICS
+#endif // #if ENABLE_PHYSICS
+
+#if ENABLE_2D_PHYSICS
+ AddRequiredClass (Rigidbody2D, Transform);
+ AddRequiredClass (Collider2D, Transform);
+ AddRequiredClass (Joint2D, Transform);
+ AddAllowsMultipleInclusion (SpringJoint2D);
+ AddAllowsMultipleInclusion (DistanceJoint2D);
+ AddAllowsMultipleInclusion (HingeJoint2D);
+ AddRequiredClass (Joint2D, Rigidbody2D);
+
+ // Do not want physics components on 2D physics objects
+ #if ENABLE_PHYSICS
+ AddConflictingClass (Rigidbody2D, Rigidbody);
+ AddConflictingClass (Rigidbody2D, Collider);
+ AddConflictingClass (Rigidbody2D, Joint);
+ AddConflictingClass (Rigidbody2D, ConstantForce);
+ AddConflictingClass (Collider2D, Rigidbody);
+ AddConflictingClass (Collider2D, Collider);
+ AddConflictingClass (Collider2D, Joint);
+ AddConflictingClass (Collider2D, ConstantForce);
+ AddConflictingClass (Joint2D, Rigidbody);
+ AddConflictingClass (Joint2D, Collider);
+ AddConflictingClass (Joint2D, Joint);
+ AddConflictingClass (Joint2D, ConstantForce);
+
+ #endif // #if ENABLE_PHYSICS
+#endif // #if ENABLE_2D_PHYSICS
+
+ AddRequiredClass (GUIElement, Transform);
+#if ENABLE_AUDIO
+ AddRequiredClass (AudioSource, Transform);
+ AddRequiredClass (AudioListener, Transform);
+#endif
+#if ENABLE_AUDIO_FMOD
+ AddRequiredClass (AudioReverbZone, Transform);
+ AddRequiredClass (AudioLowPassFilter, AudioBehaviour);
+ AddRequiredClass (AudioEchoFilter, AudioBehaviour);
+ AddRequiredClass (AudioDistortionFilter, AudioBehaviour);
+ AddRequiredClass (AudioReverbFilter, AudioBehaviour);
+ AddRequiredClass (AudioHighPassFilter, AudioBehaviour);
+ AddRequiredClass (AudioChorusFilter, AudioBehaviour);
+#endif
+ AddRequiredClass (TextMesh, Transform);
+
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_2_a1))
+ AddRequiredClass (TextMesh, MeshRenderer);
+
+#if ENABLE_PHYSICS
+ AddRequiredClass (Collider, Transform);
+ AddRequiredClass (Joint, Rigidbody);
+ AddRequiredClass (ConstantForce, Rigidbody);
+#endif
+
+ AddRequiredClass (FlareLayer, Camera);
+ AddRequiredClass (GUILayer, Camera);
+ AddRequiredClass (Halo, Transform);
+
+#if ENABLE_CLOTH
+ AddRequiredClass (Cloth, Transform);
+ AddRequiredClass (ClothRenderer, InteractiveCloth);
+ AddRequiredClass (SkinnedCloth, SkinnedMeshRenderer);
+#endif
+
+ //////////////////// Setup allows multiple inclusion
+ AddAllowsMultipleInclusion (HingeJoint);
+ AddAllowsMultipleInclusion (FixedJoint);
+ AddAllowsMultipleInclusion (CharacterJoint);
+ AddAllowsMultipleInclusion (ConfigurableJoint);
+ AddAllowsMultipleInclusion (SpringJoint);
+
+ AddAllowsMultipleInclusion (AudioSource);
+ AddAllowsMultipleInclusion (OffMeshLink);
+
+ AddAllowsMultipleInclusion (Skybox)
+ AddAllowsMultipleInclusion (MonoBehaviour)
+
+ AddAllowsMultipleInclusion (NetworkView)
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ {
+ AddAllowsMultipleInclusion (BoxCollider);
+ AddAllowsMultipleInclusion (SphereCollider);
+ AddAllowsMultipleInclusion (CapsuleCollider);
+ AddAllowsMultipleInclusion (MeshCollider);
+ }
+
+#if ENABLE_2D_PHYSICS
+ AddAllowsMultipleInclusion (CircleCollider2D);
+ AddAllowsMultipleInclusion (BoxCollider2D);
+ AddAllowsMultipleInclusion (EdgeCollider2D);
+ AddAllowsMultipleInclusion (PolygonCollider2D);
+ #if ENABLE_SPRITECOLLIDER
+ AddAllowsMultipleInclusion (SpriteCollider2D);
+ #endif
+#endif // #if ENABLE_2D_PHYSICS
+
+ #if UNITY_EDITOR
+ gComponentHierarchy->clear ();
+
+
+ ////////////////// Setup component hierarchy / Component menu
+ AddGroup ("Mesh")
+ AddToComponentHierarchy (MeshFilter);
+ AddToComponentHierarchy (TextMesh);
+ AddSeparator ();
+ AddToComponentHierarchy (MeshRenderer);
+
+ AddGroup ("Effects")
+ AddToComponentHierarchy (ParticleSystem);
+ AddToComponentHierarchy (TrailRenderer);
+ AddToComponentHierarchy (LineRenderer);
+ AddToComponentHierarchy (LensFlare);
+ AddToComponentHierarchy (Halo);
+ AddToComponentHierarchy (Projector);
+ AddSeparator ();
+ AddGroup ("Effects/Legacy Particles")
+ AddToComponentHierarchy (EllipsoidParticleEmitter);
+ AddToComponentHierarchy (MeshParticleEmitter);
+ AddSeparator ();
+ AddToComponentHierarchy (ParticleAnimator);
+ AddToComponentHierarchy (WorldParticleCollider);
+ AddSeparator ();
+ AddToComponentHierarchy (ParticleRenderer);
+
+ AddGroup ("Physics")
+ AddToComponentHierarchy (Rigidbody);
+ AddToComponentHierarchy (CharacterController);
+ AddSeparator ();
+ AddToComponentHierarchy (BoxCollider);
+ AddToComponentHierarchy (SphereCollider);
+ AddToComponentHierarchy (CapsuleCollider);
+ AddToComponentHierarchy (MeshCollider);
+ AddToComponentHierarchy (WheelCollider);
+ AddToComponentHierarchy (TerrainCollider);
+ AddSeparator ();
+ AddToComponentHierarchy (InteractiveCloth);
+ AddToComponentHierarchy (SkinnedCloth);
+ AddToComponentHierarchy (ClothRenderer);
+ AddSeparator ();
+ AddToComponentHierarchy (HingeJoint);
+ AddToComponentHierarchy (FixedJoint);
+ AddToComponentHierarchy (SpringJoint);
+ AddToComponentHierarchy (CharacterJoint);
+ AddToComponentHierarchy (ConfigurableJoint);
+ AddSeparator ();
+ AddToComponentHierarchy (ConstantForce);
+
+ #if ENABLE_2D_PHYSICS
+ AddGroup ("Physics 2D");
+ AddToComponentHierarchy (Rigidbody2D);
+ AddSeparator ();
+ AddToComponentHierarchy (CircleCollider2D);
+ AddToComponentHierarchy (BoxCollider2D);
+ AddToComponentHierarchy (EdgeCollider2D);
+ AddToComponentHierarchy (PolygonCollider2D);
+ #if ENABLE_SPRITECOLLIDER
+ AddToComponentHierarchy (SpriteCollider2D);
+ #endif
+ AddSeparator ();
+ AddToComponentHierarchy (SpringJoint2D);
+ AddToComponentHierarchy (DistanceJoint2D);
+ AddToComponentHierarchy (HingeJoint2D);
+ AddToComponentHierarchy (SliderJoint2D);
+ #endif // #if ENABLE_2D_PHYSICS
+
+ AddGroup ("Navigation")
+ AddToComponentHierarchy (NavMeshAgent);
+ AddToComponentHierarchy (OffMeshLink);
+ AddToComponentHierarchy (NavMeshObstacle);
+
+ AddGroup ("Audio")
+ AddToComponentHierarchy (AudioListener);
+ AddToComponentHierarchy (AudioSource);
+ AddToComponentHierarchy (AudioReverbZone);
+
+ if (LicenseInfo::Flag (lf_pro_version)
+ || LicenseInfo::Flag (lf_iphone_pro)
+ || LicenseInfo::Flag (lf_android_pro)
+ || LicenseInfo::Flag (lf_bb10_pro)
+ || LicenseInfo::Flag (lf_tizen_pro))
+ {
+ AddSeparator ();
+ AddToComponentHierarchy (AudioLowPassFilter);
+ AddToComponentHierarchy (AudioHighPassFilter);
+ AddToComponentHierarchy (AudioEchoFilter);
+ AddToComponentHierarchy (AudioDistortionFilter);
+ AddToComponentHierarchy (AudioReverbFilter);
+ AddToComponentHierarchy (AudioChorusFilter);
+ }
+
+ AddGroup ("Rendering")
+ // Camera and components that need camera
+ AddToComponentHierarchy (Camera);
+ AddToComponentHierarchy (Skybox);
+ AddToComponentHierarchy (FlareLayer);
+ AddToComponentHierarchy (GUILayer);
+ AddSeparator ();
+ // Lights related
+ AddToComponentHierarchy (Light);
+ AddToComponentHierarchy (LightProbeGroup);
+ AddSeparator ();
+ // Optimization
+ AddToComponentHierarchy (OcclusionArea);
+ AddToComponentHierarchy (OcclusionPortal);
+ AddToComponentHierarchy (LODGroup);
+ AddSeparator ();
+ // 2D related
+#if ENABLE_SPRITES
+ AddToComponentHierarchy (SpriteRenderer);
+ AddSeparator ();
+#endif
+ // Old old GUI
+ AddToComponentHierarchy (GUITexture);
+ AddToComponentHierarchy (GUIText);
+
+ #define RemoveComponentFromMisc(x) { int removeComponentClassID = Object::StringToClassID (#x); AssertIf (removeComponentClassID == -1); components.erase (removeComponentClassID); }
+ RemoveComponentFromMisc (Transform);
+ RemoveComponentFromMisc (MonoBehaviour);
+ RemoveComponentFromMisc (Component);
+ RemoveComponentFromMisc (Pipeline);
+ RemoveComponentFromMisc (HaloLayer);
+
+ // Although added to a menu above, these would still appear under
+ // misc for non-pro licenses if not explicitly removed here.
+ RemoveComponentFromMisc (AudioLowPassFilter);
+ RemoveComponentFromMisc (AudioHighPassFilter);
+ RemoveComponentFromMisc (AudioEchoFilter);
+ RemoveComponentFromMisc (AudioDistortionFilter);
+ RemoveComponentFromMisc (AudioReverbFilter);
+ RemoveComponentFromMisc (AudioChorusFilter);
+
+ RemoveComponentFromMisc (RaycastCollider); // Deprecated since 3.0
+ RemoveComponentFromMisc (SkinnedMeshRenderer);
+ RemoveComponentFromMisc (Tree);
+ RemoveComponentFromMisc (ParticleSystemRenderer);
+
+
+ // Add others to Miscellaneous
+ const string misc = "Miscellaneous";
+ gComponentHierarchy->push_back (std::make_pair (misc, std::vector<GOComponentDescription> ()));
+ for (ClassIDSet::const_iterator i = components.begin ();i != components.end ();i++)
+ gComponentHierarchy->back ().second.push_back (GOComponentDescription (Object::ClassIDToString (*i), *i));
+ #endif
+
+}
diff --git a/Runtime/Misc/ComponentRequirement.h b/Runtime/Misc/ComponentRequirement.h
new file mode 100644
index 0000000..50f3dde
--- /dev/null
+++ b/Runtime/Misc/ComponentRequirement.h
@@ -0,0 +1,33 @@
+#ifndef COMPONENTREQUIREMENT_H
+#define COMPONENTREQUIREMENT_H
+#include "Runtime/Utilities/vector_set.h"
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+
+// Returns all components that are required for a component
+// of componentClassID to run.
+const vector_set<int>& FindRequiredComponentsForComponent (int componentClassID);
+void FindAllRequiredComponentsRecursive (int componentClassID, vector_set<int>& results);
+const vector_set<int>& FindConflictingComponents (int classID);
+
+// Can the component with componentClassID, be more than once in a gameobject?
+bool DoesComponentAllowMultipleInclusion (int componentClassID);
+
+struct GOComponentDescription
+{
+ int classID;
+ std::string name;
+ GOComponentDescription (const std::string& inName, int inClassID) { name = inName; classID = inClassID; }
+};
+
+typedef std::list<std::pair<std::string, std::vector<GOComponentDescription> > > ComponentsHierarchy;
+// Returns a sorted hierarchy of components
+const ComponentsHierarchy& GetComponentsHierarchy ();
+
+int GetAllowComponentReplacementClass (int classID);
+
+void InitComponentRequirements ();
+
+#endif
diff --git a/Runtime/Misc/DebugUtility.cpp b/Runtime/Misc/DebugUtility.cpp
new file mode 100644
index 0000000..881c271
--- /dev/null
+++ b/Runtime/Misc/DebugUtility.cpp
@@ -0,0 +1,156 @@
+#include "UnityPrefix.h"
+
+#if UNITY_EDITOR
+
+#include "DebugUtility.h"
+#include "Editor/Src/Gizmos/GizmoUtil.h"
+#include "Runtime/Math/Color.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Editor/Src/Gizmos/GizmoManager.h"
+#include "Editor/Src/Gizmos/GizmoRenderer.h"
+#include <vector>
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+using namespace std;
+
+class DebugLineRenderer
+{
+public:
+
+ void AddLine (const Vector3f& v0, const Vector3f& v1, const ColorRGBAf& color, double durationSeconds, bool depthTest)
+ {
+ // We do not support timed debug lines in edit mode
+ if (!IsWorldPlaying ())
+ durationSeconds = 0.0;
+
+ Line l;
+ l.start = v0;
+ l.end = v1;
+ l.color = color;
+ l.depthTest = depthTest;
+ if (durationSeconds > 0.0)
+ {
+ // We use game time (CurTime) to be able to pause game and investigate debug lines.
+ l.removeTime = GetTimeManager ().GetCurTime () + durationSeconds;
+ m_TimedLines.push_back (l);
+ }
+ else
+ {
+ l.removeTime = -1.0;
+ if (GetTimeManager ().IsUsingFixedTimeStep ())
+ m_FixedStepLines.push_back (l);
+ else
+ m_DynamicStepLines.push_back (l);
+ }
+ }
+
+ void Clear ()
+ {
+ if (GetTimeManager ().IsUsingFixedTimeStep ())
+ {
+ m_FixedStepLines.clear ();
+ }
+ else
+ {
+ m_DynamicStepLines.clear ();
+
+ if (!m_TimedLines.empty())
+ {
+ float curTime = GetTimeManager ().GetCurTime ();
+ list<Line>::iterator it = m_TimedLines.begin();
+ while (it != m_TimedLines.end())
+ {
+ if (curTime >= it->removeTime)
+ it = m_TimedLines.erase (it);
+ else
+ it++;
+ }
+ }
+ }
+ }
+
+ void ClearAll ()
+ {
+ m_DynamicStepLines.clear ();
+ m_FixedStepLines.clear ();
+ m_TimedLines.clear ();
+ }
+
+ void Draw () const
+ {
+ for (unsigned i=0; i<m_FixedStepLines.size(); ++i)
+ {
+ const Line& line = m_FixedStepLines[i];
+ gizmos::g_GizmoColor = line.color;
+ DrawLine(line.start, line.end, line.depthTest);
+ }
+ for (unsigned i=0; i<m_DynamicStepLines.size(); ++i)
+ {
+ const Line& line = m_DynamicStepLines[i];
+ gizmos::g_GizmoColor = line.color;
+ DrawLine(line.start, line.end, line.depthTest);
+ }
+
+ for (list<Line>::const_iterator it = m_TimedLines.begin(); it != m_TimedLines.end(); it++)
+ {
+ const Line& line = *it;
+ gizmos::g_GizmoColor = line.color;
+ DrawLine(line.start, line.end, line.depthTest);
+ }
+ }
+ static DebugLineRenderer& Get ();
+ static void StaticDestroy();
+private:
+ struct Line
+ {
+ Vector3f start;
+ Vector3f end;
+ ColorRGBAf color;
+ double removeTime;
+ bool depthTest;
+ };
+ list<Line> m_TimedLines;
+ vector<Line> m_FixedStepLines;
+ vector<Line> m_DynamicStepLines;
+
+};
+
+static RegisterRuntimeInitializeAndCleanup gDebugLineRendererInstance(NULL, DebugLineRenderer::StaticDestroy);
+static DebugLineRenderer* g_DebugLineRenderer = NULL;
+
+void DebugLineRenderer::StaticDestroy()
+{
+ if(g_DebugLineRenderer)
+ UNITY_DELETE(g_DebugLineRenderer, kMemEditorUtility);
+}
+
+DebugLineRenderer& DebugLineRenderer::Get ()
+{
+ if(g_DebugLineRenderer == NULL)
+ g_DebugLineRenderer = UNITY_NEW_AS_ROOT(DebugLineRenderer,kMemEditorUtility, "Manager", "DebugLineRenderer");
+ return *g_DebugLineRenderer;
+}
+
+void ClearLines ()
+{
+ DebugLineRenderer::Get ().Clear ();
+}
+
+void ClearAllLines ()
+{
+ DebugLineRenderer::Get ().ClearAll ();
+}
+
+void DrawDebugLinesGizmo ()
+{
+ DebugLineRenderer::Get ().Draw ();
+}
+
+void DebugDrawLine (const Vector3f& p0, const Vector3f& p1, const ColorRGBAf& color, double durationSeconds /*=0.0*/, bool depthTest /*=true*/)
+{
+ DebugLineRenderer::Get ().AddLine (p0, p1, color, durationSeconds, depthTest);
+}
+
+#endif
diff --git a/Runtime/Misc/DebugUtility.h b/Runtime/Misc/DebugUtility.h
new file mode 100644
index 0000000..90d355c
--- /dev/null
+++ b/Runtime/Misc/DebugUtility.h
@@ -0,0 +1,30 @@
+#ifndef DEBUGUTILITY_H
+#define DEBUGUTILITY_H
+
+class Vector3f;
+class ColorRGBAf;
+class EditorExtension;
+
+#if GAMERELEASE
+
+//#define DebugDrawLine (a,b,c,d) ;
+//#define ClearLines () ;
+//#define PauseEditor () ;
+inline void DebugDrawLine (const Vector3f& p0, const Vector3f& p1, const ColorRGBAf& color, double durationSeconds, bool depthTest){}
+inline void ClearLines (){}
+inline void ClearAllLines (){}
+inline void PauseEditor (){}
+
+#else
+
+void DebugDrawLine (const Vector3f& p0, const Vector3f& p1, const ColorRGBAf& color, double durationSeconds, bool depthTest);
+void ClearLines ();
+void ClearAllLines ();
+void PauseEditor ();
+
+void DrawDebugLinesGizmo ();
+
+#endif
+
+
+#endif
diff --git a/Runtime/Misc/DeveloperConsole.cpp b/Runtime/Misc/DeveloperConsole.cpp
new file mode 100644
index 0000000..13def26
--- /dev/null
+++ b/Runtime/Misc/DeveloperConsole.cpp
@@ -0,0 +1,475 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/DeveloperConsole.h"
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+
+#include "Runtime/IMGUI/GUILabel.h"
+#include "Runtime/IMGUI/GUIButton.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+
+#include "Runtime/Threads/ThreadUtility.h"
+
+#include "Runtime/Misc/ReproductionLog.h"
+
+
+static DeveloperConsole* s_DeveloperConsole = 0;
+
+// This function performs the normal loggin operation with the initialized developer console
+void DeveloperConsole_HandleLogFunction (const std::string& condition, const std::string& stackTrace, int type)
+{
+ // Bas Smit and Elvis Alistar suggested to disable the developer console when we are in reproduction mode.
+ // [17:00:40] Bas Smit: I dont see the dev console adding value to the rig
+ // [17:00:57] Elvis Alistar: it actual makes the results very difficult to interpret
+ // [17:01:07] Elvis Alistar: as most of the runs will be different
+#if SUPPORT_REPRODUCE_LOG
+ if (GetReproduceMode() != kPlaybackReproduceLog)
+#endif
+ {
+ UnityMemoryBarrier();
+ if (s_DeveloperConsole)
+ s_DeveloperConsole->HandleLog(condition, stackTrace, static_cast<LogType>(type));
+ }
+}
+
+
+// If nothing is created, then CleanupDeveloperConsole () is a no-op
+void CleanupDeveloperConsole ()
+{
+ RegisterLogCallback(NULL, false);
+
+ DeveloperConsole* tmp = 0;
+ std::swap(s_DeveloperConsole, tmp); // "non-locking destruction"; it is carried out a little bit later
+
+ UnityMemoryBarrier();
+
+ delete tmp;
+}
+
+void InitializeDeveloperConsole ()
+{
+ Assert( NULL == s_DeveloperConsole);
+ s_DeveloperConsole = new DeveloperConsole();
+ RegisterLogCallback(DeveloperConsole_HandleLogFunction, true);
+}
+
+DeveloperConsole& GetDeveloperConsole()
+{
+ Assert( NULL != s_DeveloperConsole);
+ return *s_DeveloperConsole;
+}
+DeveloperConsole* GetDeveloperConsolePtr()
+{
+ return s_DeveloperConsole;
+}
+
+
+DeveloperConsole::DeveloperConsole()
+ : m_ConsoleClosed(false)
+ , m_LogBuffer()
+
+ // State
+ , m_GUIState()
+ , m_Content()
+
+ // Used styles
+ , m_LabelStyle(0)
+ , m_ButtonStyle(0)
+ , m_BoxStyle(0)
+
+ // Titles for GUI elements
+ , m_DevelopmentConsoleBoxTitle("Development Console")
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ , m_OpenLogFileButtonTitle("Open Log File")
+#endif
+ , m_ClearButtonTitle("Clear")
+ , m_CloseButtonTitle("Close")
+{}
+
+DeveloperConsole::~DeveloperConsole()
+{
+ delete m_LabelStyle;
+ delete m_ButtonStyle;
+ delete m_BoxStyle;
+}
+
+LogBufferEntry::LogBufferEntry(const std::string& condition, const std::string& stripped_stacktrace, LogType log)
+ : type(log)
+{
+ // Concatenate condition and the stacktrace;
+ // the only difference between expanded and non-expanded console entry is,
+ // actually, just the length of the rendered string
+ std::string strace(condition);
+ cond_len = strace.length(); // Preserve the length of the condition
+
+ strace += "\n> ";
+ strace += stripped_stacktrace;
+
+ // Initialize the debug string
+ UTF8ToUTF16String(strace.c_str(), debug_text);
+}
+
+void DeveloperConsole::HandleLog(const std::string& condition, const std::string& strippedStacktrace, LogType type)
+{
+ if (type > kLogLevel && type != LogType_Exception)
+ return;
+
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_NewEntriesMutex);
+#endif
+
+ m_NewEntries.push_back( LogBufferEntry(condition, strippedStacktrace, type));
+ if (m_NewEntries.size() > kMaxNumberOfLogMessages)
+ {
+ m_NewEntries.pop_front(); // Forget the first message
+ }
+}
+
+ObjectGUIState& DeveloperConsole::GetObjectGUIState()
+{
+ return m_GUIState;
+}
+
+bool DeveloperConsole::IsVisible () const
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock consoleLock(m_ConsoleMutex); // Required for m_LogBuffer
+ Mutex::AutoLock newEntriesLock(m_NewEntriesMutex); // Required for m_NewEntries
+#endif
+ if (m_ConsoleClosed)
+ return false;
+ return !m_LogBuffer.empty() || !m_NewEntries.empty();
+}
+
+void DeveloperConsole::SetOpen(bool opened)
+{
+ m_ConsoleClosed = !opened;
+}
+void DeveloperConsole::Clear()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+ m_LogBuffer.clear();
+}
+bool DeveloperConsole::HasLogMessages()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+ return !m_LogBuffer.empty();
+}
+
+// Initializes an existing instance of a UTF16String in a fast way (avoiding double-copy pattern)
+inline static void FastUTF16StringInit(const UTF16String& src, UTF16String& dst)
+{
+ dst.TakeOverPreAllocatedUTF16Bytes(src.text, src.length);
+ dst.owns = false;
+}
+
+inline static void SetupLogEntryGUIContent(GUIContent& content, const LogBufferEntry& entry)
+{
+ FastUTF16StringInit(entry.debug_text, content.m_Text);
+ if (entry.cond_len > 0)
+ {
+ // if the entry is not expanded, we want to show only the part
+ // that does not include the stacktrace
+ content.m_Text.length = entry.cond_len;
+ }
+}
+
+bool DeveloperConsole::DoGUI()
+{
+#if SUPPORT_THREADS
+ Mutex::AutoLock lock(m_ConsoleMutex);
+#endif
+
+ AppendNewEntries();
+
+ // New entries were copied into into m_LogBuffer, so just check that
+ if (m_ConsoleClosed || m_LogBuffer.empty())
+ return false;
+
+ if (0 == m_LabelStyle) // if this is not initialized,
+ { // then all styles need to be fetched
+ InitGUIStyles();
+ }
+
+ Assert( NULL != m_LabelStyle);
+ Assert( NULL != m_ButtonStyle);
+ Assert( NULL != m_BoxStyle);
+
+ GUIState &guiState = GetGUIState ();
+ guiState.m_CanvasGUIState.m_GUIClipState.BeginOnGUI (*guiState.m_CurrentEvent);
+ guiState.BeginOnGUI(GetObjectGUIState());
+
+ // GUI code must go here
+ const float screen_width = GetScreenManager().GetWidth();
+ const float screen_height = GetScreenManager().GetHeight();
+
+ const float half_screen_width = 0.5f * screen_width;
+
+ float box_height = DrawBox(guiState, screen_height, half_screen_width);
+
+ Rectf position;
+ float label_pos = 0.f;
+ GUIStyle& labelStyle = *m_LabelStyle;
+
+ // We output messages in this order: the newest at the bottom and the oldest at the top
+ for( std::list<LogBufferEntry>::reverse_iterator rit = m_LogBuffer.rbegin();
+ rit != m_LogBuffer.rend(); ++rit)
+ {
+ SetupLogEntryGUIContent(m_Content, *rit);
+
+ switch (rit->type)
+ {
+ case LogType_Error:
+ case LogType_Assert:
+ case LogType_Exception:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 0.0f, 0.0f, 1.0f); // Color.red;
+ break;
+
+ case LogType_Warning:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 0.0f, 0.0f, 1.0f); // Color.red;
+ break;
+
+ default:
+ labelStyle.m_Normal.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f); // Color.white;
+ break;
+ }
+
+ label_pos += rit->label_height; // Label height has been previously computed in DrawBox
+
+ position.Set(0, screen_height - label_pos, half_screen_width, box_height);
+ if (IMGUI::GUIButton(guiState, position, m_Content, labelStyle))
+ {
+ rit->cond_len = -rit->cond_len;
+ }
+ }
+
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ position.Set(half_screen_width, screen_height - 75, 70, 20);
+ if (DrawButton(guiState, position, m_OpenLogFileButtonTitle))
+ {
+ DeveloperConsole_OpenConsoleFile();
+ }
+#endif // UNITY_WEBPLAYER_AND_STANDALONE
+
+ position.Set(half_screen_width, screen_height - 50, 70, 20);
+ if (DrawButton(guiState, position, m_ClearButtonTitle))
+ {
+ m_LogBuffer.clear();
+ }
+
+ position.Set(half_screen_width, screen_height - 25, 70, 20);
+ if (DrawButton(guiState, position, m_CloseButtonTitle))
+ {
+ m_ConsoleClosed = true;
+ }
+
+ guiState.EndOnGUI ();
+ guiState.m_CanvasGUIState.m_GUIClipState.EndOnGUI (*guiState.m_CurrentEvent);
+
+ return guiState.m_CurrentEvent->type == InputEvent::kUsed;
+}
+
+void DeveloperConsole::AppendNewEntries()
+{
+#if SUPPORT_THREADS
+ // We already have a lock on m_ConsoleMutex for writing to m_LogBuffer
+ // We need a lock on new entries potentially added from other threads
+ Mutex::AutoLock lock(m_NewEntriesMutex);
+#endif
+
+ while (!m_NewEntries.empty())
+ {
+ m_LogBuffer.push_back(m_NewEntries.front());
+ m_NewEntries.pop_front();
+ if (m_LogBuffer.size() > kMaxNumberOfLogMessages)
+ {
+ m_LogBuffer.pop_front(); // Forget the first message
+ }
+ m_ConsoleClosed = false; // New log message, the console pops back
+ }
+}
+
+bool DeveloperConsole::DrawButton(GUIState& guiState, const Rectf& position, const UTF16String& label)
+{
+ FastUTF16StringInit(label, m_Content.m_Text);
+ return IMGUI::GUIButton(guiState, position, m_Content, *m_ButtonStyle);
+}
+
+float DeveloperConsole::DrawBox(GUIState& guiState, float height, float width)
+{
+ const GUIStyle& labelStyle = *m_LabelStyle;
+
+ // Compute how much space log messages themselves would take
+ float box_body_height = 0.f;
+ for( std::list<LogBufferEntry>::iterator it = m_LogBuffer.begin();
+ it != m_LogBuffer.end(); ++it)
+ {
+ SetupLogEntryGUIContent(m_Content, *it);
+ it->label_height = labelStyle.CalcHeight(m_Content, width);
+ box_body_height += it->label_height;
+ }
+
+ // Accomodate the box title
+ GUIStyle& boxStyle = *m_BoxStyle;
+
+ FastUTF16StringInit(m_DevelopmentConsoleBoxTitle, m_Content.m_Text);
+ float box_title_height = boxStyle.CalcHeight(m_Content, width);
+
+ // Summa summarum: the height of the developer console is its body plus its title
+ float dev_console_height = box_body_height + box_title_height;
+
+ Rectf position(0, height - dev_console_height, width, dev_console_height);
+ IMGUI::GUILabel(guiState, position, m_Content, boxStyle);
+ return box_body_height;
+}
+
+
+// Hardcoded styles (the values copied from the Editor)
+
+static GUIStyle* CreateLabelIconGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ style->m_Normal.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f);//ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+ style->m_Hover.textColor = ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+ style->m_OnNormal.textColor = ColorRGBAf (0.0f, 0.0f, 0.0f, 1.0f);
+
+ style->m_Padding.left = 3; // so that the messages do not look glued to the left of the screen
+ style->m_Padding.right = 0;
+ style->m_Padding.top = 3;
+ style->m_Padding.bottom = 3;
+
+ style->m_RichText = true;
+ style->m_Alignment = kUpperLeft;
+ style->m_Clipping = kOverflow;
+
+ style->m_ImagePosition = kTextOnly;
+ style->m_WordWrap = true;
+
+ return style;
+}
+
+static Texture2D* GetResourceTexture(const char* texture_name)
+{
+ return reinterpret_cast<Texture2D*>(
+ GetBuiltinResourceManager ().GetResource (ClassID(Texture2D), texture_name));
+}
+
+static GUIStyle* CreateButtonGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ const float normal_color = 230.0f / 255.0f;
+ style->m_Normal.textColor = ColorRGBAf (normal_color, normal_color, normal_color, 1.0f);
+ style->m_Normal.background = GetResourceTexture("GameSkin/button.png");
+
+ style->m_Hover.textColor = ColorRGBAf (1.0f, 1.0f, 1.0f, 1.0f);
+ style->m_Hover.background = GetResourceTexture("GameSkin/button hover.png");
+
+ style->m_Active.textColor = style->m_Normal.textColor;
+ style->m_Active.background = GetResourceTexture("GameSkin/button active.png");
+
+ style->m_Focused.textColor = style->m_Hover.textColor;
+
+ style->m_OnNormal.textColor = style->m_Normal.textColor;
+ style->m_OnNormal.background = GetResourceTexture("GameSkin/button on.png");
+
+ style->m_OnHover.textColor = style->m_Hover.textColor;
+ style->m_OnHover.background = GetResourceTexture("GameSkin/button on hover.png");
+
+ style->m_OnActive.textColor = style->m_Normal.textColor;
+ style->m_OnActive.background = GetResourceTexture("GameSkin/button active.png");
+
+ style->m_Border.left = 6;
+ style->m_Border.right = 6;
+ style->m_Border.top = 6;
+ style->m_Border.bottom = 4;
+
+ style->m_Margin.left = 4;
+ style->m_Margin.right = 4;
+ style->m_Margin.top = 4;
+ style->m_Margin.bottom = 4;
+
+ style->m_Padding.left = 6;
+ style->m_Padding.right = 6;
+ style->m_Padding.top = 3;
+ style->m_Padding.bottom = 3;
+
+ style->m_RichText = false;
+ style->m_FontSize = 10; // make button captions a little bit smaller
+ style->m_Alignment = kMiddleCenter;
+
+ style->m_StretchWidth = true;
+ style->m_StretchHeight = false;
+
+ return style;
+}
+
+static GUIStyle* CreateBoxGUIStyle ()
+{
+ GUIStyle* style = new GUIStyle ();
+
+ const float normal_color = 204.0f / 255.0f;
+ style->m_Normal.textColor = ColorRGBAf (normal_color, normal_color, normal_color, 1.0f);
+ style->m_Normal.background = GetResourceTexture("GameSkin/box.png");
+
+ style->m_Border.left = 6;
+ style->m_Border.right = 6;
+ style->m_Border.top = 6;
+ style->m_Border.bottom = 6;
+
+ style->m_Margin.left = 4;
+ style->m_Margin.right = 4;
+ style->m_Margin.top = 4;
+ style->m_Margin.bottom = 4;
+
+ style->m_Padding.left = 4;
+ style->m_Padding.right = 4;
+ style->m_Padding.top = 4;
+ style->m_Padding.bottom = 4;
+
+ style->m_RichText = false;
+ style->m_FontStyle = 1; // The box title is bold
+ style->m_Alignment = kUpperCenter;
+
+ style->m_StretchWidth = true;
+ style->m_StretchHeight = false;
+
+ return style;
+}
+
+void DeveloperConsole::InitGUIStyles()
+{
+ m_LabelStyle = CreateLabelIconGUIStyle();
+ m_ButtonStyle = CreateButtonGUIStyle();
+ m_BoxStyle = CreateBoxGUIStyle();
+}
+
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+
+#if UNITY_WIN
+#include <ShellAPI.h>
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#endif
+
+void DeveloperConsole_OpenConsoleFile ()
+{
+ std::string path = GetConsoleLogPath();
+#if UNITY_OSX
+ system (("open \""+path+"\"").c_str());
+#elif UNITY_WIN && !UNITY_WINRT
+ std::wstring widePath;
+ ConvertUnityPathName(path, widePath);
+
+ int res = (int)::ShellExecuteW( NULL, L"open", widePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
+ if (res <= 32)
+ res = (int)::ShellExecuteW( NULL, L"edit", widePath.c_str(), NULL, NULL, SW_SHOWNORMAL);
+#else
+ ErrorString ("Opening Console File is not supported on this platform.");
+#endif
+}
diff --git a/Runtime/Misc/DeveloperConsole.h b/Runtime/Misc/DeveloperConsole.h
new file mode 100644
index 0000000..59c103b
--- /dev/null
+++ b/Runtime/Misc/DeveloperConsole.h
@@ -0,0 +1,154 @@
+#pragma once
+
+#include "Runtime/Utilities/LogAssert.h" // for LogType
+
+// Can't get the DeveloperConsole to work on iOS, Android or XBox
+// (fails with Can't add script behaviour DeveloperConsole. The scripts file name does not match the name of the class defined in the script!)
+// No time to look into, need to merge core. Fix & Enabled later.
+#define UNITY_HAS_DEVELOPER_CONSOLE \
+ !(UNITY_FLASH || UNITY_WEBGL) && GAMERELEASE && UNITY_DEVELOPER_BUILD && !(UNITY_IPHONE || UNITY_XENON || UNITY_ANDROID || UNITY_PS3)
+
+
+#if UNITY_HAS_DEVELOPER_CONSOLE
+
+#include "Runtime/IMGUI/GUIState.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#include "Runtime/IMGUI/GUIContent.h"
+
+#if SUPPORT_THREADS
+#include "Runtime/Threads/Mutex.h"
+#endif
+
+struct LogBufferEntry
+{
+ // The sign bit is used to see whether a log entry is expanded or not
+ int cond_len;
+
+ LogType type;
+
+ // This is only used as cache, so that label height
+ // computation would not be done twice
+ float label_height;
+
+ UTF16String debug_text;
+
+ // Constructs the object and initializes UTF16String members
+ LogBufferEntry(const std::string& condition, const std::string& stripped_stacktrace, LogType log);
+
+ // Moves the ownership of UTF16String memory to this object
+ LogBufferEntry(const LogBufferEntry& rhs)
+ : cond_len(rhs.cond_len)
+ , type(rhs.type)
+ {
+ fast_string_init(rhs);
+ }
+
+ // Moves the ownership of UTF16String memory to this object
+ LogBufferEntry& operator=(const LogBufferEntry& rhs)
+ {
+ fast_string_init(rhs);
+ cond_len = rhs.cond_len;
+ type = rhs.type;
+ return *this;
+ }
+
+private:
+ static void change_memory_ownership_impl(UTF16String& src, UTF16String& dst)
+ {
+ dst.TakeOverPreAllocatedUTF16Bytes(src.text, src.length);
+ dst.owns = src.owns; // it is possible that src DOES NOT own the string
+ src.owns = false; // if dst owns, then src must not own anymore;
+ // if dst does not own, then src does not own either, so this assignment is a no-op.
+ }
+
+ void fast_string_init(const LogBufferEntry& rhs)
+ {
+ change_memory_ownership_impl(*const_cast<UTF16String*>(&rhs.debug_text), debug_text);
+ }
+};
+
+
+class DeveloperConsole : private NonCopyable
+{
+public:
+ static const LogType kLogLevel = LogType_Assert;
+
+ // The number of log messages that are displayed
+ static const unsigned int kMaxNumberOfLogMessages = 10u;
+
+ // Interface that is needed by the GUIManager in order to render the console, etc.
+ bool IsVisible() const;
+
+ void SetOpen(bool opened);
+
+ void Clear();
+
+ bool HasLogMessages();
+
+ bool DoGUI();
+
+ ObjectGUIState& GetObjectGUIState();
+
+private:
+ // Developer console should be instantiated only and only from InitializeDeveloperConsole,
+ // thus its constructor is made private. For all other functions, for example,
+ // functions in LogAssert.h, the only accessible member functions are the static ones.
+ friend void InitializeDeveloperConsole ();
+ friend void CleanupDeveloperConsole ();
+
+ DeveloperConsole();
+ ~DeveloperConsole();
+
+private:
+ friend void DeveloperConsole_HandleLogFunction (const std::string& condition, const std::string &stackTrace, int type);
+
+ void HandleLog(const std::string& condition, const std::string& strippedStacktrace, LogType logType);
+
+private:
+ // Helper member functions
+ void InitGUIStyles();
+
+ void AppendNewEntries();
+
+ bool DrawButton(GUIState& guiState, const Rectf& position, const UTF16String& label);
+ float DrawBox(GUIState& guiState, float height, float width);
+
+private:
+ bool m_ConsoleClosed;
+ std::list<LogBufferEntry> m_LogBuffer;
+ std::list<LogBufferEntry> m_NewEntries;
+ ObjectGUIState m_GUIState;
+ GUIContent m_Content;
+
+ // GUI styles that are used in DeveloperConsole.
+ // Initial values are currently copied from the default skin and hardcoded.
+ GUIStyle* m_LabelStyle;
+ GUIStyle* m_ButtonStyle;
+ GUIStyle* m_BoxStyle;
+
+ // Labels are cached, so that they are not allocated and copied on each DoGUI invocation
+ const UTF16String m_DevelopmentConsoleBoxTitle;
+#if UNITY_WEBPLAYER_AND_STANDALONE
+ const UTF16String m_OpenLogFileButtonTitle;
+#endif
+ const UTF16String m_ClearButtonTitle;
+ const UTF16String m_CloseButtonTitle;
+
+#if SUPPORT_THREADS
+ mutable Mutex m_ConsoleMutex;
+ mutable Mutex m_NewEntriesMutex;
+#endif
+};
+
+
+DeveloperConsole& GetDeveloperConsole();
+DeveloperConsole* GetDeveloperConsolePtr();
+void CleanupDeveloperConsole ();
+void InitializeDeveloperConsole ();
+
+void DeveloperConsole_HandleLogFunction (const std::string& condition, const std::string &stackTrace, int type);
+
+#endif // UNITY_HAS_DEVELOPER_CONSOLE
+
+// This function is used in UnityEngineDebug.cpp
+void DeveloperConsole_OpenConsoleFile();
diff --git a/Runtime/Misc/GOCreation.cpp b/Runtime/Misc/GOCreation.cpp
new file mode 100644
index 0000000..d6277b8
--- /dev/null
+++ b/Runtime/Misc/GOCreation.cpp
@@ -0,0 +1,95 @@
+#include "UnityPrefix.h"
+#include "GOCreation.h"
+#include "GameObjectUtility.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Dynamics/CapsuleCollider.h"
+#include "Runtime/Dynamics/Collider.h"
+#include "ResourceManager.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Interfaces/IPhysics.h"
+
+GameObject* CreatePrimitive (int type)
+{
+ GameObject* go = NULL;
+ if (type == kPrimitiveSphere)
+ {
+ go = &CreateGameObject ("Sphere", "MeshFilter", "SphereCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Sphere.fbx"));
+
+ Collider* collider = go->QueryComponent (Collider);
+ if (collider)
+ {
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ SmartResetObject(*collider);
+ }
+
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveCapsule)
+ {
+ go = &CreateGameObject ("Capsule", "MeshFilter", "CapsuleCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Capsule.fbx"));
+
+ CapsuleCollider* collider = go->QueryComponent (CapsuleCollider);
+ if (collider != NULL)
+ {
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ SmartResetObject(*collider);
+ else
+ GetIPhysics()->CapsuleColliderSetHeight(*collider, 2.0f);
+ }
+
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveCylinder)
+ {
+ go = &CreateGameObject ("Cylinder", "MeshFilter", "CapsuleCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Cylinder.fbx"));
+
+ CapsuleCollider* collider = go->QueryComponent (CapsuleCollider);
+ if (collider != NULL)
+ {
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ SmartResetObject(*collider);
+ else
+ GetIPhysics()->CapsuleColliderSetHeight(*collider, 2.0f);
+ }
+
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveCube)
+ {
+ go = &CreateGameObject ("Cube",
+ "MeshFilter",
+#if ENABLE_PHYSICS
+ "BoxCollider",
+#endif
+ "MeshRenderer",
+ NULL);
+
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("Cube.fbx"));
+#if ENABLE_PHYSICS
+ SmartResetObject(go->GetComponent (Collider));
+#endif
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitivePlane)
+ {
+ go = &CreateGameObject ("Plane", "MeshFilter", "MeshCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("New-Plane.fbx"));
+ SmartResetObject(go->GetComponent (Collider));
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+ else if (type == kPrimitiveQuad)
+ {
+ go = &CreateGameObject ("Quad", "MeshFilter", "MeshCollider", "MeshRenderer", NULL);
+ go->GetComponent (MeshFilter).SetSharedMesh (GetBuiltinResource<Mesh> ("Quad.fbx"));
+ SmartResetObject(go->GetComponent (Collider));
+ go->GetComponent (Renderer).SetMaterial (Material::GetDefaultDiffuseMaterial(), 0);
+ }
+
+ return go;
+}
diff --git a/Runtime/Misc/GOCreation.h b/Runtime/Misc/GOCreation.h
new file mode 100644
index 0000000..67d04b4
--- /dev/null
+++ b/Runtime/Misc/GOCreation.h
@@ -0,0 +1,18 @@
+#ifndef GO_CREATION_CPP_H
+#define GO_CREATION_CPP_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+
+enum
+{
+ kPrimitiveSphere = 0,
+ kPrimitiveCapsule = 1,
+ kPrimitiveCylinder = 2,
+ kPrimitiveCube = 3,
+ kPrimitivePlane = 4,
+ kPrimitiveQuad = 5
+};
+
+Unity::GameObject* CreatePrimitive (int type);
+
+#endif
diff --git a/Runtime/Misc/GOCreationTests.cpp b/Runtime/Misc/GOCreationTests.cpp
new file mode 100644
index 0000000..5d60e70
--- /dev/null
+++ b/Runtime/Misc/GOCreationTests.cpp
@@ -0,0 +1,54 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Misc/GOCreation.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Dynamics/CapsuleCollider.h"
+
+SUITE (GameObjectCreationTests)
+{
+ TEST (CreateSphereTest)
+ {
+ GameObject* go = CreatePrimitive(kPrimitiveSphere);
+
+ CHECK_EQUAL(go->GetComponentCount(), 4);
+ CHECK_EQUAL(go->GetName(), "Sphere");
+ CHECK(!go->GetComponent(MeshFilter).GetSharedMesh().IsNull());
+ CHECK_EQUAL(go->GetComponent(Renderer).GetMaterialCount(), 1);
+ }
+
+ TEST (CreateCubeTest)
+ {
+ GameObject* go = CreatePrimitive(kPrimitiveCube);
+
+#if ENABLE_PHYSICS
+ int count = 4;
+#else
+ int count = 3;
+#endif
+
+ CHECK_EQUAL(go->GetComponentCount(), count);
+ CHECK_EQUAL(go->GetName(), "Cube");
+ CHECK(!go->GetComponent(MeshFilter).GetSharedMesh().IsNull());
+ CHECK_EQUAL(go->GetComponent(Renderer).GetMaterialCount(), 1);
+ }
+
+ TEST (CreateCylinderTest)
+ {
+ GameObject* go = CreatePrimitive(kPrimitiveCylinder);
+
+ CHECK_EQUAL(go->GetComponentCount(), 4);
+ CHECK_EQUAL(go->GetName(), "Cylinder");
+ CHECK(!go->GetComponent(MeshFilter).GetSharedMesh().IsNull());
+ CHECK_EQUAL(go->GetComponent(Renderer).GetMaterialCount(), 1);
+
+#if ENABLE_PHYSICS
+ CHECK_EQUAL(go->GetComponent (CapsuleCollider).GetHeight(), 2.0f);
+#endif
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/Runtime/Misc/GameObjectUtility.cpp b/Runtime/Misc/GameObjectUtility.cpp
new file mode 100644
index 0000000..b2ee5b4
--- /dev/null
+++ b/Runtime/Misc/GameObjectUtility.cpp
@@ -0,0 +1,1276 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "ComponentRequirement.h"
+#include "GameObjectUtility.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "BatchDeleteObjects.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoScriptCache.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/dynamic_bitset.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/IterateTypeTree.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "BuildSettings.h"
+#include "Runtime/Camera/Camera.h" //// @TODO: Only used by FindMainCamera, should be moved to a different file?
+#include "Runtime/BaseClasses/Tags.h"
+#include "Player.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/Prefabs/Prefab.h"
+#endif
+
+PROFILER_INFORMATION(gDestroyProfile, "Destroy", kProfilerOther)
+
+using namespace std;
+const char* kUnityEngine = "UnityEngine";
+
+#if UNITY_EDITOR
+
+static std::vector<AddComponentCallbackFunction*> gGOAddComponentCallbacks;
+
+void RegisterAddComponentCallback (AddComponentCallbackFunction* callback)
+{
+ gGOAddComponentCallbacks.push_back(callback);
+}
+
+static void InvokeAddComponentCallback (Unity::Component& com)
+{
+ for (int i=0;i<gGOAddComponentCallbacks.size();i++)
+ gGOAddComponentCallbacks[i] (com);
+}
+#else
+inline void InvokeAddComponentCallback (Unity::Component& com) { }
+#endif
+
+
+
+bool IsComponentSubclassOfMonoClass (Unity::Component& com, MonoClass* requiredClass);
+bool IsComponentSubclassOfMonoClass (Unity::Component& com, MonoClass* requiredClass)
+{
+#if ENABLE_SCRIPTING
+ int curComponentClassID = com.GetClassID();
+
+ ScriptingClassPtr curKlass = GetMonoManager().ClassIDToScriptingClass (curComponentClassID);
+
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&com);
+ if (monoBehaviour)
+ curKlass = monoBehaviour->GetClass();
+
+ // Check classID against behaviour
+ if (curKlass && requiredClass)
+ {
+ if (requiredClass == curKlass || scripting_class_is_subclass_of (curKlass, requiredClass))
+ return true;
+ }
+#endif
+ return false;
+}
+
+Unity::Component* AddComponentUnchecked (GameObject& go, int classID, MonoScriptPtr script, string* error)
+{
+ // Produce object
+ Unity::Component* o = static_cast<Unity::Component*>(Object::Produce (classID));
+ if (o == NULL)
+ {
+ if (error)
+ *error = Format ("Can't add component because the component '%s' can't be produced.", Object::ClassIDToString (classID).c_str());
+ return NULL;
+ }
+
+ AssertIf (!o->IsDerivedFrom (ClassID (Component)));
+
+ o->Reset ();
+ go.AddComponentInternal (static_cast<Unity::Component*> (o));
+
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (o);
+ if (monoBehaviour)
+ {
+#if ENABLE_SCRIPTING
+ int instanceID = o->GetInstanceID();
+ monoBehaviour->SetScript (script);
+ // Check if the object has destroyed itself in Awake.
+ if (!PPtr<Object> (instanceID).IsValid())
+ return NULL;
+#else
+ AssertIf( script == 0 && "Mono's disabled" );
+#endif
+
+#if UNITY_EDITOR
+ if (!IsInsidePlayerLoop())
+ ApplyDefaultReferences(*monoBehaviour, script->GetDefaultReferences());
+#endif
+ }
+
+ o->Reset();
+ o->SmartReset();
+ o->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
+ o->SetDirty ();
+
+ InvokeAddComponentCallback (*o);
+
+ return static_cast<Unity::Component*> (o);
+}
+
+bool CheckForAbstractClass(GameObject& go, int classID, string* error)
+{
+ if (Object::ClassIDToRTTI(classID)->isAbstract)
+ {
+ // list all derived components
+ string objectList;
+ vector<SInt32> derivedObjects;
+ Object::FindAllDerivedClasses (classID, &derivedObjects);
+ for (vector<SInt32>::const_iterator it=derivedObjects.begin();it!=derivedObjects.end();++it)
+ {
+ SInt32 id = (*it);
+ objectList += Format("'%s'", Object::ClassIDToString (id).c_str());
+ if (it != derivedObjects.end()-1)
+ objectList += " or ";
+ }
+ *error = Format ("Adding component failed. Add required component of type %s to the game object '%s' first.", objectList.c_str(), go.GetName ());
+ return false;
+ }
+ return true;
+}
+
+static void AddRequiredScriptComponents (GameObject& go, MonoScript* script, std::set<ScriptingClassPtr> &processed)
+{
+#if ENABLE_MONO || UNITY_WINRT
+ ScriptingArrayPtr array = RequiredComponentsOf(script->GetClass());
+
+ if (array)
+ {
+ for (int j = 0; j < GetScriptingArraySize(array); j++)
+ {
+ ScriptingObjectPtr requiredClassObject = Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(array, j);
+ if (requiredClassObject == SCRIPTING_NULL)
+ continue;
+
+ ScriptingClassPtr requiredClass = GetScriptingTypeRegistry().GetType (requiredClassObject);
+
+ if (requiredClass != NULL && processed.find(requiredClass) != processed.end())
+ continue;
+
+ // Is that script/component already added to the game obejct?
+ bool needToAdd = true;
+ for (int c=0;c<go.GetComponentCount();c++)
+ {
+ if (IsComponentSubclassOfMonoClass (go.GetComponentAtIndex(c), requiredClass))
+ {
+ needToAdd = false;
+ break;
+ }
+ }
+
+ if (needToAdd)
+ {
+ int clsID = -1;
+ MonoScript* requiredScript = NULL;
+ if (StrICmp (scripting_class_get_namespace (requiredClass), kUnityEngine) == 0)
+ clsID = Object::StringToClassID (scripting_class_get_name (requiredClass));
+
+ if (clsID == -1 || !Object::IsDerivedFromClassID (clsID, ClassID (Component)))
+ {
+ // We can't find the script.
+ // Not very good but we will just let it slip through
+ requiredScript = GetMonoScriptManager().FindRuntimeScript(requiredClass);
+ if (requiredScript == NULL )
+ continue;
+ clsID = ClassID (MonoBehaviour);
+ }
+
+ string internalError;
+ if (!CheckForAbstractClass(go, clsID, &internalError) || AddComponentInternal (go, clsID, requiredScript, processed, &internalError) == NULL)
+ ErrorString (internalError);
+ }
+ }
+ }
+#endif
+}
+
+
+static bool ValidateScriptComponent (MonoScript* script, std::string* error)
+{
+ // Check if the script is instantiatable
+ if (script == NULL)
+ {
+ if (error)
+ *error = Format ("Can't add script behaviour because the script couldn't be found.");
+ return false;
+ }
+
+ MonoScriptType type = (MonoScriptType)script->GetScriptType();
+
+ if (type == kScriptTypeMonoBehaviourDerived)
+ return true;
+
+
+ // Check if the script is instantiatable
+ if (type == kScriptTypeClassNotFound)
+ {
+ if (error)
+ {
+#if UNITY_EDITOR
+ if (GetMonoManager().HasCompileErrors())
+ *error = Format("Can't add script behaviour %s. You need to fix all compile errors in all scripts first!", script->GetName());
+ else
+#endif
+ *error = Format("Can't add script behaviour %s. The scripts file name does not match the name of the class defined in the script!", script->GetName());
+ }
+
+ return false;
+ }
+ // Check if the script is instantiatable
+ else
+ {
+ if (error)
+ {
+ if (script->IsEditorScript())
+ *error = Format ("Can't add script behaviour %s because it is an editor script. To attach a script it needs to be outside the 'Editor' folder.", script->GetName());
+ else if (type == kScriptTypeNotInitialized)
+ *error = Format("Script %s has not finished compilation yet. Please wait until compilation of the script has finished and try again.", script->GetName());
+ else if (type == kScriptTypeClassIsAbstract)
+ *error = Format("Can't add script behaviour %s. The script class can't be abstract!", script->GetName());
+ else if (type == kScriptTypeClassIsInterface)
+ *error = Format("Can't add script behaviour %s. The script can't be an interface!", script->GetName());
+ else if (type == kScriptTypeClassIsGeneric)
+ *error = Format("Can't add script behaviour %s. Generic MonoBehaviours are not supported!", script->GetName());
+ else
+ *error = Format("Can't add script behaviour %s. The script needs to derive from MonoBehaviour!", script->GetName());
+ }
+ return false;
+ }
+}
+
+bool CanAddComponent (Unity::GameObject& go, int classID)
+{
+ if (go.CountDerivedComponents (classID) && !DoesComponentAllowMultipleInclusion (classID))
+ return false;
+ if (go.HasConflictingComponents(classID))
+ return false;
+ return true;
+}
+
+Unity::Component* AddComponent (GameObject& go, int classID, MonoScriptPtr script, string* error)
+{
+ std::set<ScriptingClassPtr> processed;
+ return AddComponentInternal (go, classID, script, processed, error);
+}
+
+Unity::Component* AddComponentInternal (GameObject& go, int classID, MonoScriptPtr script, std::set<ScriptingClassPtr> &processed, std::string* error)
+{
+ // Are we actually derived from go component?
+ if (!Object::IsDerivedFromClassID (classID, ClassID (Component)))
+ {
+ if (error)
+ *error = Format ("Can't add component because '%s' is not derived from Component.", Object::ClassIDToString (classID).c_str ());
+ return NULL;
+ }
+
+ // We can't add a component if it has conflicting components. This is included when checking if we can add a component but we want to give a specific reason here i.e. a component conflict.
+ Unity::Component* conflictingComponent = go.FindConflictingComponentPtr (classID);
+ if (conflictingComponent)
+ {
+ if (error)
+ *error = Format ("Can't add component '%s' to %s because it conflicts with the existing '%s' derived component!",
+ Object::ClassIDToString (classID).c_str (), go.GetName (), Object::ClassIDToString (conflictingComponent->GetClassID ()).c_str ());
+ return NULL;
+ }
+
+ // We can't add a component if we are already inserted and the component doesn't allow multiple insertion!
+ if (!CanAddComponent (go, classID))
+ {
+ if (error)
+ *error = Format ("Can't add component '%s' to %s because such a component is already added to the game object!", Object::ClassIDToString (classID).c_str (), go.GetName ());
+ return NULL;
+ }
+
+ // Don't allow adding a component to an imported model
+ // @TODO: go.TestHideFlag(Object::kNotEditable) && go.IsPersistent()
+ // -> Is weird. We should cleanup the hide flags to be sensible design
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_5_a1) && go.TestHideFlag(Object::kNotEditable) && go.IsPersistent())
+ {
+ if (error)
+ *error = Format ("Can't add component '%s' to %s because the game object is a generated prefab and can only be modified through an AssetPostprocessor.", Object::ClassIDToString (classID).c_str (), go.GetName ());
+ return NULL;
+ }
+
+ // Find all required components and add them before!
+ const vector_set<int>& requiredCom = FindRequiredComponentsForComponent (classID);
+ vector_set<int>::const_iterator i;
+ for (i=requiredCom.begin ();i!=requiredCom.end ();i++)
+ {
+ if (go.CountDerivedComponents (*i))
+ continue;
+
+ //TODO: check
+ string internalError;
+ if (!CheckForAbstractClass(go, *i, &internalError) || AddComponentInternal (go, *i, NULL, processed, &internalError) == NULL)
+ {
+ ErrorString (internalError);
+ return NULL;
+ }
+ }
+
+ // Find all required components for a script and add them before!
+ if (classID == ClassID (MonoBehaviour))
+ {
+ #if ENABLE_SCRIPTING
+
+ if (!ValidateScriptComponent (script, error))
+ return NULL;
+
+ Assert(script->GetClass () != SCRIPTING_NULL);
+
+ processed.insert(script->GetClass());
+ AddRequiredScriptComponents (go, script, processed);
+
+ #else
+ if( error )
+ *error = Format ("Can't add script behaviour because the Mono's disabled.");
+ return NULL;
+ #endif // ENABLE_SCRIPTING
+ }
+
+ // this means user is trying to add abstract class directly (AddComponent("Collider"))
+ if (Object::ClassIDToRTTI(classID)->isAbstract)
+ {
+ *error = Format ("Cannot add component of type '%s' because it is abstract. Add component of type that is derived from '%s' instead.", Object::ClassIDToString (classID).c_str (), Object::ClassIDToString (classID).c_str ());
+ return NULL;
+ }
+
+ return AddComponentUnchecked (go, classID, script, error);
+}
+
+
+
+Unity::Component* AddComponent (GameObject& go, const char* name, string* error)
+{
+ if (BeginsWith(name, "UnityEngine."))
+ name += strlen("UnityEngine.");
+
+ int classID = Object::StringToClassID (name);
+ if (classID != -1 && Object::IsDerivedFromClassID(classID, ClassID(Component)))
+ {
+ return AddComponent (go, classID, NULL, error);
+ }
+#if ENABLE_SCRIPTING
+ else
+ {
+ MonoScriptPtr script = GetMonoScriptManager().FindRuntimeScript (name);
+
+ if (script)
+ return AddComponent (go, ClassID (MonoBehaviour), script, error);
+
+ if (error)
+ {
+ if (classID == -1)
+ *error = Format ("Can't add component because class '%s' doesn't exist!", name);
+ else
+ *error = Format("Can't add component because '%s' is not derived from Component.", name);
+ }
+
+ return NULL;
+ }
+#else
+ return NULL;
+#endif
+}
+
+// varargs can only be passed around by passing the va_list, so caller is responsible for calling va_start/va_end
+void AddComponentsFromVAList (GameObject& go, const char* componentName, va_list componentList)
+{
+ if (componentName == NULL)
+ return;
+
+ string error;
+ if (AddComponent (go, componentName, &error) == NULL)
+ ErrorString (error);
+
+ while (true)
+ {
+ const char* cur = va_arg (componentList, const char*);
+ if (cur == NULL)
+ break;
+ if (AddComponent (go, cur, &error) == NULL)
+ ErrorString (error);
+ }
+}
+
+void AddComponents (GameObject& go, const char* componentName, ...)
+{
+ va_list ap;
+ va_start (ap, componentName);
+ AddComponentsFromVAList (go, componentName, ap);
+ va_end (ap);
+}
+
+void ActivateGameObject (GameObject& go, const string& name)
+{
+ go.Reset ();
+ go.SetName (name.c_str ());
+ go.AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad);
+ go.Activate ();
+}
+
+void SetNameAndResetGameObject (GameObject& go, const string& name)
+{
+ go.Reset ();
+ go.SetName (name.c_str ());
+ go.AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad);
+}
+
+GameObject& CreateGameObject (const string& name, const char* componentName, ...)
+{
+ // Create game object with name!
+ GameObject &go = *NEW_OBJECT (GameObject);
+
+ ActivateGameObject (go, name);
+
+ // Add components with class names!
+ va_list ap;
+ va_start (ap, componentName);
+ AddComponentsFromVAList (go, componentName, ap);
+ va_end (ap);
+
+ return go;
+}
+
+
+Unity::GameObject& CreateGameObjectWithVAList (const std::string& name, const char* componentName, va_list list)
+{
+ va_list componentList;
+ va_copy (componentList, list);
+ GameObject &go = *NEW_OBJECT (GameObject);
+
+ ActivateGameObject (go, name);
+ AddComponentsFromVAList (go, componentName, componentList);
+ va_end (componentList);
+
+ return go;
+}
+
+GameObject& CreateGameObjectWithHideFlags (const string& name, bool isActive, int flags, const char* componentName, ...)
+{
+ // Create game object with name!
+ GameObject &go = *NEW_OBJECT (GameObject);
+
+ // HideFlags need to be set before object activation because of a bug where
+ // Unity will not immediately update visible root game object list when
+ // changing hide flags after creation Case: 382530
+ go.SetHideFlags (flags);
+
+ if (isActive)
+ ActivateGameObject (go, name);
+ else
+ SetNameAndResetGameObject (go, name);
+
+ // Add components with class names!
+ va_list ap;
+ va_start (ap, componentName);
+ AddComponentsFromVAList(go, componentName, ap);
+ va_end (ap);
+
+ return go;
+}
+
+inline string GetComponentOrScriptName (Unity::Component& com)
+{
+#if ENABLE_SCRIPTING
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&com);
+ if (monoBehaviour)
+ {
+ MonoScript* script = monoBehaviour->GetScript();
+ if (script)
+ return Append (script->GetName(), " (Script)");
+ }
+#endif
+ return com.GetClassName ();
+}
+
+bool CanRemoveComponent(Unity::Component& component, std::string* error)
+{
+ return CanReplaceComponent(component, -1, error);
+}
+
+bool CanReplaceComponent(Unity::Component& component, int replacementClassID, std::string* error)
+{
+ GameObject* go = component.GetGameObjectPtr ();
+ if (go == NULL)
+ return false;
+
+ int componentIndex = go->GetComponentIndex(&component);
+ if (componentIndex == -1)
+ return false;
+
+ // Starting with Unity 3.2, a transform component has to be attached to every game object!
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1))
+ {
+ if (component.GetClassID () == ClassID (Transform))
+ {
+ if (error)
+ {
+ const char* goName = go->GetName ();
+ const char* message = "Can't destroy Transform component of '%s'. "
+ "If you want to destroy the game object, please call 'Destroy' on the game object instead. "
+ "Destroying the transform component is not allowed.";
+
+ *error = Format (message, goName);
+ }
+
+ return false;
+ }
+ }
+
+ int componentClassID = component.GetClassID ();
+ ScriptingClassPtr removeKlass = SCRIPTING_NULL;
+ MonoBehaviour* removeMonoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&component);
+ if (removeMonoBehaviour)
+ removeKlass = removeMonoBehaviour->GetClass();
+
+ int countSameComponentType = 0;
+ bool mayRemove = true;
+ for (int i=0;i<go->GetComponentCount ();i++)
+ {
+ int curClassID = go->GetComponentClassIDAtIndex (i);
+ const vector_set<int>& requiredCom = FindRequiredComponentsForComponent (go->GetComponentClassIDAtIndex (i));
+
+ // if the component we are releasing is needed by another component
+ if (requiredCom.count (componentClassID))
+ {
+ if (error)
+ {
+ if (!mayRemove)
+ *error += ", ";
+ *error += Object::ClassIDToString (curClassID);
+ mayRemove = false;
+ }
+ }
+
+ // Check component requirement for scripts.
+ // - Fetch all component requirement attributes
+ // - Check if the class we remove is derived from the requirement
+ if (curClassID == ClassID (MonoBehaviour))
+ {
+ MonoBehaviour* behaviour = (MonoBehaviour*)&go->GetComponentAtIndex(i);
+ ScriptingClassPtr scriptClass = behaviour->GetClass();
+ if (scriptClass)
+ {
+#if ENABLE_MONO
+
+ MonoArray* array = RequiredComponentsOf(behaviour);
+
+ if (array)
+ {
+ for (int j=0;j<mono_array_length(array);j++)
+ {
+ MonoObject* requiredClassObject = GetMonoArrayElement<MonoObject*>(array, j);
+ if (requiredClassObject == NULL)
+ continue;
+ MonoClass* requiredClass = GetScriptingTypeRegistry().GetType(requiredClassObject);
+ if (IsComponentSubclassOfMonoClass (component, requiredClass))
+ {
+ // Find if the new replacement meets the requirements
+ bool replacementMeetRequirements = false;
+ if (replacementClassID != -1)
+ {
+ MonoClass* replacementClass = GetMonoManager().ClassIDToScriptingClass(replacementClassID);
+ // This check doesn't work with MonoBehaviour type system.
+ // But currently component replacement only happens for Collider family.
+ // Assert it is a subclass of Collider
+ Assert(scripting_class_is_subclass_of(replacementClass, GetMonoManager().ClassIDToScriptingClass(ClassID(Collider))));
+ if (replacementClass == requiredClass
+ || scripting_class_is_subclass_of(replacementClass, requiredClass))
+ {
+ replacementMeetRequirements = true;
+ }
+ }
+
+ // Find if other components of the same GO meets the requirements
+ bool otherComponentMeetRequirements = false;
+ for (int k = 0; k < go->GetComponentCount(); ++k)
+ {
+ Unity::Component& otherComponent = go->GetComponentAtIndex(k);
+ if (&otherComponent == &component // not the component being removed...
+ || &otherComponent == &go->GetComponentAtIndex(i)) // not the component asking requirements...
+ {
+ continue;
+ }
+ if (IsComponentSubclassOfMonoClass(otherComponent, requiredClass))
+ {
+ otherComponentMeetRequirements = true;
+ break;
+ }
+ }
+
+ if (!replacementMeetRequirements && !otherComponentMeetRequirements)
+ {
+ if (error)
+ {
+ if (!mayRemove)
+ *error += ", ";
+ *error += mono_class_get_name(scriptClass);
+ *error += " (Script)";
+ mayRemove = false;
+ }
+ }
+ }
+ }
+ }
+#endif // ENABLE_MONO
+
+ if (removeKlass == scriptClass)
+ countSameComponentType ++;
+ }
+ }
+ else
+ {
+ if (curClassID == componentClassID)
+ countSameComponentType ++;
+ }
+ }
+
+ if (mayRemove || countSameComponentType > 1)
+ {
+ if (error)
+ *error = "";
+ return true;
+ }
+ else
+ {
+ if (error)
+ *error = Format ("Can't remove %s because %s depends on it", GetComponentOrScriptName (component).c_str (), error->c_str ());
+ return false;
+ }
+}
+
+#if UNITY_EDITOR
+
+int GetMonoBehaviourEngineTypeTreeVariableCount ()
+{
+ static int gCount = -1;
+ if (gCount == -1)
+ {
+ MonoBehaviour* temp = NEW_OBJECT(MonoBehaviour);
+ temp->HackSetResetWasCalled();
+ temp->HackSetAwakeWasCalled();
+
+ TypeTree tree;
+ if (temp)
+ {
+ GenerateTypeTree(*temp, &tree);
+ DestroySingleObject(temp);
+ }
+ gCount = CountTypeTreeVariables(tree);
+ }
+
+ return gCount;
+}
+
+struct DisableMonoBehaviourPPtrSerialize
+{
+ dynamic_bitset override;
+ bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition)
+ {
+ if (IsTypeTreePPtr (typeTree))
+ override[typeTree.m_Index] = false;
+ else if (IsTypeTreePPtrArray (typeTree))
+ override[typeTree.m_Father->m_Index] = false;
+ else if (typeTree.m_Index < GetMonoBehaviourEngineTypeTreeVariableCount())
+ override[typeTree.m_Index] = false;
+
+ return true;
+ }
+};
+
+// Resets all non-pptr values.
+// PPtr values that are already set to a value stay, otherwise they are set to the value of the default properties
+// Does not call AwakeFromLoad so you need to do that yourself
+static void ResetMonoBehaviourToScriptDefaults (MonoBehaviour& behaviour)
+{
+ MonoScript* script = behaviour.GetScript();
+ if (script == NULL)
+ return;
+
+ if (script->GetScriptType() != kScriptTypeMonoBehaviourDerived && script->GetScriptType() != kScriptTypeScriptableObjectDerived && script->GetScriptType() != kScriptTypeEditorScriptableObjectDerived)
+ return;
+
+ // When there is a script class available
+ if (script->GetClass())
+ {
+ // Create a clone and write out the pristine state
+ MonoBehaviour* clone = NEW_OBJECT (MonoBehaviour);
+ clone->HackSetResetWasCalled();
+ clone->HackSetAwakeWasCalled();
+
+ clone->SetScript(behaviour.GetScript());
+ dynamic_array<UInt8> data(kMemTempAlloc);
+ TypeTree typeTree;
+ WriteObjectToVector (*clone, &data, kSerializeForPrefabSystem);
+ GenerateTypeTree (*clone, &typeTree, kSerializeForPrefabSystem);
+
+ DestroySingleObject (clone);
+
+ // Read back replacing everything except pptrs
+ ReadObjectFromVector (&behaviour, data, typeTree, kSerializeForPrefabSystem);
+ }
+
+ ApplyDefaultReferences(behaviour, script->GetDefaultReferences());
+}
+
+#endif
+
+
+void UnloadGameObjectAndComponents (GameObject& go)
+{
+ LockObjectCreation();
+ Assert(go.IsPersistent());
+ Assert(!go.IsActive());
+
+ for (int i=0;i<go.GetComponentCount();i++)
+ {
+ if (go.GetComponentAtIndexIsLoaded(i))
+ {
+ Unity::Component& com = go.GetComponentAtIndex(i);
+ delete_object_internal (&com);
+ }
+ }
+
+ delete_object_internal (&go);
+ UnlockObjectCreation();
+}
+
+bool UnloadGameObjectHierarchy (GameObject& go)
+{
+ LockObjectCreation();
+ Assert(!go.IsActive());
+ Transform* transform = go.QueryComponent(Transform);
+ // AssertIf(transform && !transform->IsPersistent());
+ for (int i=0;i<transform->GetChildrenCount();i++)
+ {
+ Transform& child = transform->GetChild(i);
+ UnloadGameObjectHierarchy (child.GetGameObject());
+ }
+
+ ///@TODO: This should probably be removed and fixed in the specific components that are misbehaving.
+
+ // Run this once to make sure all components are actually loaded.
+ // Otherwise, components of incompletely loaded GameObjects may be loaded in the process,
+ // and when the reference other, previous Components of the same GO, those will be loaded as well
+ // even though they should be unloaded already, causing crashes later on.
+ for (int i=0;i<go.GetComponentCount();i++)
+ go.GetComponentAtIndex(i);
+
+ for (int i=0;i<go.GetComponentCount();i++)
+ {
+ Unity::Component& com = go.GetComponentAtIndex(i);
+ delete_object_internal (&com);
+ }
+
+ delete_object_internal (&go);
+
+ UnlockObjectCreation();
+ return true;
+}
+
+void SendMessageToEveryone(MessageIdentifier message, MessageData msgData)
+{
+ // First, collect all GameObjects
+ vector<GameObject*> gameObjects;
+ Object::FindObjectsOfType (&gameObjects);
+
+ // Next, keep a list of all instance IDs, since it is possible for any GameObject
+ // to be destroyed when a message is handled.
+ set<SInt32> ids;
+ for (int i=0;i<gameObjects.size ();i++)
+ {
+
+ GameObject* go = gameObjects[i];
+ if (go->IsActive ())
+ ids.insert(go->GetInstanceID());
+ }
+
+ // Send all active gameobjects a message
+ for (set<SInt32>::iterator i=ids.begin ();i != ids.end ();i++)
+ {
+ GameObject* go = static_cast<GameObject*>(Object::IDToPointer(*i));
+ if (go && go->IsActive())
+ go->SendMessageAny (message, msgData);
+ }
+}
+
+GameObject* FindGameObjectWithTag (UInt32 tag)
+{
+ GameObjectList& tagged = GetGameObjectManager().m_TaggedNodes;
+ for (GameObjectList::iterator i=tagged.begin();i != tagged.end();i++)
+ {
+ GameObject& go = **i;
+ Assert(go.IsActive() && go.GetTag() != 0);
+ if (go.GetTag() == tag)
+ return & go;
+ }
+ return NULL;
+}
+
+void FindGameObjectsWithTag (UInt32 tag, std::vector<Unity::GameObject*>& gos)
+{
+ GameObjectList& tagged = GetGameObjectManager().m_TaggedNodes;
+ for (GameObjectList::iterator i=tagged.begin();i != tagged.end();i++)
+ {
+ GameObject& go = **i;
+ Assert(go.IsActive() && go.GetTag() != 0);
+
+ if (go.GetTag() == tag)
+ gos.push_back(&go);
+ }
+}
+
+Camera* FindMainCamera ()
+{
+ std::vector<GameObject*> gos;
+ FindGameObjectsWithTag(kMainCameraTag, gos);
+ for (int i=0;i<gos.size();i++)
+ {
+ GameObject* go = gos[i];
+ Camera* cam = go->QueryComponent(Camera);
+ if (cam != NULL && cam->GetEnabled())
+ return cam;
+ }
+ return NULL;
+}
+
+void SmartResetObject (Object& object)
+{
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&object);
+ if (behaviour)
+ {
+ #if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ ResetMonoBehaviourToScriptDefaults(*behaviour);
+ #endif
+
+ behaviour->Reset();
+ behaviour->SmartReset();
+ behaviour->AwakeFromLoad(kDefaultAwakeFromLoad);
+ behaviour->SetDirty();
+ }
+ else
+ {
+ object.Reset();
+ object.SmartReset();
+ object.AwakeFromLoad(kDefaultAwakeFromLoad);
+ object.SetDirty();
+ }
+}
+
+Unity::Component* GetComponentWithScript (GameObject& go, int classID, MonoScriptPtr script)
+{
+
+ if (classID != ClassID(MonoBehaviour))
+ return go.QueryComponentT<Unity::Component>(classID);
+#if ENABLE_SCRIPTING
+ if (script == NULL)
+ return NULL;
+
+ ScriptingClassPtr compareKlass = script->GetClass();
+ if (compareKlass == NULL)
+ return NULL;
+
+ int count = go.GetComponentCount ();
+ for (int i=0;i<count;i++)
+ {
+ // We are looking only for MonoBehaviours
+ int clsID = go.GetComponentClassIDAtIndex (i);
+ if (!Object::IsDerivedFromClassID (clsID, ClassID (MonoBehaviour)))
+ continue;
+
+ MonoBehaviour& behaviour = static_cast<MonoBehaviour&> (go.GetComponentAtIndex (i));
+ ScriptingObjectPtr object = behaviour.GetInstance ();
+ if (object)
+ {
+ ScriptingClassPtr klass = scripting_object_get_class(object, GetScriptingTypeRegistry());
+ if (scripting_class_is_subclass_of (klass, compareKlass))
+ return &behaviour;
+ }
+ }
+#endif
+ return NULL;
+}
+
+void GetComponentsInChildren (const GameObject& gameObject, bool includeInactive, int classID, dynamic_array<Unity::Component*>& outComponents)
+{
+ // Find components on this game object
+ if (includeInactive || gameObject.IsActive())
+ {
+ for (int i=0;i<gameObject.GetComponentCount();i++)
+ {
+ if (Object::IsDerivedFromClassID(gameObject.GetComponentClassIDAtIndex(i), classID))
+ outComponents.push_back(&gameObject.GetComponentAtIndex(i));
+ }
+ }
+
+ // Recurse children
+ Transform* transform = gameObject.QueryComponent(Transform);
+ if (transform != NULL)
+ {
+ for (Transform::iterator i=transform->begin();i != transform->end();++i)
+ {
+ GameObject& child = (**i).GetGameObject ();
+ GetComponentsInChildren(child, includeInactive, classID, outComponents);
+ }
+ }
+}
+
+static void AddToBatchDeleteAndMakeUnpersistent (Object& object, BatchDelete& batchDelete)
+{
+ if (object.IsPersistent())
+ GetPersistentManager ().MakeObjectUnpersistent (object.GetInstanceID (), kDestroyFromFile);
+
+ batchDelete.objects[batchDelete.objectCount++] = &object;
+}
+
+static void DestroyGameObjectRecursive (GameObject& gameObject, BatchDelete& batchDelete)
+{
+ Assert(!gameObject.IsActive());
+ Assert(gameObject.IsDestroying());
+
+ Transform* transform = gameObject.QueryComponent (Transform);
+ if (transform)
+ {
+ for (Transform::iterator i=transform->begin();i!=transform->end();i++)
+ {
+ Transform& transform = **i;
+ DestroyGameObjectRecursive(*transform.GetGameObjectPtr(), batchDelete);
+ }
+ }
+
+ if (gameObject.IsActivating())
+ {
+ if (transform)
+ transform->RemoveFromParent();
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+
+ for (int i=0;i<gameObject.GetComponentCount();i++)
+ {
+ Unity::Component& com = gameObject.GetComponentAtIndex(i);
+ AddToBatchDeleteAndMakeUnpersistent (com, batchDelete);
+ }
+
+ AddToBatchDeleteAndMakeUnpersistent (gameObject, batchDelete);
+}
+
+static void PreDestroyRecursive (GameObject& gameObject, size_t* destroyedObjectCount)
+{
+ if (gameObject.IsActivating())
+ {
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+
+
+ // the callback is only called if the GameObject is
+ // really destroyed (not only removed from memory)
+ GameObject::InvokeDestroyedCallback(&gameObject);
+
+ if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ gameObject.Deactivate(kWillDestroyGameObjectDeactivate);
+
+ gameObject.WillDestroyGameObject();
+ *destroyedObjectCount += 1 + gameObject.GetComponentCount();
+
+ Transform* transform = gameObject.QueryComponent (Transform);
+ if (transform)
+ {
+ for (Transform::iterator i=transform->begin();i!=transform->end();i++)
+ {
+ Transform& child = **i;
+ PreDestroyRecursive(child.GetGameObject(), destroyedObjectCount);
+ }
+ }
+}
+
+#if UNITY_EDITOR
+static void DisconnectPrefabInstanceRecursive (GameObject& gameObject)
+{
+ PrefabDestroyObjectCallback(gameObject);
+
+ Transform* transform = gameObject.QueryComponent (Transform);
+ if (transform == NULL)
+ return;
+
+ for (Transform::iterator i=transform->begin();i!=transform->end();i++)
+ {
+ Transform& child = **i;
+ DisconnectPrefabInstanceRecursive(child.GetGameObject());
+ }
+}
+#endif
+
+void DestroyTransformComponentAndChildHierarchy (Transform& transform)
+{
+ size_t objectCount = 0;
+ for (Transform::iterator i=transform.begin();i!=transform.end();i++)
+ {
+ Transform& child = **i;
+ child.GetGameObject().Deactivate(kWillDestroyGameObjectDeactivate);
+ PreDestroyRecursive(child.GetGameObject(), &objectCount);
+ }
+
+ // Remove transform from transform hierarchy
+ transform.RemoveFromParent();
+
+ BatchDelete batchDelete = CreateBatchDelete (objectCount);
+
+ for (Transform::iterator i=transform.begin();i!=transform.end();i++)
+ {
+ Transform& child = **i;
+ DestroyGameObjectRecursive(child.GetGameObject(), batchDelete);
+ }
+
+ CommitBatchDelete (batchDelete);
+}
+
+void DestroyGameObjectHierarchy (GameObject& gameObject)
+{
+ if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1))
+ {
+ // Deactivate and mark is being destroyed recursively
+ // Send all necessary callbacks etc.
+ gameObject.Deactivate(kWillDestroyGameObjectDeactivate);
+ }
+
+ size_t objectCount = 0;
+ PreDestroyRecursive(gameObject, &objectCount);
+
+ // Remove transform from transform hierarchy
+ Transform* transform = gameObject.QueryComponent(Transform);
+ if (transform)
+ transform->RemoveFromParent();
+
+ BatchDelete batchDelete = CreateBatchDelete (objectCount);
+
+ // Destroy the objects (There should be no callbacks happening at this stage anymore)
+ DestroyGameObjectRecursive(gameObject, batchDelete);
+
+ CommitBatchDelete (batchDelete);
+}
+
+void DestroyObjectHighLevel (Object* object, bool forceDestroy)
+{
+ PROFILER_AUTO (gDestroyProfile, NULL)
+
+ if (object)
+ {
+ if (object->IsDerivedFrom (ClassID (Component)))
+ {
+ Unity::Component& component = *static_cast<Unity::Component*> (object);
+ MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (object);
+
+ // MonoBehaviour needs per
+ if (monoBehaviour && monoBehaviour->IsDestroying())
+ {
+ ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.");
+ return;
+ }
+
+
+ GameObject* gameObject = component.GetGameObjectPtr();
+ if (gameObject)
+ {
+ if (GetDisableImmediateDestruction())
+ {
+ ErrorStringObject ("Destroying components immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.", object);
+ return;
+ }
+
+ if (gameObject->IsDestroying())
+ {
+ ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.");
+ return;
+ }
+
+
+ if (gameObject->IsActivating())
+ {
+ ErrorStringObject("Cannot destroy Component while GameObject is being activated or deactivated.", gameObject);
+ return;
+ }
+
+ string error;
+ if (!forceDestroy && !CanRemoveComponent(component, &error))
+ {
+ ErrorStringObject (error, &component);
+ return;
+ }
+
+ PPtr<Unity::Component> componentPPtr = &component;
+
+ if (gameObject->IsActive ())
+ {
+ component.Deactivate (kWillDestroySingleComponentDeactivate);
+
+ // The game object might get destroyed during the OnDisable / OnDestroy callbacks, so don't rely on it.
+ if ((Unity::Component*)componentPPtr != &component)
+ return;
+ }
+
+ // Deleting a transform component is a special case, we have to destroy
+ // the entire child transform hierarchy.
+ // This is necessary to keep behaviour consistent for pre 3.2 content
+ // Starting with 3.2 we prevent this in CanRemoveComponent
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1) && component.GetClassID() == ClassID (Transform))
+ {
+ DestroyTransformComponentAndChildHierarchy (static_cast<Transform&> (component));
+
+ if ((Unity::Component*)componentPPtr != &component)
+ return;
+ }
+
+ component.WillDestroyComponent();
+
+ // The game object might get destroyed during the OnDisable / OnDestroy callbacks, so don't rely on it.
+ if ((Unity::Component*)componentPPtr != &component)
+ return;
+
+ int componentIndex = gameObject->GetComponentIndex(&component);
+ if (componentIndex != -1)
+ component.GetGameObject().RemoveComponentAtIndex (componentIndex);
+ else
+ {
+ ErrorString("Component Removing internal failure");
+ }
+ }
+ else
+ {
+ component.WillDestroyComponent();
+ }
+
+ #if UNITY_EDITOR
+ PrefabDestroyObjectCallback(component);
+ #endif
+
+ DestroySingleObject (&component);
+ }
+ else if (object->IsDerivedFrom (ClassID (GameObject)))
+ {
+ if (GetDisableImmediateDestruction())
+ {
+ ErrorStringObject ("Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.", object);
+ return;
+ }
+
+ GameObject& gameObject = *static_cast<GameObject*>(object);
+ if (gameObject.IsDestroying())
+ {
+ ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy.");
+ return;
+ }
+
+ if (gameObject.IsActivating())
+ {
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+
+ Transform* parent = gameObject.QueryComponent(Transform);
+ if (parent)
+ {
+ parent = parent->GetParent();
+ if (parent && parent->GetGameObject().IsActivating())
+ {
+ ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject);
+ return;
+ }
+ }
+ #if UNITY_EDITOR
+ DisconnectPrefabInstanceRecursive (gameObject);
+ #endif
+
+ DestroyGameObjectHierarchy(gameObject);
+ }
+ else if (object->IsDerivedFrom (ClassID(AssetBundle)))
+ {
+ ErrorStringObject ("Destroying AssetBundle directly is not permitted.\nUse AssetBundle.UnloadBundle to destroy an asset bundle.", object);
+ return;
+ }
+ else
+ {
+ DestroySingleObject(object);
+ }
+ }
+}
+
+std::string UnityObjectToString (Object *object)
+{
+ #if ENABLE_SCRIPTING
+ std::string type;
+
+ if (object == NULL) return "null";
+
+ if (object->GetClassID() == ClassID(MonoBehaviour))
+ type = dynamic_pptr_cast<MonoBehaviour*>(object)->GetScriptFullClassName();
+ else
+ type = "UnityEngine."+object->GetClassName();
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1))
+ return Format("%s (%s)", object->GetName(), type.c_str());
+ else
+ return type;
+ #else
+ return "UnityObject";
+ #endif
+}
+
+Unity::Component* FindAncestorComponentExactTypeImpl (Unity::GameObject& gameObject, int classId)
+{
+ Transform* parent = gameObject.QueryComponent (Transform);
+ while (parent != NULL)
+ {
+ Unity::Component* component = parent->GetGameObject().QueryComponentExactTypeImplementation (classId);
+ if (component != NULL)
+ return component;
+
+ parent = parent->GetParent ();
+ }
+ return NULL;
+}
+
+Unity::Component* FindAncestorComponentImpl (Unity::GameObject& gameObject, int classId)
+{
+ Transform* parent = gameObject.QueryComponent (Transform);
+ while (parent != NULL)
+ {
+ Unity::Component* component = parent->GetGameObject().QueryComponentImplementation (classId);
+ if (component != NULL)
+ return component;
+
+ parent = parent->GetParent ();
+ }
+ return NULL;
+}
+
+#if ENABLE_SCRIPTING
+int ExtractTagThrowing(ICallString& name)
+{
+ string tagString = name;
+ int tag = StringToTag (tagString);
+ if (tag != kUndefinedTag)
+ return tag;
+ else
+ {
+ Scripting::RaiseMonoException ("Tag: %s is not defined!", tagString.c_str());
+ return tag;
+ }
+}
+#endif
diff --git a/Runtime/Misc/GameObjectUtility.h b/Runtime/Misc/GameObjectUtility.h
new file mode 100644
index 0000000..91ed672
--- /dev/null
+++ b/Runtime/Misc/GameObjectUtility.h
@@ -0,0 +1,91 @@
+#ifndef GAMEOBJECTUTILITY_H
+#define GAMEOBJECTUTILITY_H
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Mono/MonoScript.h"
+
+struct ICallString;
+
+class MonoBehaviour;
+class Camera;
+
+/// Adds a component by classID or className to the game object.
+/// This method does several checks and returns null if any of them fail.
+/// - Class has to be derived from Component
+/// - Class has to be not already added to the game object or be allowed to be added multiple times (ComponentRequirement.cpp)
+/// On failure this method returns NULL and if error != null an error string.
+/// This method automatically orders filters by their sort priority.
+/// * Default properties are only setup in edit mode
+Unity::Component* AddComponentInternal (GameObject& go, int classID, MonoScriptPtr script, std::set<ScriptingClassPtr> &processed, std::string* error = NULL);
+Unity::Component* AddComponent (GameObject& go, int classID, MonoScriptPtr script, std::string* error = NULL);
+Unity::Component* AddComponent (GameObject& go, const char* className, std::string* error = NULL);
+Unity::Component* AddComponentUnchecked (GameObject& go, int classID, MonoScriptPtr script, std::string* error);
+
+/// Creates a game object with name. Add's a null terminated list of components by className.
+/// Errors when a component can't be added!
+Unity::GameObject& CreateGameObject (const std::string& name, const char* componentName, ...);
+Unity::GameObject& CreateGameObjectWithVAList (const std::string& name, const char* componentName, va_list componentList);
+Unity::GameObject& CreateGameObjectWithHideFlags (const string& name, bool isActive, int flags, const char* componentName, ...);
+
+/// Adds a null terminated list of components by className.
+/// Errors when a component can't be added!
+void AddComponents (Unity::GameObject& go, const char* componentName, ...);
+
+/// Checks if a component can be removed from its game object.
+/// Does error checking so that
+/// we don't remove a component which is required by another component!
+bool CanRemoveComponent(Unity::Component& component, std::string* error);
+/// See whether a component can be replaced by another
+/// Note: currently it only specifically handles requirements from RequireComponent attributes
+bool CanReplaceComponent(Unity::Component& component, int replacementClassID, std::string* error);
+
+bool CanAddComponent (Unity::GameObject& go, int classID);
+
+EXPORT_COREMODULE void DestroyObjectHighLevel (Object* object, bool forceDestroy = false);
+void DestroyTransformComponentAndChildHierarchy (Transform& transform);
+
+/// On return all GameObject's with the specified tag are added to the array.
+/// Only active game objects are returned
+void FindGameObjectsWithTag (UInt32 tag, std::vector<Unity::GameObject*>& gos);
+Camera* FindMainCamera ();
+
+/// Returns the first game object with the specified tag found!
+/// Only active game objects are returned
+Unity::GameObject* FindGameObjectWithTag (UInt32 tag);
+
+///
+Unity::Component* FindAncestorComponentExactTypeImpl (Unity::GameObject& gameObject, int classId);
+Unity::Component* FindAncestorComponentImpl (Unity::GameObject& gameObject, int classId);
+
+template<class T>
+T* FindAncestorComponent (Unity::GameObject& gameObject)
+{
+ if (T::IsSealedClass())
+ return static_cast<T*> (FindAncestorComponentExactTypeImpl (gameObject, T::GetClassIDStatic()));
+ else
+ return static_cast<T*> (FindAncestorComponentImpl (gameObject, T::GetClassIDStatic()));
+}
+
+/// Sends the message to all active game objects
+void SendMessageToEveryone(MessageIdentifier message, MessageData msgData);
+bool UnloadGameObjectHierarchy (Unity::GameObject& go);
+void UnloadGameObjectAndComponents (Unity::GameObject& go);
+
+EXPORT_COREMODULE void SmartResetObject (Object& com);
+
+Unity::Component* GetComponentWithScript (Unity::GameObject& go, int classID, MonoScriptPtr script);
+
+std::string UnityObjectToString (Object *object);
+
+/// Returns all components in this and any child game objects.
+/// If includeInactive, in active components will be returned, otherwise only active components will be returned.
+void GetComponentsInChildren (const GameObject& gameObject, bool includeInactive, int classID, dynamic_array<Unity::Component*>& outComponents);
+
+
+typedef void AddComponentCallbackFunction (Unity::Component& com);
+void RegisterAddComponentCallback (AddComponentCallbackFunction* callback);
+
+int ExtractTagThrowing (ICallString& name);
+
+#endif
diff --git a/Runtime/Misc/GameObjectUtilityTests.cpp b/Runtime/Misc/GameObjectUtilityTests.cpp
new file mode 100644
index 0000000..a8f3e3c
--- /dev/null
+++ b/Runtime/Misc/GameObjectUtilityTests.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_UNIT_TESTS
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Testing/Testing.h"
+
+class GameObjectFixture
+{
+protected:
+ GameObject* NewGameObject()
+ {
+ return NEW_OBJECT_RESET_AND_AWAKE(GameObject);
+ }
+};
+
+SUITE (GameObjectUtilityTests)
+{
+ TEST (CreateGameObjectTest)
+ {
+ const char* name = "TestGameObject";
+ GameObject& go = CreateGameObject (name, "Transform", "MeshRenderer", NULL);
+
+ CHECK_EQUAL (go.GetName(), name);
+ CHECK_EQUAL (go.GetComponentCount(), 2);
+ CHECK (go.IsActive());
+
+ DestroyObjectHighLevel(&go);
+ }
+
+ TEST (CreateGameObjectWithFlagsTest)
+ {
+ int flags = 2;
+ GameObject& go = CreateGameObjectWithHideFlags ("TestGameObject", true, flags, NULL);
+
+ CHECK (go.IsActive());
+ CHECK_EQUAL (go.GetHideFlags(), flags);
+
+ DestroyObjectHighLevel(&go);
+
+ GameObject& go1 = CreateGameObjectWithHideFlags ("TestGameObject", false, flags, NULL);
+
+ CHECK (!go1.IsActive());
+ CHECK_EQUAL (go1.GetHideFlags(), flags);
+
+ DestroyObjectHighLevel(&go1);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, AddComponentsTest)
+ {
+ GameObject* go = NewGameObject();
+
+ AddComponent(*go, "Transform", NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 1);
+
+ AddComponent(*go, ClassID(MeshRenderer), NULL, NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 2);
+
+ // Transform and MeshRenderer don't support multiple inclusion.
+ EXPECT (Error, "Can't add component 'Transform'");
+ EXPECT (Error, "Can't add component 'MeshRenderer'");
+ AddComponents(*go, "Transform", "MeshRenderer", "Skybox", NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 3);
+
+#if ENABLE_SPRITES
+ // SpriteRenderer can't be added to a GO with a MeshRenderer - conflicting classes.
+ EXPECT (Error, "Can't add component 'SpriteRenderer'");
+ AddComponents(*go, "SpriteRenderer", NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 3);
+#endif
+
+ // Skybox supports multiple inclusion.
+ AddComponent(*go, ClassID(Skybox), NULL);
+ CHECK_EQUAL (go->GetComponentCount(), 4);
+
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, CanAddorRemoveComponentTest)
+ {
+ GameObject* go = NewGameObject();
+
+ AddComponents(*go, "Transform", "MeshFilter", "Skybox", NULL);
+
+ CHECK (!CanAddComponent(*go, ClassID(Transform)));
+ CHECK (CanAddComponent(*go, ClassID(Skybox)));
+
+ // Refer to the InitComponentRequirements() function for details.
+ CHECK ( !CanRemoveComponent(go->GetComponentT<Transform>(ClassID(Transform)), NULL) );
+ DestroyObjectHighLevel(go);
+ }
+
+ TEST_FIXTURE (GameObjectFixture, FindWithTagTest)
+ {
+ GameObject* go = NewGameObject();
+ int tag = 2;
+
+ CHECK (FindGameObjectWithTag(tag) == NULL);
+
+ go->SetTag(tag);
+ CHECK (FindGameObjectWithTag(tag) == NULL);
+
+ go->Activate();
+ CHECK (FindGameObjectWithTag(tag) != NULL);
+
+ GameObject* go1 = NewGameObject();
+ go1->Activate();
+ go1->SetTag(tag);
+
+ std::vector<Unity::GameObject*> gos;
+ FindGameObjectsWithTag(tag, gos);
+ CHECK_EQUAL(gos.size(), 2);
+
+ DestroyObjectHighLevel(go);
+ }
+}
+
+#endif
diff --git a/Runtime/Misc/GarbageCollectSharedAssets.cpp b/Runtime/Misc/GarbageCollectSharedAssets.cpp
new file mode 100644
index 0000000..9b75d14
--- /dev/null
+++ b/Runtime/Misc/GarbageCollectSharedAssets.cpp
@@ -0,0 +1,1188 @@
+#include "UnityPrefix.h"
+#if !ENABLE_OLD_GARBAGE_COLLECT_SHARED_ASSETS
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Mono/Coroutine.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "GameObjectUtility.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/Dynamics/MeshCollider.h"
+#include "Runtime/Filters/Mesh/LodMeshFilter.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/CollectProfilerStats.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "BatchDeleteObjects.h"
+#include "Runtime/Interfaces/IPhysics.h"
+
+/// All these includes are used by AddManagerRoots
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/AssetPipeline/AssetImporter.h"
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/AssetServer/ASCache.h"
+#include "Editor/Src/EditorBuildSettings.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/EditorSettings.h"
+#include "Editor/Src/EditorUserSettings.h"
+#include "Editor/Src/AssetPipeline/AssetInterface.h"
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/AssetPipeline/AssetPathUtilities.h"
+#include "Editor/Src/Application.h"
+#include "Editor/Src/HierarchyState.h"
+#include "Editor/Src/InspectorExpandedState.h"
+#include "Editor/Src/AnnotationManager.h"
+#include "Editor/Src/EditorExtensionImpl.h"
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Editor/Src/EditorAssetGarbageCollectManager.h"
+#endif
+#include "Runtime/Allocator/MemoryManager.h"
+
+#include "Runtime/Scripting/Scripting.h"
+
+PROFILER_INFORMATION(gGarbageCollectSharedAssetsProfile, "GarbageCollectAssetsProfile", kProfilerLoading)
+PROFILER_INFORMATION(gGCFindLiveObjects, "GC.FindLiveObjects", kProfilerLoading)
+PROFILER_INFORMATION(gGCBuildLiveObjectMaps, "GC.BuildLiveObjectMaps", kProfilerLoading)
+PROFILER_INFORMATION(gGCMarkDependencies, "GC.MarkDependencies", kProfilerLoading)
+PROFILER_INFORMATION(gGCDeletedUnusedAssets, "GC.DeleteUnusedAssets", kProfilerLoading)
+PROFILER_INFORMATION(gGCOnDestroyCallback, "ScriptableObject.OnDestroy", kProfilerLoading)
+
+#if UNITY_EDITOR
+static int gPreventGarbageCollectionOfInstanceID = 0;
+#endif
+
+struct ObjectState
+{
+ Object* object;
+ UInt32 classID : 29;
+ UInt32 marked : 1;
+ UInt32 isPersistent : 1;
+};
+
+
+struct ObjectHashFunctor
+{
+ inline size_t operator()(const Object* x) const
+ {
+ return (size_t)x / 32;
+ }
+};
+
+
+#define ASSET_REMAP_TABLE 1
+#define USE_MONO_LIVENESS ENABLE_MONO
+
+// Cap on how much memory we can use for the dense remap table for assets
+enum { kMaximumAssetRemapTableSize = 250 * 1024 };
+
+struct GarbageCollectorState;
+
+struct GenericSlowGarbageCollector : public GenerateIDFunctor
+{
+ GarbageCollectorState* gcState;
+
+ GenericSlowGarbageCollector () { }
+ virtual ~GenericSlowGarbageCollector () {}
+
+ inline void ProcessReference (SInt32 oldInstanceID);
+
+ // General purpose GarbageCollector callback
+ virtual SInt32 GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlag);
+};
+
+struct GarbageCollectorState
+{
+ typedef pair<const int, int> InstanceIDToIndexPair;
+
+#if ASSET_REMAP_TABLE
+ typedef dense_hash_map<int, int, InstanceIDHashFunctor, std::equal_to<int>, STL_ALLOCATOR( kMemTempAlloc, InstanceIDToIndexPair )> InstanceIDToIndex;
+#else
+ typedef map<int, int> InstanceIDToIndex;
+#endif
+ InstanceIDToIndex instanceIDToIndex;
+ dynamic_array<UInt32> assetRemapTable;
+
+ dynamic_array<ObjectState> liveObjects;
+ dynamic_array<UInt32> needsProcessing;
+
+ int originalObjectCount;
+
+ void* livenessState;
+
+ bool followMonoReferences;
+
+ #if ENABLE_MEM_PROFILER
+ bool alwaysAddToNeedsProcessing;
+ #endif
+
+ RemapPPtrTransfer genericSlowTransfer;
+ GenericSlowGarbageCollector genericCollector;
+
+ GarbageCollectorState ()
+ : assetRemapTable (kMemTempAlloc),
+ liveObjects (kMemTempAlloc),
+ needsProcessing (kMemTempAlloc),
+ genericSlowTransfer (kDontRequireAllMetaFlags | kPerformUnloadDependencyTracking, false)
+ {
+ genericCollector.gcState = this;
+#if ENABLE_MEM_PROFILER
+ alwaysAddToNeedsProcessing = false;
+#endif
+ genericSlowTransfer.SetGenerateIDFunctor (&genericCollector);
+ }
+};
+
+
+///@TODO: Make a function that can let us find all classes that have references
+static bool DoesClassIDHaveReferences (int classID)
+{
+#if UNITY_EDITOR
+
+ bool classHasNoReferences =
+ classID == ClassID(AssetDatabase) ||
+ classID == ClassID(HierarchyState) ||
+ classID == ClassID(GUIDSerializer) ||
+ classID == ClassID(AssetServerCache);
+
+ if(classHasNoReferences)
+ return false;
+
+#endif
+
+ return
+ classID != ClassID (ScriptMapper) &&
+ classID != ClassID (MonoScript) &&
+ classID != ClassID (NetworkManager) &&
+ classID != ClassID (ResourceManager) &&
+ classID != ClassID (PreloadData) &&
+ classID != ClassID (Texture) &&
+ classID != ClassID (Texture2D) &&
+ classID != ClassID (Texture3D) &&
+ classID != ClassID (Cubemap) &&
+ classID != ClassID (WebCamTexture) &&
+ classID != ClassID (RenderTexture) &&
+ classID != ClassID (AssetBundle) &&
+ classID != ClassID (Mesh) &&
+ classID != ClassID (TagManager);
+}
+
+static void MarkManagedStaticVariableRoots (GarbageCollectorState& gcState);
+static void FindAllLiveObjects (GarbageCollectorState& gcState);
+static void MarkSceneRootsAndReduceLiveObjects (GarbageCollectorState& gcState);
+static void MarkManagerRoots (GarbageCollectorState& gcState);
+static void MarkSelectedObjectsAsRoots (GarbageCollectorState& gcState);
+static void MarkAllDependencies (GarbageCollectorState& gcState);
+static void CleanupUnusedObjects (GarbageCollectorState& gcState);
+static void ValidateNoObjectsWereLoaded (GarbageCollectorState& state);
+static void CreateObjectToIndexMappingFromNonRootObjects (GarbageCollectorState& gcState);
+static void CleanupOtherUnusedMemory (GarbageCollectorState& gcState);
+static void BeginLivenessChecking( GarbageCollectorState& gcState);
+static void EndLivenessChecking( GarbageCollectorState& gcState);
+
+
+// Rename
+void GarbageCollectSharedAssets (bool monoReferences)
+{
+ PROFILER_AUTO(gGarbageCollectSharedAssetsProfile, NULL);
+
+ ABSOLUTE_TIME totalTime;
+ ABSOLUTE_TIME findAllLiveObjectsTime;
+ ABSOLUTE_TIME buildLiveObjectMapTime;
+ ABSOLUTE_TIME markTime;
+ ABSOLUTE_TIME unloadTime;
+
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ // Gab: ManagedLivenessAnalysis needs to be reset every time before executing the liveness check.
+ static ScriptingInvocation resetManagedAnalysis(GetScriptingMethodRegistry().GetMethod("UnityEngine.Serialization","ManagedLivenessAnalysis","ResetState"));
+ resetManagedAnalysis.Invoke();
+#endif
+
+ totalTime = START_TIME;
+ int originalObjectCount = Object::GetLoadedObjectCount();
+#if ENABLE_PROFILER
+ printf_console("System memory in use before: %s.\n", FormatBytes(GetUsedHeapSize()).c_str());
+#endif
+ // This is essentially a Mark & Sweep Garbage Collector.
+
+ GarbageCollectorState state;
+
+ state.followMonoReferences = monoReferences;
+ state.originalObjectCount = originalObjectCount;
+
+ findAllLiveObjectsTime = START_TIME;
+
+ //@TODO: This stage can easily be jobified
+ // Fills all live objects and extracts their classID's for use by the marking algorithm
+ FindAllLiveObjects(state);
+ MarkSceneRootsAndReduceLiveObjects (state);
+
+ findAllLiveObjectsTime = ELAPSED_TIME(findAllLiveObjectsTime);
+
+ buildLiveObjectMapTime = START_TIME;
+ // Create mapping to quickly look up all object references
+ CreateObjectToIndexMappingFromNonRootObjects(state);
+ buildLiveObjectMapTime = ELAPSED_TIME(buildLiveObjectMapTime);
+
+ markTime = START_TIME;
+
+ MarkManagerRoots (state);
+ MarkSelectedObjectsAsRoots (state);
+
+ ValidateNoObjectsWereLoaded (state);
+
+ {
+ // Once we call BeginLivenessChecking we can not use the profiler because it might allocate memory.
+ PROFILER_AUTO(gGCMarkDependencies, NULL);
+
+ BeginLivenessChecking(state);
+
+ // Mark static variables
+ MarkManagedStaticVariableRoots (state);
+ // Process roots to find all referenced objects
+ MarkAllDependencies (state);
+
+ EndLivenessChecking(state);
+ }
+ markTime = ELAPSED_TIME(markTime);
+
+ ValidateNoObjectsWereLoaded (state);
+
+ if (state.originalObjectCount != Object::GetLoadedObjectCount())
+ {
+ ErrorString("UnloadUnusedAssets incorrect caused some assets to load. This can easily cause deadlocks or crashes.");
+ }
+
+ unloadTime = START_TIME;
+
+ // Cleanup all objects not marked as dependencies or roots
+ CleanupUnusedObjects (state);
+
+ unloadTime = ELAPSED_TIME(unloadTime);
+
+ int unloadedObjects = originalObjectCount - Object::GetLoadedObjectCount();
+
+ // Unload PersistentManager memory that is not needed
+ CleanupOtherUnusedMemory (state);
+
+ totalTime = ELAPSED_TIME(totalTime);
+
+#if ENABLE_PROFILER
+ printf_console("System memory in use after: %s.\n", FormatBytes(GetUsedHeapSize()).c_str());
+#endif
+ printf_console("\nUnloading %d unused Assets to reduce memory usage. Loaded Objects now: %d.\n", unloadedObjects, (int)Object::GetLoadedObjectCount());
+ printf_console("Total: %f ms (FindLiveObjects: %f ms CreateObjectMapping: %f ms MarkObjects: %f ms DeleteObjects: %f ms)\n\n",
+ AbsoluteTimeToMilliseconds(totalTime), AbsoluteTimeToMilliseconds(findAllLiveObjectsTime), AbsoluteTimeToMilliseconds(buildLiveObjectMapTime), AbsoluteTimeToMilliseconds(markTime), AbsoluteTimeToMilliseconds(unloadTime) );
+
+ #if UNITY_EDITOR
+ EditorAssetGarbageCollectManager::Get()->SetPostCollectMemoryUsage();
+ #endif
+}
+
+
+#if ASSET_REMAP_TABLE
+static void CreateObjectToIndexMappingFromNonRootObjects (GarbageCollectorState& gcState)
+{
+ PROFILER_AUTO(gGCBuildLiveObjectMaps, NULL);
+
+ gcState.instanceIDToIndex.set_empty_key (-1);
+ gcState.instanceIDToIndex.set_deleted_key (-2);
+
+ int largestInstanceID = 0;
+
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ if (instanceID > 0)
+ {
+ largestInstanceID = std::max<SInt32>(largestInstanceID, instanceID);
+ }
+ else
+ {
+ gcState.instanceIDToIndex.insert(std::make_pair(instanceID, i));
+ }
+ }
+ }
+
+ // Cap the memory of dense assetRemapTable
+ if (largestInstanceID < (kMaximumAssetRemapTableSize / sizeof(UInt32)))
+ {
+ gcState.assetRemapTable.resize_initialized(largestInstanceID + 1, -1);
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ if (instanceID > 0)
+ gcState.assetRemapTable[instanceID] = i;
+ }
+ }
+ }
+ // Use hashtable for all instance ids as a fallback
+ else
+ {
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ if (instanceID > 0)
+ gcState.instanceIDToIndex.insert(std::make_pair(instanceID, i));
+ }
+ }
+ }
+}
+
+#else
+static void CreateObjectToIndexMappingFromNonRootObjects (GarbageCollectorState& gcState)
+{
+ ///@TODO: Should we recreate gcState.liveObjects here? There is no reason to ever visit objects that are already marked at this point...
+ //// Try it...
+
+ for (int i=0;i<gcState.liveObjects.size();++i)
+ {
+ ObjectState& state = gcState.liveObjects[i];
+ if (state.marked == 0)
+ {
+ int instanceID = state.object->GetInstanceID();
+
+ gcState.instanceIDToIndex.insert(make_pair(instanceID, i));
+ }
+ }
+}
+#endif
+
+
+static inline int LookupInstanceIDIndex (int instanceID, GarbageCollectorState& gcState)
+{
+ if (instanceID == 0)
+ return -1;
+
+#if ASSET_REMAP_TABLE
+
+ size_t size = gcState.assetRemapTable.size();
+ if (instanceID > 0 && size != 0)
+ {
+ if (instanceID < size)
+ return gcState.assetRemapTable[instanceID];
+ else
+ return -1;
+ }
+#endif
+ else
+ {
+ GarbageCollectorState::InstanceIDToIndex::const_iterator found = gcState.instanceIDToIndex.find(instanceID);
+ if (found == gcState.instanceIDToIndex.end())
+ return -1;
+ else
+ return found->second;
+ }
+}
+
+static inline int LookupObjectIndex (const Object& object, GarbageCollectorState& gcState)
+{
+ return LookupInstanceIDIndex (object.GetInstanceID(), gcState);
+}
+
+
+static void MarkIndexAsRoot (int index, GarbageCollectorState& gcState)
+{
+ ObjectState& state = gcState.liveObjects[index];
+
+ Assert(state.marked == 0);
+ state.marked = 1;
+
+ // If we have references to other objects in the marked object -> then we need to process it
+ // No processing is needed if the class has no references
+ // (Eg. a texture has no references to other objects, thus it does not have to be processed)
+ bool addToNeedsProcessing = DoesClassIDHaveReferences (state.classID);
+ Assert(state.object->ShouldIgnoreInGarbageDependencyTracking() == !addToNeedsProcessing);
+
+ #if ENABLE_MEM_PROFILER
+ // When extracting references for the memory profiler we use the needsProcessing array as the object which have been marked.
+ // Thus we can not use the fastpath for objects that have no references to other objects
+ addToNeedsProcessing |= gcState.alwaysAddToNeedsProcessing;
+ #endif
+
+ if (addToNeedsProcessing)
+ {
+ gcState.needsProcessing.push_back(index);
+ }
+}
+
+static void MarkObjectAsRoot (const Object& object, GarbageCollectorState& gcState)
+{
+ int index = LookupObjectIndex (object, gcState);
+ if (index != -1)
+ MarkIndexAsRoot(index, gcState);
+}
+
+static void MarkInstanceIDAsRoot (int instanceID, GarbageCollectorState& gcState)
+{
+ int index = LookupInstanceIDIndex (instanceID, gcState);
+ if (index != -1)
+ {
+ if (gcState.liveObjects[index].marked == 0)
+ MarkIndexAsRoot(index, gcState);
+ }
+}
+
+static void MarkObjectAsRootUnknownMarkState (const Object& object, GarbageCollectorState& gcState)
+{
+ int index = LookupObjectIndex (object, gcState);
+ if (index != -1 && gcState.liveObjects[index].marked == 0)
+ MarkIndexAsRoot(index, gcState);
+}
+
+static void MarkObjectAsRootCheckNull (const Object* object, GarbageCollectorState& gcState)
+{
+ if (object != NULL)
+ MarkObjectAsRootUnknownMarkState (*object, gcState);
+}
+
+#if USE_MONO_LIVENESS
+static void RegisterFilteredObjectCallback(gpointer* arr, int count, void* userdata)
+{
+ GarbageCollectorState* gcStatePtr = (GarbageCollectorState*)userdata;
+ for (int i = 0; i < count ;i++)
+ {
+ SInt32 instanceID = Scripting::GetInstanceIDFromScriptingWrapper((MonoObject*)arr[i]);
+ MarkInstanceIDAsRoot(instanceID, *gcStatePtr);
+ }
+}
+#endif
+
+static void BeginLivenessChecking( GarbageCollectorState& gcState)
+{
+ if(!gcState.followMonoReferences)
+ return;
+#if USE_MONO_LIVENESS
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().DisallowAllocationsOnThisThread();
+ #endif
+ gcState.livenessState = mono_unity_liveness_calculation_begin(ScriptingClassFor(Object), gcState.liveObjects.size(), RegisterFilteredObjectCallback, (void*)&gcState);
+#endif
+}
+
+static void EndLivenessChecking( GarbageCollectorState& gcState)
+{
+ if(!gcState.followMonoReferences)
+ return;
+#if USE_MONO_LIVENESS
+ mono_unity_liveness_calculation_end(gcState.livenessState);
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().ReallowAllocationsOnThisThread();
+ #endif
+#endif
+}
+
+static void MarkManagedStaticVariableRoots (GarbageCollectorState& gcState)
+{
+ if(!gcState.followMonoReferences)
+ return;
+
+#if USE_MONO_LIVENESS
+ ValidateNoObjectsWereLoaded (gcState);
+ mono_unity_liveness_calculation_from_statics (gcState.livenessState);
+#endif
+}
+
+#if UNITY_EDITOR
+void SetPreventGarbageCollectionOfAsset (int instanceID)
+{
+ Assert(gPreventGarbageCollectionOfInstanceID == 0);
+ gPreventGarbageCollectionOfInstanceID = instanceID;
+}
+
+void ClearPreventGarbageCollectionOfAsset (int instanceID)
+{
+ Assert(gPreventGarbageCollectionOfInstanceID == instanceID);
+ gPreventGarbageCollectionOfInstanceID = 0;
+}
+#endif
+
+
+static void MarkSelectedObjectsAsRoots (GarbageCollectorState& gcState)
+{
+#if UNITY_EDITOR
+ // Add all selected objects as GC Roots because they are being shown in the inspector.
+ set<int> selection = GetSceneTracker().GetSelectionID ();
+ for (set<int>::iterator i=selection.begin();i!=selection.end();i++)
+ MarkInstanceIDAsRoot(*i, gcState);
+#endif
+}
+
+
+static void MarkManagerRoots (GarbageCollectorState& gcState)
+{
+ // All managers are roots (except script mapper)
+ for (int i=0;i<ManagerContext::kManagerCount;i++)
+ {
+ if (GetManagerPtrFromContext(i) != NULL)
+ MarkObjectAsRootUnknownMarkState (*GetManagerPtrFromContext(i), gcState);
+ }
+
+ #if UNITY_EDITOR
+ MarkObjectAsRootUnknownMarkState (AssetDatabase::Get(), gcState);
+ MarkObjectAsRootUnknownMarkState (AssetServerCache::Get(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorBuildSettings(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorUserBuildSettings(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorSettings(), gcState);
+ MarkObjectAsRootUnknownMarkState (GetEditorUserSettings(), gcState);
+ MarkObjectAsRootCheckNull (AssetInterface::Get().GetGUIDSerializer(), gcState);
+ MarkObjectAsRootCheckNull (GetProjectWindowHierarchyStateIfLoaded (), gcState);
+ MarkObjectAsRootUnknownMarkState (GetInspectorExpandedState (), gcState);
+ MarkObjectAsRootUnknownMarkState (GetAnnotationManager (), gcState);
+
+ if (gPreventGarbageCollectionOfInstanceID != 0)
+ MarkInstanceIDAsRoot(gPreventGarbageCollectionOfInstanceID, gcState);
+
+ #endif
+
+ ValidateNoObjectsWereLoaded (gcState);
+}
+
+#if UNITY_EDITOR
+bool ShouldPersistentDirtyObjectBeKeptAlive (int instanceID)
+{
+ // The Object is not mapped to disk
+ SerializedObjectIdentifier identifier;
+ if (!GetPersistentManager().InstanceIDToSerializedObjectIdentifier(instanceID, identifier))
+ {
+ // #if !UNITY_RELEASE
+ // WarningString("Persistent object is known in persistent manager will unload Might happen due to Assetbundle.Unload(false)");
+ // #endif
+ return false;
+ }
+
+ // Cached asset file assets should just be unloaded. They contain nothing that can not be reloaded.
+ // Eg a texture in an imported .psd file, while scripts might call SetDirty on it, it doesn't actually ever get serialized back.
+ // Because the only thing that save an texture is the asset importer. Any modifications done in memory will never be saved.
+ // -> Thus a texture that is marked dirty can be unloaded
+ // -> But a MetaData object or AssetImporter settings object (eg. class TextureImporter) should not be unloaded if it is marked dirty
+ FileIdentifier file = GetGUIDPersistentManager().PathIDToFileIdentifierInternal(identifier.serializedFileIndex);
+ if (file.type == FileIdentifier::kMetaAssetType)
+ {
+ if (identifier.localIdentifierInFile == kAssetMetaDataFileID)
+ {
+ // We can't have this warning since changing an import setting and then starting a long import operation will trigger it.
+ //WarningString(Format("ImportSettings '%s' has been modified but AssetDatabase.ImportAsset has not been called. Please fix the scripts code or Import the asset manually.", GetAssetPathFromInstanceID(instanceID).c_str()));
+ return true;
+ }
+ else if (identifier.localIdentifierInFile == kAssetImporterFileID)
+ {
+ // We can't have this warning since changing an import setting and then starting a long import operation will trigger it.
+ //WarningString(Format("ImportSettings '%s' has been modified but AssetDatabase.ImportAsset has not been called. Please fix the scripts code or Import the asset manually.", GetAssetPathFromInstanceID(instanceID).c_str()));
+ return true;
+ }
+
+ return false;
+ }
+ // Any other assets are will not be unloaded when dirtied.
+ // Eg. scriptmapper, guidmapper, builtin resources...
+ else
+ {
+ return true;
+ }
+}
+#endif
+
+enum GCRootType
+{
+ // The object is not a GC root (might be referenced during the marking stage)
+ kNoGCRoot = 0,
+ // The object is a GC root and is guaranteed to not have any references to non-root objects (Things that are already guaranteed to be marked)
+ // For example, game objects and transforms in a scene don't need to be processed, because they can only reference components that are already in the scene and those are already marked as roots.
+ kGCRootReferencesOnly = 1,
+
+ // a root for the GC marking, must be processed and it's dependencies must be found
+ kGCRootWithReferences = 2
+};
+
+#if UNITY_EDITOR
+#define DoesNotHavePrefabAttached(liveObject) (static_cast<EditorExtension*> (liveObject.object)->GetPrefab().GetInstanceID() == 0)
+#else
+#define DoesNotHavePrefabAttached(liveObject) true
+#endif
+
+inline bool HasValidGameObject (const ObjectState& liveObject)
+{
+ const Unity::Component* component = static_cast<const Unity::Component*> (liveObject.object);
+ return component->GetGameObjectPtr() != NULL;
+}
+
+inline bool IsScriptableObject (const ObjectState& liveObject)
+{
+ const MonoBehaviour* monoBehaviour = static_cast<const MonoBehaviour*> (liveObject.object);
+ return monoBehaviour->GetGameObjectPtr() == NULL;
+}
+
+static bool IsSceneObject (const ObjectState& liveObject)
+{
+ if (liveObject.isPersistent)
+ return false;
+
+ if (liveObject.classID == ClassID(MonoBehaviour) && IsScriptableObject(liveObject))
+ return false;
+
+ return liveObject.classID == ClassID(GameObject) || Object::IsDerivedFromClassID(liveObject.classID, ClassID(Component));
+}
+
+static bool IsAssetNotYetSavedToDisk (const ObjectState& liveObject)
+{
+#if UNITY_EDITOR
+ if (liveObject.isPersistent && liveObject.object->IsPersistentDirty())
+ {
+ if (ShouldPersistentDirtyObjectBeKeptAlive (liveObject.object->GetInstanceID()))
+ return true;
+ }
+#endif
+ return false;
+}
+
+
+static GCRootType IsObjectAGCRoot (const ObjectState& liveObject)
+{
+ int classID = liveObject.classID;
+ if (!liveObject.isPersistent)
+ {
+ // Game Objects & Transforms in the scene are roots and have no references beyond other objects that are also in the scene
+ // Except for prefabs which we specifically check for in the editor
+ if (classID == ClassID(GameObject) && DoesNotHavePrefabAttached(liveObject))
+ return kGCRootReferencesOnly;
+ else if (classID == ClassID(Transform) && DoesNotHavePrefabAttached(liveObject))
+ return kGCRootReferencesOnly;
+ // Only MonoBehaviour not ScriptableObject are treated as roots
+ else if (classID == ClassID(MonoBehaviour))
+ {
+ if (!IsScriptableObject(liveObject))
+ return kGCRootWithReferences;
+ }
+ else if (Object::IsDerivedFromClassID(classID, ClassID(Component)))
+ {
+ Assert(HasValidGameObject (liveObject));
+ return kGCRootWithReferences;
+ }
+ }
+
+ // Asset bundles are always explicitly unloaded
+ if (classID == ClassID(AssetBundle))
+ return kGCRootWithReferences;
+ // Objects marked as hide and dont save are treated as roots
+ // else if (liveObject.object->TestHideFlag (Object::kDontSave))
+ // return kGCRootWithReferences;
+ else if (liveObject.object->TestHideFlag (Object::kDontSave))
+ return kGCRootWithReferences;
+#if UNITY_EDITOR
+ else if (liveObject.isPersistent && liveObject.object->IsPersistentDirty())
+ {
+ if (ShouldPersistentDirtyObjectBeKeptAlive (liveObject.object->GetInstanceID()))
+ return kGCRootWithReferences;
+ }
+#endif
+
+ return kNoGCRoot;
+}
+
+static void FindAllLiveObjects (GarbageCollectorState& gcState)
+{
+ PROFILER_AUTO(gGCFindLiveObjects, NULL);
+ gcState.originalObjectCount = Object::GetLoadedObjectCount();
+
+ Object::IDToPointerMap& pointerMap = Object::GetIDToPointerMapInternal ();
+ gcState.liveObjects.resize_uninitialized(pointerMap.size());
+ gcState.needsProcessing.reserve(pointerMap.size());
+
+ Object::IDToPointerMap::const_iterator end = pointerMap.end();
+ Object::IDToPointerMap::const_iterator i=pointerMap.begin();
+
+ int index = 0;
+
+ ObjectState* liveObjects = gcState.liveObjects.begin();
+
+ // Extract All live objects
+ while (i != end)
+ {
+ Object& object = *i->second;
+
+ ObjectState& state = liveObjects[index];
+
+ state.object = &object;
+ state.classID = object.GetClassID();
+ state.marked = 0;
+ state.isPersistent = object.IsPersistent();
+
+ index++;
+ i++;
+ }
+
+ ValidateNoObjectsWereLoaded (gcState);
+}
+
+static void MarkSceneRootsAndReduceLiveObjects (GarbageCollectorState& gcState)
+{
+ int size = gcState.liveObjects.size();
+ ObjectState* liveObjects = gcState.liveObjects.begin();
+
+ // Mark scene roots
+ for (int index=0;index<size;)
+ {
+ ObjectState& state = liveObjects[index];
+
+ GCRootType type = IsObjectAGCRoot (state);
+
+ // It is a root and is guaranteed to have no references to non-roots
+ // We dont need it in any maps or even the liveObjects list.
+ if (type == kGCRootReferencesOnly)
+ {
+ // Reducing the size of this array early on saved around 3% on angrybots so not a significant optimization
+ size--;
+ liveObjects[index] = liveObjects[size];
+ }
+ // Root that might have references to other objects, thus must be processed
+ else if (type == kGCRootWithReferences )
+ {
+ MarkIndexAsRoot (index, gcState);
+ index++;
+ }
+ else
+ {
+ index++;
+ }
+ }
+
+ gcState.liveObjects.resize_uninitialized(size);
+
+ ValidateNoObjectsWereLoaded (gcState);
+}
+
+#if UNITY_EDITOR
+static void ProcessEditorExtension (GarbageCollectorState& gcState, EditorExtension& object)
+{
+ MarkInstanceIDAsRoot (object.GetPrefab().GetInstanceID(), gcState);
+}
+#else
+#define ProcessEditorExtension(x,y)
+#endif
+
+
+static void ProcessGameObject (GarbageCollectorState& gcState, ObjectState& state)
+{
+ GameObject& go = *static_cast<Unity::GameObject*> (state.object);
+
+ ProcessEditorExtension(gcState, go);
+
+ GameObject::Container& container = go.GetComponentContainerInternal ();
+ GameObject::Container::iterator end = container.end();
+ for (GameObject::Container::iterator i = container.begin();i != end;++i)
+ {
+ MarkObjectAsRootUnknownMarkState (*i->second, gcState);
+ }
+}
+
+static void ProcessValidComponent (GarbageCollectorState& gcState, ObjectState& state)
+{
+ Unity::Component& com = *static_cast<Unity::Component*> (state.object);
+
+ ProcessEditorExtension(gcState, com);
+
+ GameObject& go = com.GetGameObject ();
+ MarkObjectAsRootUnknownMarkState (go, gcState);
+}
+
+
+static void ProcessTransform (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent(gcState, state);
+
+ Transform& transform = *static_cast<Transform*> (state.object);
+
+ const Transform::TransformComList& children = transform.GetChildrenInternal ();
+ Transform::TransformComList::const_iterator end = children.end();
+ for (Transform::TransformComList::const_iterator i = children.begin();i != end;++i)
+ {
+ MarkObjectAsRootUnknownMarkState (**i, gcState);
+ }
+
+ MarkObjectAsRootCheckNull (transform.GetParentPtrInternal (), gcState);
+}
+
+static void ProcessMeshFilter (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent (gcState, state);
+
+ MeshFilter& filter = *static_cast<MeshFilter*> (state.object);
+ MarkInstanceIDAsRoot (filter.GetSharedMesh().GetInstanceID(), gcState);
+}
+
+static void ProcessMeshRenderer (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent (gcState, state);
+
+ Renderer& renderer = *static_cast<Renderer*> (state.object);
+
+ const Renderer::MaterialArray& materials = renderer.GetMaterialArray ();
+ Renderer::MaterialArray::const_iterator end = materials.end();
+ for (Renderer::MaterialArray::const_iterator i=materials.begin();i != end;++i)
+ MarkInstanceIDAsRoot(i->GetInstanceID(), gcState);
+
+ // Make sure that the static batching root has a game object...
+// MarkInstanceIDAsRoot (renderer.GetStaticBatchRoot ().GetInstanceID(), gcState);
+
+
+ MarkInstanceIDAsRoot (renderer.GetLightProbeAnchor ().GetInstanceID(), gcState);
+}
+
+#if ENABLE_PHYSICS
+static void ProcessCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessValidComponent (gcState, state);
+
+ Collider& collider = *static_cast<Collider*> (state.object);
+ MarkInstanceIDAsRoot(collider.GetMaterial().GetInstanceID(), gcState);
+}
+
+static void ProcessPrimitiveCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessCollider (gcState, state);
+}
+
+static void ProcessMeshCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ ProcessCollider (gcState, state);
+
+ MeshCollider& collider = *static_cast<MeshCollider*> (state.object);
+ MarkInstanceIDAsRoot(collider.GetSharedMesh().GetInstanceID(), gcState);
+}
+#else
+
+static void ProcessPrimitiveCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ AssertString("Not supported");
+}
+static void ProcessMeshCollider (GarbageCollectorState& gcState, ObjectState& state)
+{
+ AssertString("Not supported");
+}
+#endif
+
+#if USE_MONO_LIVENESS
+
+static void ProcessLivenessFromScriptingObject (GarbageCollectorState& gcState, ScriptingObjectPtr object)
+{
+ mono_unity_liveness_calculation_from_root(object, gcState.livenessState);
+}
+
+static void ProcessMonoBehaviour (GarbageCollectorState& gcState, ObjectState& state)
+{
+ MonoBehaviour& behaviour = *static_cast<MonoBehaviour*> (state.object);
+
+ ProcessEditorExtension(gcState, behaviour);
+
+ // Not all MonoBehaviours are attached to a game object.
+ // Handle that situation specifically here.
+ GameObject* go = behaviour.GetGameObjectPtr ();
+ if (go != NULL)
+ MarkObjectAsRootUnknownMarkState (*go, gcState);
+
+ MarkInstanceIDAsRoot (behaviour.GetScript().GetInstanceID(), gcState);
+
+ if(!gcState.followMonoReferences)
+ return;
+
+ // MonoBehaviour managed instance
+ MonoObject* instance = Scripting::ScriptingWrapperFor(state.object);
+ if (instance != NULL)
+ ProcessLivenessFromScriptingObject(gcState, instance);
+
+ // Coroutine managed instances
+ List<Coroutine>& coroutine = behaviour.GetActiveCoroutines();
+ ListIterator<Coroutine> end = coroutine.end();
+ for (ListIterator<Coroutine> i=coroutine.begin();i != end;++i)
+ {
+ Coroutine& coroutine = *i;
+ ProcessLivenessFromScriptingObject(gcState, coroutine.m_CoroutineEnumerator);
+ }
+}
+
+#else
+
+static void ProcessMonoBehaviour (GarbageCollectorState& gcState, ObjectState& state)
+{
+ AssertString("Not supported");
+}
+#endif
+
+inline void GenericSlowGarbageCollector::ProcessReference (SInt32 oldInstanceID)
+{
+ int index = LookupInstanceIDIndex (oldInstanceID, *gcState);
+ if (index == -1)
+ return;
+
+ ObjectState& referencedState = gcState->liveObjects[index];
+ if (referencedState.marked == 0)
+ MarkIndexAsRoot(index, *gcState);
+ }
+
+ // General purpose GarbageCollector callback
+SInt32 GenericSlowGarbageCollector::GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlag)
+ {
+ ProcessReference(oldInstanceID);
+ return oldInstanceID;
+ }
+
+void MarkDependencies (GarbageCollectorState& gcState, UInt32 index)
+{
+ ObjectState& state = gcState.liveObjects[index];
+
+ #if ENABLE_MEM_PROFILER
+ Assert(!state.object->ShouldIgnoreInGarbageDependencyTracking() || gcState.alwaysAddToNeedsProcessing);
+ #else
+ Assert(!state.object->ShouldIgnoreInGarbageDependencyTracking());
+ #endif
+
+ Assert((bool)state.marked);
+
+ if (state.classID == ClassID (GameObject))
+ ProcessGameObject(gcState, state);
+ else if (state.classID == ClassID (Transform))
+ ProcessTransform(gcState, state);
+ else if (state.classID == ClassID (MeshRenderer))
+ ProcessMeshRenderer(gcState, state);
+ else if (state.classID == ClassID (MeshFilter))
+ ProcessMeshFilter(gcState, state);
+ // Specialized MonoBehaviour code path for mono based platforms, otherwise falls back to the generic RemapPPtrTransfer
+ else if ((USE_MONO_LIVENESS) && gcState.followMonoReferences && state.classID == ClassID (MonoBehaviour))
+ ProcessMonoBehaviour(gcState, state);
+ else if ((ENABLE_PHYSICS) && state.classID == ClassID (MeshCollider))
+ ProcessMeshCollider(gcState, state);
+ else if ((ENABLE_PHYSICS) && state.classID == ClassID (BoxCollider))
+ ProcessPrimitiveCollider(gcState, state);
+#if ENABLE_SERIALIZATION_BY_CODEGENERATION
+ else if (state.classID == ClassID (MonoBehaviour))
+ state.object->DoLivenessCheck(gcState.genericSlowTransfer);
+#endif
+ else
+ state.object->VirtualRedirectTransfer (gcState.genericSlowTransfer);
+}
+
+static void MarkAllDependencies (GarbageCollectorState& gcState)
+{
+ //@TODO: This stage can easily be multi-threaded
+ /// The out of this stage is gcState.liveObjects[i].marked = 1;
+ // Find some efficient way multithread that without expensive thread synchronization
+
+ dynamic_array<UInt32>& needsProcessing = gcState.needsProcessing;
+ while (!needsProcessing.empty ())
+ {
+ int index = needsProcessing.back();
+ needsProcessing.pop_back();
+
+ MarkDependencies (gcState, index);
+ }
+}
+
+#if ENABLE_MEM_PROFILER
+static void ExtractObjectArray (GarbageCollectorState& gcState, dynamic_array<Object*>& objects)
+{
+ objects.resize_uninitialized(gcState.liveObjects.size());
+ for (int i=0;i<objects.size();i++)
+ objects[i] = gcState.liveObjects[i].object;
+}
+
+static void ResetMarkedAndNeedsProcessing (GarbageCollectorState& state, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices)
+{
+ referencedObjectCount.push_back(state.needsProcessing.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), state.needsProcessing.begin(), state.needsProcessing.end());
+ for (int i=0;i<state.needsProcessing.size();i++)
+ state.liveObjects[state.needsProcessing[i]].marked = 0;
+ state.needsProcessing.resize_uninitialized(0);
+}
+
+static void ClassifyRootObjects (GarbageCollectorState& state, dynamic_array<const char*>& additionalCategories, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices)
+{
+ // Calculate references for all scene roots
+ dynamic_array<UInt32> sceneRootIndices;
+ dynamic_array<UInt32> otherRootIndices;
+ dynamic_array<UInt32> dirtyAssetIndices;
+
+ for (int i=0;i<state.liveObjects.size();i++)
+ {
+ ObjectState& liveObject = state.liveObjects[i];
+
+ if (IsSceneObject (liveObject))
+ sceneRootIndices.push_back(i);
+ else if (IsAssetNotYetSavedToDisk (liveObject))
+ dirtyAssetIndices.push_back(i);
+ else if (IsObjectAGCRoot (liveObject) != kNoGCRoot)
+ otherRootIndices.push_back(i);
+ }
+
+ additionalCategories.push_back("Scene Object");
+ referencedObjectCount.push_back(sceneRootIndices.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), sceneRootIndices.begin(), sceneRootIndices.end());
+
+ additionalCategories.push_back("HideAndDontSave, Manager or AssetBundle");
+ referencedObjectCount.push_back(otherRootIndices.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), otherRootIndices.begin(), otherRootIndices.end());
+
+ additionalCategories.push_back("Asset has been edited and not yet saved to disk");
+ referencedObjectCount.push_back(dirtyAssetIndices.size());
+ referencedObjectIndices.insert(referencedObjectIndices.end(), dirtyAssetIndices.begin(), dirtyAssetIndices.end());
+}
+
+void CalculateAllObjectReferences (dynamic_array<Object*>& loadedObjects, dynamic_array<const char*>& additionalCategories, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices)
+{
+ enum { kNbAdditionalCategoriesReserve = 20 };
+
+ GarbageCollectorState state;
+ state.alwaysAddToNeedsProcessing = true;
+ state.followMonoReferences = true;
+
+ // Setup live object mapping
+ FindAllLiveObjects(state);
+ CreateObjectToIndexMappingFromNonRootObjects (state);
+
+ // Build loadedObjects output
+ ExtractObjectArray (state, loadedObjects);
+
+ referencedObjectIndices.reserve (loadedObjects.size() * 2);
+ referencedObjectCount.reserve(loadedObjects.size() + kNbAdditionalCategoriesReserve);
+
+ // Calculate references for all loaded objects
+ for (int i=0;i<loadedObjects.size();i++)
+ {
+ ObjectState& liveObject = state.liveObjects[i];
+ if (liveObject.classID == ClassID(MonoBehaviour))
+ BeginLivenessChecking(state);
+
+ Assert(!liveObject.marked);
+ if (DoesClassIDHaveReferences (liveObject.classID))
+ {
+ liveObject.marked = 1;
+ MarkDependencies (state, i);
+ liveObject.marked = 0;
+ }
+
+ if (liveObject.classID == ClassID(MonoBehaviour))
+ EndLivenessChecking(state);
+
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+ }
+
+ // Calculate references for managed static references
+ additionalCategories.push_back("ManagedStaticReferences");
+ BeginLivenessChecking(state);
+ MarkManagedStaticVariableRoots (state);
+ EndLivenessChecking(state);
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+
+ // Calculate references from any managers
+ additionalCategories.push_back("Managers");
+ MarkManagerRoots(state);
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+
+ // Calculate references from any managers
+ additionalCategories.push_back("Selection");
+ MarkSelectedObjectsAsRoots(state);
+ ResetMarkedAndNeedsProcessing (state, referencedObjectCount, referencedObjectIndices);
+
+ // Classify root objects (Scene objects, hide and dontsave etc)
+ ClassifyRootObjects (state, additionalCategories, referencedObjectCount, referencedObjectIndices);
+}
+#endif
+
+static void InvokeScriptableObjectUnloadCallbacks (const SInt32* scriptableObjectCallbacks, int size)
+{
+ PROFILER_AUTO(gGCOnDestroyCallback, NULL);
+
+#if ENABLE_SCRIPTING
+ for (int i=0;i<size;i++)
+ {
+ MonoBehaviour* behaviour = reinterpret_cast<MonoBehaviour*>(Object::IDToPointer(scriptableObjectCallbacks[i]));
+ if (behaviour)
+ {
+ behaviour->WillUnloadScriptableObject();
+ }
+ }
+#endif
+}
+
+static void CleanupUnusedObjects (GarbageCollectorState& gcState)
+{
+ PROFILER_AUTO(gGCDeletedUnusedAssets, NULL);
+
+ dynamic_array<SInt32> unloadObjects;
+ dynamic_array<SInt32> scriptableObjectUnloadCallbacks;
+ unloadObjects.reserve(gcState.liveObjects.size());
+ scriptableObjectUnloadCallbacks.reserve(gcState.liveObjects.size());
+
+ // Classify
+ for (int i=0;i<gcState.liveObjects.size();i++)
+ {
+ const ObjectState& objectState = gcState.liveObjects[i];
+
+ if (objectState.marked == 0)
+ {
+ SInt32 instanceID = objectState.object->GetInstanceID();
+ unloadObjects.push_back(instanceID);
+
+ if (objectState.classID == ClassID(MonoBehaviour))
+ scriptableObjectUnloadCallbacks.push_back(instanceID);
+ }
+ }
+
+ // ScriptableObject.OnDestroy
+ InvokeScriptableObjectUnloadCallbacks (scriptableObjectUnloadCallbacks.begin(), scriptableObjectUnloadCallbacks.size());
+
+ // Delete objects
+ BatchDeleteObjectInternal (unloadObjects.begin(), unloadObjects.size());
+}
+
+static void CleanupOtherUnusedMemory (GarbageCollectorState& gcState)
+{
+ // Unload streams that are not dirty
+ GetPersistentManager().UnloadNonDirtyStreams ();
+}
+
+
+static void ValidateNoObjectsWereLoaded (GarbageCollectorState& state)
+{
+ Assert(state.originalObjectCount == Object::GetLoadedObjectCount());
+}
+
+
+/*
+
+TODO:
+
+Tests:
+ - ScriptableObject referenced from static variable and no longer referenced from static variable
+ - Dirty assets are not unloaded
+ - Get rid of Validation of bound animation targets on every frame again. Profiler new event system for performance of deleting characters in that case.
+
+Important:
+-- When unloading make sure we dont unload other objects during destruction. Maybe we can use it to get rid of Object* lookups this way???
+ @TODO: Try if we can enable this safely.
+ #if DEBUGMODE
+ int count = Object::GetLoadedObjectCount ();
+#endif
+ Do deletion
+ #if DEBUGMODE
+ if (count-1 != Object::GetLoadedObjectCount ())
+ {
+ AssertString("Unloading this object deleted more than the object alone. This is not allowed.");
+ }
+#endif
+*/
+#endif
diff --git a/Runtime/Misc/GarbageCollectSharedAssets.h b/Runtime/Misc/GarbageCollectSharedAssets.h
new file mode 100644
index 0000000..a9a990d
--- /dev/null
+++ b/Runtime/Misc/GarbageCollectSharedAssets.h
@@ -0,0 +1,31 @@
+#ifndef GARBAGE_COLLECT_SHARED_ASSETS_H
+#define GARBAGE_COLLECT_SHARED_ASSETS_H
+
+/// Unloads all assets that are not referenced using dependency tracking and mono GC.
+/// You must call GetPreloadManager().LockPreloading(); GetPreloadManager().UnlockPreloading(); around GarbageCollectSharedAssets.
+void GarbageCollectSharedAssets (bool includeMonoReferencesAsRoots);
+
+/// Calculates all direct references of all loaded objects and additional categories
+/// * loadedObjects is an array of all loaded objects
+/// * additionalCategories is an array of all custom categories (eg. "SceneObject", "HideAndDontSave" etc)
+/// * referencedObjectCount is the number of referenced objects (referencedObjectIndices holds the indices)
+/// * referencedObjectIndices is a combined array of all indices (referencedObjectCount)
+/// This is a stream layout so to make sense of the data you have to walk referencedObjectCount in linear order
+#if ENABLE_MEM_PROFILER
+void CalculateAllObjectReferences (dynamic_array<Object*>& loadedObjects, dynamic_array<const char*>& additionalCategories, dynamic_array<UInt32>& referencedObjectCount, dynamic_array<UInt32>& referencedObjectIndices);
+#endif
+
+#if UNITY_EDITOR
+bool ShouldPersistentDirtyObjectBeKeptAlive (int instanceID);
+
+/// Prevents an Object from being destroyed by GarbageCollectSharedAssets().
+/// This is useful in cases where an Object is internally created by code without ties
+/// to the scene and needs to be kept alive across operations that may trigger an
+/// asset GC run.
+void SetPreventGarbageCollectionOfAsset (int instanceID);
+/// Reverts previous call to SetPreventGarbageCollectionOfAsset().
+void ClearPreventGarbageCollectionOfAsset (int instanceID);
+
+#endif
+
+#endif
diff --git a/Runtime/Misc/GraphicsDevicesDB.cpp b/Runtime/Misc/GraphicsDevicesDB.cpp
new file mode 100644
index 0000000..9a00a84
--- /dev/null
+++ b/Runtime/Misc/GraphicsDevicesDB.cpp
@@ -0,0 +1,1322 @@
+#include "UnityPrefix.h"
+#include "GraphicsDevicesDB.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+
+#if GRAPHICS_DEVICES_DB_AVAILABLE
+
+
+// Device ID lists based on:
+// General everything: http://pciids.sourceforge.net/pci.ids
+// ATI:
+// May 2011, http://developer.amd.com/gpu_assets/ATI_Device_ID_List_May_2011.txt
+// July 2010, http://developer.amd.com/gpu_assets/ATI_Device_ID_List_July_2010.txt
+// Jan 2009, http://developer.amd.com/gpu_assets/ATI_Device_IDs_Jan_09.txt
+// NVIDIA:
+// Home: http://developer.nvidia.com/content/device-id-list
+// March 2010, http://www.nvidia.com/object/device_ids.html
+// GeForce/ION/Quadro 257.41 WHQL driver INF list
+// Mesa: http://cgit.freedesktop.org/mesa/mesa/tree/include/pci_ids
+// Intel: http://software.intel.com/en-us/articles/intel-gma-3000-and-x3000-developers-guide/
+// Linux kernel driver database, CONFIG_DRM_* stuff: http://cateee.net/lkddb/web-lkddb/index_d.html
+
+// Performance tables can be found on Wikipedia, for better or worse:
+// http://en.wikipedia.org/wiki/Comparison_of_AMD_graphics_processing_units
+// http://en.wikipedia.org/wiki/Comparison_of_Nvidia_graphics_processing_units
+// http://en.wikipedia.org/wiki/Comparison_of_Intel_graphics_processing_units
+// also here: http://www.techarp.com/showarticle.aspx?artno=88
+
+
+
+struct GraphicsDeviceDesc {
+ int deviceID;
+ const char* name;
+ int pixelFillrate;
+};
+
+
+static const GraphicsDeviceDesc kATIDevices[] = {
+{ 0x3150, "Mobility Radeon X600 M24", 1600 },
+{ 0x3151, "FireMV 2400 RV380", 1600 },
+{ 0x3152, "Mobility Radeon X300 M24", 1600 },
+{ 0x3154, "Mobility FireGL V3200 M24GL", 1600 },
+{ 0x3171, "FireMV 2400 RV380", 1600 },
+{ 0x3E50, "Radeon X600/X550 Series RV380", 1600 },
+{ 0x3E54, "FireGL V3200 RV380GL", 1600 },
+{ 0x3E70, "Radeon X600/X550 Series RV380", 1600 },
+{ 0x3E74, "FireGL V3200 RV380GL", 1600 },
+{ 0x4144, "Radeon 9500 R300", 1100 },
+{ 0x4146, "Radeon 9600TX R300", 1100 },
+{ 0x4147, "FireGL Z1 R300GL", 1100 },
+{ 0x4148, "Radeon 9800 SE R350", 1300 },
+{ 0x4149, "Radeon 9500 R350", 1300 },
+{ 0x4150, "Radeon 9600 Series RV350", 1300 },
+{ 0x4151, "Radeon 9600 Series RV350", 1300 },
+{ 0x4152, "Radeon 9600 Series RV360", 1600 },
+{ 0x4153, "Radeon 9550/X1050 Series RV350", 1000 },
+{ 0x4154, "FireGL T2 RV350GL", 1300 },
+{ 0x4155, "Radeon 9600 Series RV350", 1300 },
+{ 0x4164, "Radeon 9500 R300", 1100 },
+{ 0x4166, "Radeon 9600TX R300", 1100 },
+{ 0x4167, "FireGL Z1 R300GL", 1100 },
+{ 0x4168, "Radeon 9800 SE R350", 1300 },
+{ 0x4169, "Radeon 9500 R350", 1300 },
+{ 0x4170, "Radeon 9600 Series RV350", 1300 },
+{ 0x4171, "Radeon 9600 Series RV350", 1300 },
+{ 0x4172, "Radeon 9600 Series RV360", 1600 },
+{ 0x4173, "Radeon 9550/X1050 Series RV350", 1000 },
+{ 0x4174, "FireGL T2 RV350GL", 1300 },
+{ 0x4175, "Radeon 9600 Series RV350", 1000 },
+{ 0x4966, "Radeon 9000 Series", 1000 },
+{ 0x4A48, "Radeon X800 Series R420", 3800 },
+{ 0x4A49, "Radeon X800 PRO R420", 5700 },
+{ 0x4A4A, "Radeon X800 Series R420", 3800 },
+{ 0x4A4B, "Radeon X800 XT R420", 8000 },
+{ 0x4A4C, "Radeon X800 Series R420", 3800 },
+{ 0x4A4D, "FireGL X3-256 R420GL", 3800 },
+{ 0x4A4E, "Mobility Radeon 9800 M18", 2800 },
+{ 0x4A4F, "Radeon X800 SE R420", 3400 },
+{ 0x4A50, "Radeon X800 XT Platinum Edition R420", 8320 },
+{ 0x4A54, "Radeon X800 VE R420", 1700 },
+{ 0x4A68, "Radeon X800 Series R420", 3800 },
+{ 0x4A69, "Radeon X800 PRO R420", 5700 },
+{ 0x4A6A, "Radeon X800 Series R420", 3800 },
+{ 0x4A6B, "Radeon X800 XT R420", 8000 },
+{ 0x4A6C, "Radeon X800 Series R420", 3800 },
+{ 0x4A6D, "FireGL X3-256 R420GL", 3800 },
+{ 0x4A6F, "Radeon X800 SE R420", 3400 },
+{ 0x4A70, "Radeon X800 XT Platinum Edition R420", 8320 },
+{ 0x4A74, "Radeon X800 VE R420", 1700 },
+{ 0x4B49, "Radeon X850 XT R481", 8320 },
+{ 0x4B4A, "Radeon X850 SE R481", 3400 },
+{ 0x4B4B, "Radeon X850 PRO R481", 6240 },
+{ 0x4B4C, "Radeon X850 XT Platinum Edition R481", 8640 },
+{ 0x4B69, "Radeon X850 XT R481", 8320 },
+{ 0x4B6A, "Radeon X850 SE R481", 3400 },
+{ 0x4B6B, "Radeon X850 PRO R481", 6240 },
+{ 0x4B6C, "Radeon X850 XT Platinum Edition R481", 8640 },
+{ 0x4C66, "Mobility Radeon 9000", 1000 },
+{ 0x4E44, "Radeon 9700 PRO R300", 2600 },
+{ 0x4E45, "Radeon 9500 PRO / 9700 R300", 2200 },
+{ 0x4E46, "Radeon 9600 TX R300", 1100 },
+{ 0x4E47, "FireGL X1 R300GL", 1100 },
+{ 0x4E48, "Radeon 9800 PRO R350", 3040 },
+{ 0x4E49, "Radeon 9800 R350", 2600 },
+{ 0x4E4A, "Radeon 9800 XT R360", 3296 },
+{ 0x4E4B, "FireGL X2-256/X2-256t R350GL", 2600 },
+{ 0x4E50, "Mobility Radeon 9600/9700 Series M10", 1200 },
+{ 0x4E51, "Radeon 9600 Series RV350", 1300 },
+{ 0x4E52, "Mobility Radeon 9500 M10", 840 },
+{ 0x4E54, "Mobility FIRE GL T2/T2e M10GL", 840 },
+{ 0x4E56, "Mobility Radeon 9550 M12", 840 },
+{ 0x4E64, "Radeon 9700 PRO R300", 2600 },
+{ 0x4E65, "Radeon 9500 PRO / 9700 R300", 2200 },
+{ 0x4E66, "Radeon 9600 TX R300", 1100 },
+{ 0x4E67, "FireGL X1 R300GL", 1100 },
+{ 0x4E68, "Radeon 9800 PRO R350", 3040 },
+{ 0x4E69, "Radeon 9800 R350", 2600 },
+{ 0x4E6A, "Radeon 9800 XT R360", 3296 },
+{ 0x4E6B, "FireGL X2-256/X2-256t R350GL", 2600 },
+{ 0x4E71, "Radeon 9600 Series RV350", 1300 },
+{ 0x5460, "Mobility Radeon X300 M22", 1400 },
+{ 0x5461, "Mobility Radeon X300 M22", 1400 },
+{ 0x5462, "Mobility Radeon X600 SE M24C", 1600 },
+{ 0x5464, "Mobility FireGL V3100 M22GL", 1400 },
+{ 0x5548, "Radeon X800 Series R423", 4800 },
+{ 0x5549, "Radeon X800 GTO R423", 6400 },
+{ 0x554A, "Radeon X800 XT Platinum Edition R423", 8320 },
+{ 0x554B, "Radeon X800 GT R423", 3600 },
+{ 0x554D, "Radeon X800 XL R430", 6400 },
+{ 0x554E, "Radeon X800 GT R430", 3800 },
+{ 0x554F, "Radeon X800 GTO R430", 3800 },
+{ 0x5550, "FireGL V7100 R423GL", 4800 },
+{ 0x5551, "FireGL V5100 R423GL", 4800 },
+{ 0x5568, "Radeon X800 Series R423", 4800 },
+{ 0x5569, "Radeon X800 GTO R423", 6400 },
+{ 0x556A, "Radeon X800 XT Platinum Edition R423", 8320 },
+{ 0x556B, "Radeon X800 GT R423", 3600 },
+{ 0x556D, "Radeon X800 XL R430", 6400 },
+{ 0x556E, "Radeon X800 GT R430", 3800 },
+{ 0x556F, "Radeon X800 GTO R430", 3800 },
+{ 0x5570, "FireGL V7100 R423GL", 4800 },
+{ 0x5571, "FireGL V5100 R423GL", 4800 },
+{ 0x564A, "Mobility FireGL V5000 M26GL", 1400 },
+{ 0x564B, "Mobility FireGL V5000 M26GL", 1400 },
+{ 0x564F, "Mobility Radeon X700 XL M26", 1400 },
+{ 0x5652, "Mobility Radeon X700 M26", 1400 },
+{ 0x5653, "Mobility Radeon X700 M26", 140 },
+{ 0x5657, "Radeon X550/X700 Series RV410", 3200 },
+{ 0x5673, "Mobility Radeon X700 M26", 1400 },
+{ 0x5677, "Radeon X550/X700 Series RV410", 3200 },
+{ 0x5835, "Mobility Radeon 9000 IGP", 1000 },
+{ 0x5854, "Radeon Xpress Series RS480", 600 },
+{ 0x5874, "Radeon Xpress Series RS482", 600 },
+{ 0x5954, "Radeon Xpress Series RS480", 600 },
+{ 0x5960, "Radeon 9250", 960 },
+{ 0x5961, "Radeon 9000 Series", 1000 },
+{ 0x5964, "Radeon 9200 Series", 1000 },
+{ 0x5955, "Radeon Xpress Series RS480M", 600 },
+{ 0x5974, "Radeon Xpress Series RS482", 600 },
+{ 0x5975, "Radeon Xpress Series RS482M", 600 },
+{ 0x5A41, "Radeon Xpress Series RS400", 600 },
+{ 0x5A42, "Radeon Xpress Series RS400M", 600 },
+{ 0x5A43, "Radeon Xpress Series RS400", 600 },
+{ 0x5A61, "Radeon Xpress Series RC410", 800 },
+{ 0x5A62, "Radeon Xpress Series RC410M", 800 },
+{ 0x5A63, "Radeon Xpress Series RC410", 800 },
+{ 0x5B60, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B62, "Radeon X600 Series RV380x", 1600 },
+{ 0x5B63, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B64, "FireGL V3100 RV370GL", 1300 },
+{ 0x5B65, "FireMV 2200 RV370", 1300 },
+{ 0x5B70, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B72, "Radeon X600 Series RV380x", 1600 },
+{ 0x5B73, "Radeon X300/X550/X1050 Series RV370", 1300 },
+{ 0x5B74, "FireGL V3100 RV370GL", 1300 },
+{ 0x5B75, "FireMV 2200 RV370", 1300 },
+{ 0x5C61, "Mobility Radeon 9200", 1000 },
+{ 0x5D48, "Mobility/Radeon X800 XT M28", 4800 },
+{ 0x5D49, "Mobility FireGL V5100 M28GL", 4800 },
+{ 0x5D4A, "Mobility Radeon X800 M28", 4800 },
+{ 0x5D4D, "Radeon X850 XT Platinum Edition R480", 8640 },
+{ 0x5D4F, "Radeon X800 GTO R480", 3800 },
+{ 0x5D50, "FireGL V7200 R480GL", 3800 },
+{ 0x5D52, "Radeon X850 XT R480", 8320 },
+{ 0x5D57, "Radeon X800 XT R423", 8000 },
+{ 0x5D6D, "Radeon X850 XT Platinum Edition R480", 8640 },
+{ 0x5D6F, "Radeon X800 GTO R480", 3800 },
+{ 0x5D70, "FireGL V7200 R480GL", 3800 },
+{ 0x5D72, "Radeon X850 XT R480", 8320 },
+{ 0x5D77, "Radeon X800 XT R423", 8000 },
+{ 0x5E48, "FireGL V5000 RV410GL", 3200 },
+{ 0x5E4A, "Radeon X700 XT RV410", 3800 },
+{ 0x5E4B, "Radeon X700 PRO RV410", 3400 },
+{ 0x5E4C, "Radeon X700 SE RV410", 3200 },
+{ 0x5E4D, "Radeon X700 RV410", 3200 },
+{ 0x5E4F, "Radeon X700/X550 Series RV410", 3200 },
+{ 0x5E68, "FireGL V5000 RV410GL", 3200 },
+{ 0x5E6A, "Radeon X700 XT RV410", 3800 },
+{ 0x5E6B, "Radeon X700 PRO RV410", 3400 },
+{ 0x5E6C, "Radeon X700 SE RV410", 3200 },
+{ 0x5E6D, "Radeon X700 RV410", 3200 },
+{ 0x5E6F, "Radeon X700/X550 Series RV410", 3200 },
+{ 0x6704, "FirePro V7900", 23200 },
+{ 0x6707, "FirePro V5900", 19200 },
+{ 0x6718, "Radeon HD 6900 Series", 25600 },
+{ 0x6719, "Radeon HD 6900 Series", 28200 },
+{ 0x671D, "Radeon HD 6990", 39750 }, // 2xGPU, assume 1.5x perf
+{ 0x6720, "Radeon HD 6970M", 21760 },
+{ 0x6738, "Radeon HD 6800 Series", 25600 },
+{ 0x6739, "Radeon HD 6800 Series", 25600 },
+{ 0x673E, "Radeon HD 6700 Series", 12000 },
+{ 0x6740, "Radeon HD 6770M", 5800 },
+{ 0x6741, "Radeon HD 6750M", 4800 },
+{ 0x6749, "FirePro V4900", 6400 },
+{ 0x6758, "Radeon HD 6600 Series", 6400 },
+{ 0x6759, "Radeon HD 6500 Series", 5200 },
+{ 0x675B, "Radeon HD 7670", 6400 },
+{ 0x675D, "Radeon HD 7570", 5200 },
+{ 0x675F, "Radeon HD 5500 Series", 4400 },
+{ 0x6760, "Radeon HD 6490M", 3200 },
+{ 0x6770, "Radeon HD 6400 Series", 2750 },
+{ 0x6778, "Radeon HD 7470", 2750 },
+{ 0x6779, "Radeon HD 6450", 2750 },
+{ 0x677B, "Radeon HD 7400 Series", 3000 },
+{ 0x6798, "Radeon HD 7970", 29600 },
+{ 0x6799, "Radeon HD 7990", 40000 }, // 2xGPU, assume 1.5x perf
+{ 0x679A, "Radeon HD 7950", 25600 },
+{ 0x679E, "Radeon HD 7800 Series", 27500 },
+{ 0x6800, "Radeon HD 7970M", 27200 },
+{ 0x6818, "Radeon HD 7800 Series", 27500 },
+{ 0x6819, "Radeon HD 7800 Series", 27500 },
+{ 0x683D, "Radeon HD 7700 Series", 15000 },
+{ 0x683F, "Radeon HD 7700 Series", 15000 },
+{ 0x6840, "Radeon HD 7600M Series", 4000 },
+{ 0x6841, "Radeon HD 7500/7600M Series", 4000 },
+{ 0x6843, "Radeon HD 7670M", 4800 },
+{ 0x6850, "Radeon HD 7570", 5200 },
+{ 0x6858, "Radeon HD 7400 Series", 3000 },
+{ 0x6888, "FirePro V8800", 26400 },
+{ 0x6889, "FirePro V7800", 22400 },
+{ 0x688C, "FireStream 9370", 26400 },
+{ 0x688D, "FireStream 9350", 22400 },
+{ 0x6898, "Radeon HD 5800 Series", 23200 },
+{ 0x6899, "Radeon HD 5800 Series", 23200 },
+{ 0x689C, "Radeon HD 5900 Series", 32000 }, // 2xGPU, assume 1.5x perf
+{ 0x689E, "Radeon HD 5800 Series", 23200 },
+{ 0x68A0, "Mobility Radeon HD 5870", 11200 },
+{ 0x68A1, "Radeon HD 5750", 11200 },
+{ 0x68A8, "Radeon HD 6800M", 9200 },
+{ 0x68A9, "FirePro V5800", 11200 },
+{ 0x68B8, "Radeon HD 5700 Series", 11200 },
+{ 0x68B9, "Radeon HD 5600 Series", 6200 },
+{ 0x68BA, "Radeon HD 6700 Series", 12000 },
+{ 0x68BE, "Radeon HD 5700 Series", 11200 },
+{ 0x68BF, "Radeon HD 6700 Series", 12000 },
+{ 0x68C0, "Radeon HD 5670", 6200 },
+{ 0x68C1, "Mobility Radeon HD 5650", 4400 },
+{ 0x68C8, "FirePro/FireGL V4800", 6200 },
+{ 0x68C9, "FirePro V3800", 5200 },
+{ 0x68D8, "Radeon HD 5600 Series", 6200 },
+{ 0x68D9, "Radeon HD 5500 Series", 4400 },
+{ 0x68DA, "Radeon HD 5500 Series", 4400 },
+{ 0x68E0, "Mobility Radeon HD 5470", 3000 },
+{ 0x68E1, "Mobility Radeon HD 5430", 2200 },
+{ 0x68E4, "Mobility Radeon HD 6370", 3000 },
+{ 0x68E5, "Radeon HD 6330M", 2000 },
+{ 0x68FA, "Radeon HD 7350", 2000 },
+{ 0x68F9, "Radeon HD 5400 Series", 2600 },
+{ 0x7100, "Radeon X1800 Series R520", 8000 },
+{ 0x7101, "Mobility Radeon X1800 XT M58", 8800 },
+{ 0x7102, "Mobility Radeon X1800 M58", 5400 },
+{ 0x7103, "Mobility FireGL V7200 M58GL", 5400 },
+{ 0x7104, "FireGL V7200 R520GL", 9600 },
+{ 0x7105, "FireGL V5300 R520GL", 2800 },
+{ 0x7106, "Mobility FireGL V7100 M58GL", 5400 },
+{ 0x7108, "Radeon X1800 Series R520", 8000 },
+{ 0x7109, "Radeon X1800 Series R520", 8000 },
+{ 0x710A, "Radeon X1800 Series R520", 8000 },
+{ 0x710B, "Radeon X1800 Series R520", 8000 },
+{ 0x710C, "Radeon X1800 Series R520", 8000 },
+{ 0x710E, "FireGL V7300 R520GL", 9600 },
+{ 0x710F, "FireGL V7350 R520GL", 9600 },
+{ 0x7120, "Radeon X1800 Series R520", 8000 },
+{ 0x7124, "FireGL V7200 R520GL", 9600 },
+{ 0x7125, "FireGL V5300 R520GL", 2800 },
+{ 0x7128, "Radeon X1800 Series R520", 8000 },
+{ 0x7129, "Radeon X1800 Series R520", 8000 },
+{ 0x712A, "Radeon X1800 Series R520", 8000 },
+{ 0x712B, "Radeon X1800 Series R520", 8000 },
+{ 0x712C, "Radeon X1800 Series R520", 8000 },
+{ 0x712E, "FireGL V7300 R520GL", 9600 },
+{ 0x712F, "FireGL V7350 R520GL", 9600 },
+{ 0x7140, "Radeon X1600 Series RV515", 1800 },
+{ 0x7142, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7143, "Radeon X1550 Series RV515", 1800 },
+{ 0x7145, "Mobility Radeon X1400 M54", 1780 },
+{ 0x7146, "Radeon X1300 / X1550 Series RV515", 1800 },
+{ 0x7147, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x7149, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714A, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714B, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714C, "Mobility Radeon X1300 M52", 1400 },
+{ 0x714D, "Radeon X1300 Series RV515", 1800 },
+{ 0x714E, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x7152, "FireGL V3300 RV515GL", 1800 },
+{ 0x7153, "FireGL V3350 RV515GL", 1800 },
+{ 0x715E, "Radeon X1300 Series RV515", 1800 },
+{ 0x715F, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x7160, "Radeon X1600 Series RV515", 1800 },
+{ 0x7162, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7163, "Radeon X1550 Series RV515", 1800 },
+{ 0x7166, "Radeon X1300 / X1550 Series RV515", 1800 },
+{ 0x7167, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x716D, "Radeon X1300 Series RV515", 1800 },
+{ 0x716E, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x7172, "FireGL V3300 RV515GL", 1800 },
+{ 0x7173, "FireGL V3350 RV515GL", 1800 },
+{ 0x717E, "Radeon X1300 Series RV515", 1800 },
+{ 0x717F, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x7180, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7181, "Radeon X1600 Series RV515", 1800 },
+{ 0x7183, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7186, "Mobility Radeon X1450 M54", 1800 },
+{ 0x7187, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x7188, "Mobility Radeon X2300 M54", 1920 },
+{ 0x718A, "Mobility Radeon X2300 M54", 1920 },
+{ 0x718B, "Mobility Radeon X1350 M52", 1880 },
+{ 0x718C, "Mobility Radeon X1350 M52", 1880 },
+{ 0x718D, "Mobility Radeon X1450 M54", 2200 },
+{ 0x718F, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x7193, "Radeon X1550 Series RV515", 1800 },
+{ 0x7196, "Mobility Radeon X1350 M52", 1800 },
+{ 0x719B, "FireMV 2250 RV515", 1800 },
+{ 0x719F, "Radeon X1550 64-bit RV515", 1800 },
+{ 0x71A0, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x71A1, "Radeon X1600 Series RV515", 1800 },
+{ 0x71A3, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x71A7, "Radeon X1300/X1550 Series RV515", 1800 },
+{ 0x71AF, "Radeon X1300 Series RV515PCI", 1800 },
+{ 0x71B3, "Radeon X1550 Series RV515", 1800 },
+{ 0x71BB, "FireMV 2250 RV515", 1800 },
+{ 0x71C0, "Radeon X1600 Series RV530", 2000 },
+{ 0x71C1, "Radeon X1650 Series RV535", 2000 },
+{ 0x71C2, "Radeon X1600 Series RV530", 2000 },
+{ 0x71C3, "Radeon X1300 Series RV535", 2000 },
+{ 0x71C4, "Mobility FireGL V5200 M56GL", 1800 },
+{ 0x71C5, "Mobility Radeon X1600 M56", 1800 },
+{ 0x71C6, "Radeon X1650 Series RV530", 2000 },
+{ 0x71C7, "Radeon X1650 Series RV535", 2000 },
+{ 0x71CD, "Radeon X1600 Series RV530", 2000 },
+{ 0x71CE, "Radeon X1600 Pro / Radeon X1300 XT RV530", 2000 },
+{ 0x71D2, "FireGL V3400 RV530GL", 2000 },
+{ 0x71D4, "Mobility FireGL V5250 M56GL", 1800 },
+{ 0x71D5, "Mobility Radeon X1700 M56", 1800 },
+{ 0x71D6, "Mobility Radeon X1700 XT M56", 1800 },
+{ 0x71DA, "FireGL V5200 RV530GL", 2000 },
+{ 0x71DE, "Mobility Radeon X1700 M56", 1800 },
+{ 0x71E0, "Radeon X1600 Series RV530", 2000 },
+{ 0x71E1, "Radeon X1650 Series RV535", 2000 },
+{ 0x71E2, "Radeon X1600 Series RV530", 2000 },
+{ 0x71E3, "Radeon X1300 Series RV535", 2000 },
+{ 0x71E6, "Radeon X1600 Series RV530", 2000 },
+{ 0x71E7, "Radeon X1650 Series RV535", 2000 },
+{ 0x71ED, "Radeon X1600 Series RV530", 2000 },
+{ 0x71EE, "Radeon X1600 Pro / Radeon X1300 XT RV530", 2000 },
+{ 0x71F2, "FireGL V3400 RV530GL", 2000 },
+{ 0x71FA, "FireGL V5200 RV530GL", 2000 },
+{ 0x7210, "Mobility Radeon HD 2300 M71", 1920 },
+{ 0x7211, "Mobility Radeon HD 2300 M71", 1920 },
+{ 0x7240, "Radeon X1950 Series R580", 6900 },
+{ 0x7243, "Radeon X1900 Series R580", 6900 },
+{ 0x7244, "Radeon X1950 Series R580", 6900 },
+{ 0x7245, "Radeon X1900 Series R580", 6900 },
+{ 0x7246, "Radeon X1900 Series R580", 6900 },
+{ 0x7247, "Radeon X1900 Series R580", 6900 },
+{ 0x7248, "Radeon X1900 Series R580", 6900 },
+{ 0x7249, "Radeon X1900 Series R580", 6900 },
+{ 0x724A, "Radeon X1900 Series R580", 6900 },
+{ 0x724B, "Radeon X1900 Series R580", 6900 },
+{ 0x724C, "Radeon X1900 Series R580", 6900 },
+{ 0x724D, "Radeon X1900 Series R580", 6900 },
+{ 0x724E, "FireStream 2U R580", 6900 },
+{ 0x724F, "Radeon X1900 Series R580", 6900 },
+{ 0x7260, "Radeon X1950 Series R580", 6900 },
+{ 0x7263, "Radeon X1900 Series R580", 6900 },
+{ 0x7264, "Radeon X1950 Series R580", 6900 },
+{ 0x7265, "Radeon X1900 Series R580", 6900 },
+{ 0x7266, "Radeon X1900 Series R580", 6900 },
+{ 0x7267, "Radeon X1900 Series R580", 6900 },
+{ 0x7268, "Radeon X1900 Series R580", 6900 },
+{ 0x7269, "Radeon X1900 Series R580", 6900 },
+{ 0x726A, "Radeon X1900 Series R580", 6900 },
+{ 0x726B, "Radeon X1900 Series R580", 6900 },
+{ 0x726C, "Radeon X1900 Series R580", 6900 },
+{ 0x726D, "Radeon X1900 Series R580", 6900 },
+{ 0x726E, "FireStream 2U R580", 6900 },
+{ 0x726F, "Radeon X1900 Series R580", 6900 },
+{ 0x7280, "Radeon X1950 Series R580", 6900 },
+{ 0x7284, "Mobility Radeon X1900 M58", 5400 },
+{ 0x7288, "Radeon X1950 GT R580", 6000 },
+{ 0x7291, "Radeon X1650 Series R580", 2000 },
+{ 0x7293, "Radeon X1650 Series R580", 2000 },
+{ 0x72A0, "Radeon X1950 Series R580", 6900 },
+{ 0x72A8, "Radeon X1950 GT R580", 6000 },
+{ 0x72B1, "Radeon X1650 Series R580", 2000 },
+{ 0x72B3, "Radeon X1650 Series R580", 2000 },
+{ 0x7835, "Mobility Radeon 9000 IGP", 1000 },
+{ 0x791E, "Radeon X1200 Series RS690", 700 },
+{ 0x791F, "Radeon X1200 Series RS690M", 700 },
+{ 0x793F, "Radeon Xpress 1200 Series RS600", 1000 },
+{ 0x7941, "Radeon Xpress 1200 Series RS600", 1000 },
+{ 0x7942, "Radeon Xpress 1200 Series RS600M", 1000 },
+{ 0x796E, "Radeon 2100 RS690", 1000 },
+{ 0x9400, "Radeon HD 2900 XT R600", 11900 },
+{ 0x9401, "Radeon HD 2900 XT R600", 11900 },
+{ 0x9402, "Radeon HD 2900 XT R600", 11900 },
+{ 0x9403, "Radeon HD 2900 PRO R600", 9600 },
+{ 0x9405, "Radeon HD 2900 GT R600", 7200 },
+{ 0x940A, "FireGL V8650 R600GL", 11008 },
+{ 0x940B, "FireGL V8600 R600GL", 11008 },
+{ 0x940F, "FireGL V7600 R600GL", 9600 },
+{ 0x9440, "Radeon HD 4870 RV770", 12000 },
+{ 0x9441, "Radeon HD 4870 X2 RV700", 16000 }, // 2xGPU, assume 1.5x perf
+{ 0x9442, "Radeon HD 4850 RV770", 10000 },
+{ 0x9443, "Radeon HD 4850 X2 RV700", 15000 }, // 2xGPU, assume 1.5x perf
+{ 0x9444, "FirePro V8750", 12000 },
+{ 0x944A, "Radeon HD 4800 Series M98", 8000 },
+{ 0x944B, "Radeon HD 4850 X2 M98", 1200 }, // 2xGPU, assume 1.5x perf
+{ 0x944C, "Radeon HD 4830 RV770", 9200 },
+{ 0x944E, "Radeon HD 4700 Series RV770", 6000 },
+{ 0x9450, "AMD FireStream 9270 RV770", 6000 },
+{ 0x9452, "AMD FireStream 9250 RV770", 6000 },
+{ 0x9456, "FirePro V8700", 12000 },
+{ 0x945A, "Radeon HD 4870 M98", 8800 },
+{ 0x9460, "Radeon HD 4800 Series RV790", 11200 },
+{ 0x9462, "Radeon HD 4800 Series RV790", 11200 },
+{ 0x9490, "Radeon HD 4600 Series RV730", 5200 },
+{ 0x9495, "Radeon HD 4600 Series RV730", 5200 },
+{ 0x9480, "Radeon HD 4650 M96", 4000 },
+{ 0x9488, "Radeon HD 4670 M96", 5400 },
+{ 0x9498, "Radeon HD 4600 Series RV730", 5200 },
+{ 0x949C, "FirePro V7750", 6400 },
+{ 0x949E, "FirePro V5700", 2400 },
+{ 0x949F, "FirePro V3750", 4400 },
+{ 0x94A0, "Radeon Graphics Processor M97", 6400 },
+{ 0x94A1, "Radeon Graphics Processor M97", 6400 },
+{ 0x94A3, "FirePro M7740", 10400 },
+{ 0x94B3, "Radeon HD 4770 RV740", 12000 },
+{ 0x94B4, "Radeon HD 4700 Series RV740", 12000 },
+{ 0x94B5, "Radeon HD 4770 RV740", 12000 },
+{ 0x94C1, "Radeon HD 2400 XT RV610", 2800 },
+{ 0x94C3, "Radeon HD 2400 Series RV610", 2100 },
+{ 0x94C4, "Radeon HD 2400 PRO AGP RV610", 2400 },
+{ 0x94C5, "Radeon HD 2400 LE RV610", 2100 },
+{ 0x94C7, "Radeon HD 2350 RV610", 2100 },
+{ 0x94C8, "Mobility Radeon HD 2400 XT M72", 2400 },
+{ 0x94C9, "Mobility Radeon HD 2400 M72", 1800 },
+{ 0x94CB, "Radeon E2400 M72", 1800 },
+{ 0x94CC, "Radeon HD 2400 Series RV610", 2100 },
+{ 0x9501, "Radeon HD 3690/3870/4750 RV630", 10700 },
+{ 0x9504, "Mobility Radeon HD 3850 M76", 9280 },
+{ 0x9505, "Radeon HD 3850/4750 RV630", 10700 },
+{ 0x9506, "Mobility Radeon HD 3850 X2 M76", 13000 }, // 2xGPU, assume 1.5x perf
+{ 0x9507, "Radeon HD 3830 RV630", 10700 },
+{ 0x9508, "Mobility Radeon HD 3870 M76", 10560 },
+{ 0x9509, "Mobility Radeon HD 3870 X2 M76", 15000 }, // 2xGPU, assume 1.5x perf
+{ 0x950F, "Radeon HD 3870 X2 RV630", 18000 }, // 2xGPU, assume 1.5x perf
+{ 0x9511, "FireGL V7700 RV630GL", 10700 },
+{ 0x9513, "Radeon HD 3850 X2 RV630", 15000 }, // 2xGPU, assume 1.5x perf
+{ 0x9515, "Radeon HD 3850", 10000 },
+{ 0x9519, "AMD FireStream 9170 RV630", 10700 },
+{ 0x9540, "Radeon HD 4590 R730", 4600 },
+{ 0x954F, "Radeon HD 4300/4500 Series R710", 2300 },
+{ 0x9552, "Radeon HD 4300 Series M92", 2000 },
+{ 0x9553, "Radeon HD 4500 Series M92", 2200 },
+{ 0x9555, "Radeon Graphics Processor M93", 2300 },
+{ 0x9581, "Mobility Radeon HD 2600 M76", 2000 },
+{ 0x9583, "Mobility Radeon HD 2600 XT M76", 2720 },
+{ 0x9586, "Radeon HD 2600 XT AGP RV630", 3200 },
+{ 0x9587, "Radeon HD 2600 Pro AGP RV630", 2400 },
+{ 0x9588, "Radeon HD 2600 XT RV630", 3200 },
+{ 0x9589, "Radeon HD 2600 PRO / 3610 RV630", 2400 },
+{ 0x958A, "Radeon HD 2600 X2 Series RV630", 3200 }, // 2xGPU, assume 1.5x perf
+{ 0x9593, "Mobility Radeon HD 3670", 2720 },
+{ 0x958B, "Mobility Radeon HD 2600 XT Gemini M76", 2720 },
+{ 0x958C, "FireGL V5600 RV630GL", 2000 },
+{ 0x958D, "FireGL V3600 RV630GL", 2000 },
+{ 0x958E, "Radeon HD 2600 LE RV630", 2100 },
+{ 0x9590, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x9591, "Radeon HD 3600 Series M76", 2900 },
+{ 0x9593, "Mobility Radeon HD 3670", 2720 },
+{ 0x9596, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x9597, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x9598, "Radeon HD 3600/3730/4570/4610 RV630", 2900 },
+{ 0x9599, "Radeon HD 3600 Series RV630", 2900 },
+{ 0x95C0, "Radeon HD 3400/3570/4250 RV610", 2400 },
+{ 0x95C2, "Mobility Radeon HD 3430 M72", 1800 },
+{ 0x95C4, "Mobility Radeon HD 3400 Series M72", 1800 },
+{ 0x95C5, "Radeon HD 3400/3550/4250 RV610", 2400 },
+{ 0x95C6, "Radeon HD 3450", 2400 },
+{ 0x95C7, "Radeon HD 3430 RV610", 1600 },
+{ 0x95CC, "FirePro V3700", 3200 },
+{ 0x95CD, "FireMV 2450 RV610", 2000 },
+{ 0x95CE, "FireMV 2260 RV610", 1600 },
+{ 0x95CF, "FireMV 2260 / FirePro 2260", 1600 },
+{ 0x9610, "Radeon HD 3200 Graphics RS780", 2000 },
+{ 0x9611, "Radeon 3100 Graphics RS780", 1400 },
+{ 0x9612, "Radeon HD 3200 Graphics RS780M", 2000 },
+{ 0x9613, "Radeon 3100 Graphics RS780M", 1200 },
+{ 0x9614, "Radeon HD 3300 Graphics RS780", 2000 },
+{ 0x9616, "AMD 760G RS780", 1600 },
+{ 0x9640, "Radeon 6550D", 4800 },
+{ 0x9641, "Radeon 6620G", 3550 },
+{ 0x9642, "Radeon 6370D", 1770 },
+{ 0x9643, "Radeon 6380G", 1600 },
+{ 0x9644, "Radeon 6410D", 2400 },
+{ 0x9645, "Radeon 6410D", 2400 },
+{ 0x9647, "Radeon 6520G", 3200 },
+{ 0x9648, "Radeon 6480G", 1770 },
+{ 0x9649, "Radeon 6480G", 1770 },
+{ 0x964A, "Radeon 6530D", 3540 },
+{ 0x9710, "Radeon HD 4200 RS880", 2000 },
+{ 0x9712, "Mobility Radeon HD 4200 M880G", 2000 },
+{ 0x9713, "Mobility Radeon HD 4100 M860G", 1000 },
+{ 0x9714, "Radeon HD 4290 RS880", 2800 },
+{ 0x9715, "Radeon HD 4250 RS880", 2240 },
+{ 0x9802, "Radeon HD 6300 Series", 2000 },
+{ 0x9803, "Radeon HD 6300 Series", 2000 },
+{ 0x9804, "Radeon HD 6200 Series", 1120 },
+{ 0x9805, "Radeon HD 6200 Series", 1120 },
+{ 0x9806, "Radeon HD 6320", 2000 },
+{ 0x9807, "Radeon HD 6290", 1120 },
+{ 0x9809, "Radeon HD 7310", 2000 },
+{ 0x9900, "Radeon HD 7660G", 4800 },
+{ 0x9901, "Radeon HD 7660D", 6000 },
+{ 0x9903, "Radeon HD 7640G", 4000 },
+{ 0x9904, "Radeon HD 7560D", 6000 },
+{ 0x9990, "Radeon HD 7520G", 3800 },
+{ 0, 0, 0 },
+};
+
+
+static const GraphicsDeviceDesc kNVIDIADevices[] = {
+{ 0x0020, "Riva TNT", 180 },
+{ 0x0028, "Riva TNT2/TNT2 Pro", 286 },
+{ 0x0029, "Riva TNT2 Ultra", 300 },
+{ 0x002C, "Vanta/Vanta LT", 160 },
+{ 0x002D, "Riva TNT2 Model 64/Model 64 Pro", 250 },
+{ 0x0040, "GeForce 6800 Ultra", 6400 },
+{ 0x0041, "GeForce 6800", 3200 },
+{ 0x0042, "GeForce 6800 LE", 2600 },
+{ 0x0043, "GeForce 6800 XE", 2600 },
+{ 0x0044, "GeForce 6800 XT", 2600 },
+{ 0x0045, "GeForce 6800 GT", 5600 },
+{ 0x0046, "GeForce 6800 GT", 5600 },
+{ 0x0047, "GeForce 6800 GS", 3600 },
+{ 0x0048, "GeForce 6800 XT", 2600 },
+{ 0x0049, "NV40GL", 3000 },
+{ 0x004D, "Quadro FX 3400", 5600 },
+{ 0x004E, "Quadro FX 4000", 3000 },
+{ 0x0090, "GeForce 7800 GTX", 6880 },
+{ 0x0091, "GeForce 7800 GTX", 6880 },
+{ 0x0092, "GeForce 7800 GT", 6400 },
+{ 0x0093, "GeForce 7800 GS", 3000 },
+{ 0x0095, "GeForce 7800 SLI", 7200 }, // 2xGPU, assume 1.5x perf
+{ 0x0098, "GeForce Go 7800", 3200 },
+{ 0x0099, "GeForce Go 7800 GTX", 6400 },
+{ 0x009D, "Quadro FX 4500", 7520 },
+{ 0x00A0, "Aladdin TNT2", 286 },
+{ 0x00C0, "GeForce 6800 GS", 3600 },
+{ 0x00C1, "GeForce 6800", 3200 },
+{ 0x00C2, "GeForce 6800 LE", 2600 },
+{ 0x00C3, "GeForce 6800 XT", 2600 },
+{ 0x00C8, "GeForce Go 6800", 3600 },
+{ 0x00C9, "GeForce Go 6800 Ultra", 5400 },
+{ 0x00CC, "Quadro FX Go1400", 2000 },
+{ 0x00CD, "Quadro FX 3450/4000 SDI", 3400 },
+{ 0x00CE, "Quadro FX 1400", 2800 },
+{ 0x00F0, "GeForce 6800", 3200 },
+{ 0x00F1, "GeForce 6600 GT", 2000 },
+{ 0x00F2, "GeForce 6600", 1200 },
+{ 0x00F3, "GeForce 6200", 600 },
+{ 0x00F4, "GeForce 6600 LE", 1200 },
+{ 0x00F5, "GeForce 7800 GS", 3000 },
+{ 0x00F6, "GeForce 6800 GS/XT", 2800 },
+{ 0x00F8, "Quadro FX 3400/4400", 5600 },
+{ 0x00F9, "GeForce 6800 Series GPU", 3200 },
+{ 0x00FA, "GeForce PCX 5750", 1700 },
+{ 0x00FB, "GeForce PCX 5900", 3400 },
+{ 0x00FC, "GeForce PCX 5300", 1000 },
+{ 0x00FD, "Quadro NVS 280 PCI-E", -1 },
+{ 0x00FE, "Quadro FX 1300", 2800 },
+{ 0x00FF, "GeForce PCX 4300", -1 },
+{ 0x0100, "GeForce 256", 480 },
+{ 0x0101, "GeForce DDR", 480 },
+{ 0x0103, "Quadro", -1 },
+{ 0x0110, "GeForce2 MX/MX 400", 400 },
+{ 0x0111, "GeForce2 MX 100/200", 300 },
+{ 0x0112, "GeForce2 Go", -1 },
+{ 0x0140, "GeForce 6600 GT", 2000 },
+{ 0x0141, "GeForce 6600", 1200 },
+{ 0x0142, "GeForce 6600 LE", 1200 },
+{ 0x0143, "GeForce 6600 VE", 1200 },
+{ 0x0144, "GeForce Go 6600", 3000 },
+{ 0x0145, "GeForce 6610 XL", 2100 },
+{ 0x0146, "GeForce Go 6600 TE/6200 TE", 1600 },
+{ 0x0147, "GeForce 6700 XL", 2100 },
+{ 0x0148, "GeForce Go 6600", 3000 },
+{ 0x0149, "GeForce Go 6600 GT", 3000 },
+{ 0x014A, "Quadro NVS 440", -1 },
+{ 0x014B, "NV43", -1 },
+{ 0x014C, "Quadro FX 540M", -1 },
+{ 0x014D, "Quadro FX 550", 2880 },
+{ 0x014E, "Quadro FX 540", 2400 },
+{ 0x014F, "GeForce 6200", 600 },
+{ 0x0150, "GeForce2 GTS/GeForce2 Pro", 800 },
+{ 0x0151, "GeForce2 Ti", 1000 },
+{ 0x0152, "GeForce2 Ultra", 1000 },
+{ 0x0153, "Quadro2 Pro", -1 },
+{ 0x0160, "GeForce 6500", 1400 },
+{ 0x0161, "GeForce 6200 TurboCache", 700 },
+{ 0x0162, "GeForce 6200SE TurboCache", 700 },
+{ 0x0163, "GeForce 6200 LE", 350 },
+{ 0x0164, "GeForce Go 6200", 1200 },
+{ 0x0165, "Quadro NVS 285", 700 },
+{ 0x0166, "GeForce Go 6400", 1600 },
+{ 0x0167, "GeForce Go 6200", 1200 },
+{ 0x0168, "GeForce Go 6400", 1600 },
+{ 0x0169, "GeForce 6250", 700 },
+{ 0x016A, "GeForce 7100 GS", 700 },
+{ 0x016B, "NV44GLM", -1 },
+{ 0x016C, "NV44GLM", -1 },
+{ 0x016D, "NV44GLM", -1 },
+{ 0x016E, "NV44GL", -1 },
+{ 0x0170, "GeForce4 MX 460", 600 },
+{ 0x0171, "GeForce4 MX 440", 550 },
+{ 0x0172, "GeForce4 MX 420", 500 },
+{ 0x0173, "GeForce4 MX 440-SE", 550 },
+{ 0x0174, "GeForce4 440 Go", -1 },
+{ 0x0175, "GeForce4 420 Go", -1 },
+{ 0x0176, "GeForce4 420 Go 32M", -1 },
+{ 0x0177, "GeForce4 460 Go", -1 },
+{ 0x0178, "Quadro4 550 XGL", -1 },
+{ 0x0179, "GeForce4 440 Go 64M", -1 },
+{ 0x017A, "Quadro NVS", -1 },
+{ 0x017C, "Quadro4 500 GoGL", -1 },
+{ 0x017D, "GeForce4 410 Go 16M", -1 },
+{ 0x0181, "GeForce4 MX 440 with AGP8X", 550 },
+{ 0x0182, "GeForce4 MX 440SE with AGP8X", 550 },
+{ 0x0183, "GeForce4 MX 420 with AGP8X", 500 },
+{ 0x0185, "GeForce4 MX 4000", 550 },
+{ 0x0186, "GeForce4 448 Go", -1 },
+{ 0x0187, "GeForce4 488 Go", -1 },
+{ 0x0188, "Quadro4 580 XGL", -1 },
+{ 0x018A, "Quadro NVS with AGP8X", -1 },
+{ 0x018B, "Quadro4 380 XGL", -1 },
+{ 0x018C, "Quadro NVS 50 PCI", -1 },
+{ 0x018D, "GeForce4 448 Go", -1 },
+{ 0x0191, "GeForce 8800 GTX", 13800 },
+{ 0x0193, "GeForce 8800 GTS", 10400 },
+{ 0x0194, "GeForce 8800 Ultra", 14688 },
+{ 0x0197, "Tesla C870", -1 },
+{ 0x019D, "Quadro FX 5600", 14400 },
+{ 0x019E, "Quadro FX 4600", 12000 },
+{ 0x01A0, "GeForce2 Integrated GPU", -1 },
+{ 0x01D0, "GeForce 7350 LE", 1100 },
+{ 0x01D1, "GeForce 7300 LE", 900 },
+{ 0x01D2, "GeForce 7550 LE", 1200 },
+{ 0x01D3, "GeForce 7300 SE", 900 },
+{ 0x01D4, "GeForce Go 7350", 800 },
+{ 0x01D6, "GeForce Go 7200", 450 },
+{ 0x01D7, "GeForce Go 7300", 700 },
+{ 0x01D8, "GeForce Go 7400", 900 },
+{ 0x01D9, "GeForce Go 7450", 1000 },
+{ 0x01DA, "Quadro NVS 110M", 600 },
+{ 0x01DB, "Quadro NVS 120M", 900 },
+{ 0x01DC, "Quadro FX 350M", 900 },
+{ 0x01DD, "GeForce 7500 LE", 1100 },
+{ 0x01DE, "Quadro FX 350", -1 },
+{ 0x01DF, "GeForce 7300 GS", 1100 },
+{ 0x01F0, "GeForce4 MX Integrated GPU", -1 },
+{ 0x0200, "GeForce3", 800 },
+{ 0x0201, "GeForce3 Ti 200", 700 },
+{ 0x0202, "GeForce3 Ti 500", 960 },
+{ 0x0203, "Quadro DCC", -1 },
+{ 0x0211, "GeForce 6800", 3200 },
+{ 0x0212, "GeForce 6800 LE", 2600 },
+{ 0x0215, "GeForce 6800 GT", 5600 },
+{ 0x0218, "GeForce 6800 XT", 2600 },
+{ 0x0220, "NV44", -1 },
+{ 0x0221, "GeForce 6200", 600 },
+{ 0x0222, "GeForce 6200 A-LE", 350 },
+{ 0x0228, "NV44M", -1 },
+{ 0x0240, "GeForce 6150", 475 },
+{ 0x0241, "GeForce 6150 LE", 425 },
+{ 0x0242, "GeForce 6100", 425 },
+{ 0x0244, "GeForce Go 6150", 425 },
+{ 0x0245, "Quadro NVS 210S / GeForce 6150LE", 425 },
+{ 0x0247, "GeForce Go 6100", 425 },
+{ 0x0250, "GeForce4 Ti 4600", 1200 },
+{ 0x0251, "GeForce4 Ti 4400", 1100 },
+{ 0x0252, "NV25", 2200 },
+{ 0x0253, "GeForce4 Ti 4200", 1000 },
+{ 0x0258, "Quadro4 900 XGL", 2400 },
+{ 0x0259, "Quadro4 750 XGL", 2200 },
+{ 0x025B, "Quadro4 700 XGL", 2200 },
+{ 0x0280, "GeForce4 Ti 4800", 1200 },
+{ 0x0281, "GeForce4 Ti 4200 with AGP8X", 1000 },
+{ 0x0282, "GeForce4 Ti 4800 SE", 1100 },
+{ 0x0286, "GeForce4 4200 Go", -1 },
+{ 0x0288, "Quadro4 980 XGL", 2400 },
+{ 0x0289, "Quadro4 780 XGL", -1 },
+{ 0x028C, "Quadro4 700 GoGL", -1 },
+{ 0x0290, "GeForce 7900 GTX", 10400 },
+{ 0x0291, "GeForce 7900 GT/GTO", 7200 },
+{ 0x0292, "GeForce 7900 GS", 7200 },
+{ 0x0293, "GeForce 7950 GX2", 16000 },
+{ 0x0294, "GeForce 7950 GX2", 16000 },
+{ 0x0295, "GeForce 7950 GT", 8800 },
+{ 0x0297, "GeForce Go 7950 GTX", 9200 },
+{ 0x0298, "GeForce Go 7900 GS", 6000 },
+{ 0x0299, "GeForce Go 7900 GTX", 8000 },
+{ 0x029A, "Quadro FX 2500M", 8000 },
+{ 0x029B, "Quadro FX 1500M", 6000 },
+{ 0x029C, "Quadro FX 5500", 11200 },
+{ 0x029D, "Quadro FX 3500", 7520 },
+{ 0x029E, "Quadro FX 1500", 6000 },
+{ 0x029F, "Quadro FX 4500 X2", 15040 },
+{ 0x02E0, "GeForce 7600 GT", 4480 },
+{ 0x02E1, "GeForce 7600 GS", 3200 },
+{ 0x02E2, "GeForce 7300 GT", 2800 },
+{ 0x02E3, "GeForce 7900 GS", 7200 },
+{ 0x02E4, "GeForce 7950 GT", 8800 },
+{ 0x0301, "GeForce FX 5800 Ultra", 4000 },
+{ 0x0302, "GeForce FX 5800", 3200 },
+{ 0x0308, "Quadro FX 2000", -1 },
+{ 0x0309, "Quadro FX 1000", -1 },
+{ 0x0311, "GeForce FX 5600 Ultra", 1400 },
+{ 0x0312, "GeForce FX 5600", 1300 },
+{ 0x0313, "NV31", -1 },
+{ 0x0314, "GeForce FX 5600XT", 940 },
+{ 0x0316, "NV31M", 1400 },
+{ 0x0317, "NV31M Pro", 1400 },
+{ 0x031A, "GeForce FX Go5600", 1400 },
+{ 0x031B, "GeForce FX Go5650", 1400 },
+{ 0x031C, "Quadro FX Go700", -1 },
+{ 0x031D, "NV31GLM", -1 },
+{ 0x031E, "NV31GLM Pro", -1 },
+{ 0x031F, "NV31GLM Pro", -1 },
+{ 0x0320, "GeForce FX 5200", 1000 },
+{ 0x0321, "GeForce FX 5200 Ultra", 1300 },
+{ 0x0322, "GeForce FX 5200", 1000 },
+{ 0x0323, "GeForce FX 5200LE", 800 },
+{ 0x0324, "GeForce FX Go5200", 1200 },
+{ 0x0325, "GeForce FX Go5250", 1200 },
+{ 0x0326, "GeForce FX 5500", 1080 },
+{ 0x0327, "GeForce FX 5100", 1000 },
+{ 0x0328, "GeForce FX Go5200 32M/64M", 1000 },
+{ 0x0329, "NV34MAP", 1800 },
+{ 0x032A, "Quadro NVS 280 PCI", -1 },
+{ 0x032B, "Quadro FX 500/FX 600", 1000 },
+{ 0x032C, "GeForce FX Go53xx", 1200 },
+{ 0x032D, "GeForce FX Go5100", 1200 },
+{ 0x032F, "NV34GL", 1000 },
+{ 0x0330, "GeForce FX 5900 Ultra", 3600 },
+{ 0x0331, "GeForce FX 5900", 3200 },
+{ 0x0332, "GeForce FX 5900XT", 3200 },
+{ 0x0333, "GeForce FX 5950 Ultra", 3800 },
+{ 0x0334, "GeForce FX 5900ZT", -1 },
+{ 0x0338, "Quadro FX 3000", -1 },
+{ 0x033F, "Quadro FX 700", 1100 },
+{ 0x0341, "GeForce FX 5700 Ultra", 1900 },
+{ 0x0342, "GeForce FX 5700", 1700 },
+{ 0x0343, "GeForce FX 5700LE", 1000 },
+{ 0x0344, "GeForce FX 5700VE", 1200 },
+{ 0x0345, "NV36", -1 },
+{ 0x0347, "GeForce FX Go5700", 1800 },
+{ 0x0348, "GeForce FX Go5700", 1800 },
+{ 0x0349, "NV36M Pro", 1800 },
+{ 0x034B, "NV36MAP", 1800 },
+{ 0x034C, "Quadro FX Go1000", -1 },
+{ 0x034E, "Quadro FX 1100", 1700 },
+{ 0x034F, "NV36GL", 1700 },
+{ 0x038B, "GeForce 7650 GS", 3600 },
+{ 0x0390, "GeForce 7650 GS", 3600 },
+{ 0x0391, "GeForce 7600 GT", 4480 },
+{ 0x0392, "GeForce 7600 GS", 3200 },
+{ 0x0393, "GeForce 7300 GT", 2800 },
+{ 0x0394, "GeForce 7600 LE", 2800 },
+{ 0x0395, "GeForce 7300 GT", 2800 },
+{ 0x0397, "GeForce Go 7700", 3600 },
+{ 0x0398, "GeForce Go 7600", 3600 },
+{ 0x0399, "GeForce Go 7600 GT", 4000 },
+{ 0x039A, "Quadro NVS 300M", 900 },
+{ 0x039F, "GeForce GTX 260", 16128 },
+{ 0x039B, "GeForce Go 7900 SE", 3600 },
+{ 0x039C, "Quadro FX 560M", -1 },
+{ 0x039E, "Quadro FX 560", 2800 },
+{ 0x03D0, "GeForce 6150SE nForce 430", 425 },
+{ 0x03D1, "GeForce 6100 nForce 405", 425 },
+{ 0x03D2, "GeForce 6100 nForce 400", 425 },
+{ 0x03D5, "GeForce 6100 nForce 420", 425 },
+{ 0x03D6, "GeForce 7025 / nForce 630a", 850 },
+{ 0x0400, "GeForce 8600 GTS", 5400 },
+{ 0x0401, "GeForce 8600 GT", 4320 },
+{ 0x0402, "GeForce 8600 GT", 4320 },
+{ 0x0403, "GeForce 8600 GS", 4320 },
+{ 0x0404, "GeForce 8400 GS", 1800 },
+{ 0x0405, "GeForce 9500M GS", 3800 },
+{ 0x0406, "GeForce 8300 GS", 1800 },
+{ 0x0407, "GeForce 8600M GT", 3800 },
+{ 0x0408, "GeForce 8600M GTS", 3800 },
+{ 0x0409, "GeForce 8700M GT", 5000 },
+{ 0x040A, "Quadro FX 370", 1440 },
+{ 0x040B, "Quadro NVS 320M", 4600 },
+{ 0x040C, "Quadro FX 570M", 3800 },
+{ 0x040D, "Quadro FX 1600M", 5000 },
+{ 0x040E, "Quadro FX 570", 3680 },
+{ 0x040F, "Quadro FX 1700", 3680 },
+{ 0x0410, "GeForce GT 330", 4320 },
+{ 0x0420, "GeForce 8400 SE", 1400 },
+{ 0x0421, "GeForce 8500 GT", 3600 },
+{ 0x0422, "GeForce 8400 GS", 1800 },
+{ 0x0423, "GeForce 8300 GS", 1800 },
+{ 0x0424, "GeForce 8400 GS", 1800 },
+{ 0x0425, "GeForce 8600M GS", 2400 },
+{ 0x0426, "GeForce 8400M GT", 1800 },
+{ 0x0427, "GeForce 8400M GS", 1600 },
+{ 0x0428, "GeForce 8400M G", 1600 },
+{ 0x0429, "Quadro NVS 140M", 1600 },
+{ 0x042A, "Quadro NVS 130M", 1400 },
+{ 0x042B, "Quadro NVS 135M", 1600 },
+{ 0x042C, "GeForce 9400 GT", 2200 },
+{ 0x042D, "Quadro FX 360M", 1600 },
+{ 0x042E, "GeForce 9300M G", 1600 },
+{ 0x042F, "Quadro NVS 290", 1840 },
+{ 0x0530, "GeForce 7190M / nForce 650M", 900 },
+{ 0x0531, "GeForce 7150M / nForce 630M", 850 },
+{ 0x0532, "MCP67M", 750 },
+{ 0x0533, "GeForce 7000M / nForce 610M", 700 },
+{ 0x053A, "GeForce 7050 PV / nForce 630a", 850 },
+{ 0x053B, "GeForce 7050 PV / nForce 630a", 850 },
+{ 0x053E, "GeForce 7025 / nForce 630a", 850 },
+{ 0x053F, "MCP67M", 750 },
+{ 0x05E0, "GeForce GTX 295", 24000 }, // 2xGPU, assume 1.5x perf
+{ 0x05E1, "GeForce GTX 280", 19264 },
+{ 0x05E2, "GeForce GTX 260", 16128 },
+{ 0x05E3, "GeForce GTX 285", 20736 },
+{ 0x05E6, "GeForce GTX 275", 17724 },
+{ 0x05E7, "Tesla C1060/T10", -1 },
+{ 0x05EA, "GeForce GTX 260", 16128 },
+{ 0x05EB, "GeForce GTX 295", 24000 }, // 2xGPU, assume 1.5x perf
+{ 0x05ED, "Quadroplex 2200 D2", -1 },
+{ 0x05F8, "Quadroplex 2200 S4", -1 },
+{ 0x05F9, "Quadro CX", 14448 },
+{ 0x05FD, "Quadro FX 5800", 20736 },
+{ 0x05FE, "Quadro FX 4800", 14448 },
+{ 0x05FF, "Quadro FX 3800", 9632 },
+{ 0x0600, "GeForce 8800 GTS 512", 10400 },
+{ 0x0601, "GeForce 9800 GT", 9600 },
+{ 0x0602, "GeForce 8800 GT", 9600 },
+{ 0x0603, "GeForce GT 230", 8000 },
+{ 0x0604, "GeForce 9800 GX2", 14000 }, // 2xGPU, assume 1.5x of one card
+{ 0x0605, "GeForce 9800 GT", 9600 },
+{ 0x0606, "GeForce 8800 GS", 6600 },
+{ 0x0607, "GeForce GTS 240", 10800 },
+{ 0x0608, "GeForce 9800M GTX", 8000 },
+{ 0x0609, "GeForce 8800M GTS", 8000 },
+{ 0x060A, "GeForce GTX 280M", 9360 },
+{ 0x060B, "GeForce 9800M GT", 8000 },
+{ 0x060C, "GeForce 8800M GTX", 8000 },
+{ 0x060D, "GeForce 8800 GS", 6600 },
+{ 0x060F, "GeForce GTX 285M", 9600 },
+{ 0x0610, "GeForce 9600 GSO", 6600 },
+{ 0x0611, "GeForce 8800 GT", 9600 },
+{ 0x0612, "GeForce 9800 GTX/9800 GTX+", 10800 },
+{ 0x0613, "GeForce 9800 GTX+", 11808 },
+{ 0x0614, "GeForce 9800 GT", 9600 },
+{ 0x0615, "GeForce GTS 250", 11808 },
+{ 0x0617, "GeForce 9800M GTX", 8000 },
+{ 0x0618, "GeForce GTX 260M", 8800 },
+{ 0x0619, "Quadro FX 4700 X2", 12000 }, // 2xGPU, assume 1.5x perf
+{ 0x061A, "Quadro FX 3700", 12720 },
+{ 0x061B, "Quadro VX 200", 12720 },
+{ 0x061C, "Quadro FX 3600M", 8000 },
+{ 0x061D, "Quadro FX 2800M", 8000 },
+{ 0x061E, "Quadro FX 3700M", 8800 },
+{ 0x061F, "Quadro FX 3800M", 10800 },
+{ 0x0621, "GeForce GT 230", 8000 },
+{ 0x0622, "GeForce 9600 GT", 10400 },
+{ 0x0623, "GeForce 9600 GS", 6000 },
+{ 0x0624, "G94", -1 },
+{ 0x0625, "GeForce 9600 GSO 512", 10400 },
+{ 0x0626, "GeForce GT 130", 6000 },
+{ 0x0627, "GeForce GT 140", 10400 },
+{ 0x0628, "GeForce 9800M GTS", 9600 },
+{ 0x062A, "GeForce 9700M GTS", 8480 },
+{ 0x062B, "GeForce 9800M GT", 8000 },
+{ 0x062C, "GeForce 9800M GTS", 8000 },
+{ 0x062D, "GeForce 9600 GT", 10400 },
+{ 0x062E, "GeForce 9600 GT", 10400 },
+{ 0x062F, "GeForce 9800 S", 9600 },
+{ 0x0630, "GeForce 9700 S", 9600 },
+{ 0x0631, "GeForce GTS 160M", 9600 },
+{ 0x0632, "GeForce GTS 150M", 6400 },
+{ 0x0635, "GeForce 9600 GSO", 6600 },
+{ 0x0637, "GeForce 9600 GT", 10400 },
+{ 0x0638, "Quadro FX 1800", 6600 },
+{ 0x063A, "Quadro FX 2700M", 8480 },
+{ 0x0638, "Quadro FX 1800", 6600 },
+{ 0x0640, "GeForce 9500 GT", 4400 },
+{ 0x0641, "GeForce 9400 GT", 2200 },
+{ 0x0643, "GeForce 9500 GT", 4400 },
+{ 0x0644, "GeForce 9500 GS", 4000 },
+{ 0x0645, "GeForce 9500 GS", 4000 },
+{ 0x0646, "GeForce GT 120", 4400 },
+{ 0x0647, "GeForce 9600M GT", 4000 },
+{ 0x0648, "GeForce 9600M GS", 3440 },
+{ 0x0649, "GeForce 9600M GT", 4000 },
+{ 0x064A, "GeForce 9700M GT", 5000 },
+{ 0x064B, "GeForce 9500M G", 4000 },
+{ 0x064C, "GeForce 9650M GT", 4400 },
+{ 0x064E, "GeForce 9800 GT", 9600 },
+{ 0x0650, "G96-825", -1 },
+{ 0x0651, "GeForce G 110M", 1600 },
+{ 0x0652, "GeForce GT 130M", 4800 },
+{ 0x0653, "GeForce GT 120M", 4000 },
+{ 0x0654, "GeForce GT 220M", 4000 },
+{ 0x0655, "GeForce 9600 S / GT 120", 4400 },
+{ 0x0658, "Quadro FX 380", 3600 },
+{ 0x0659, "Quadro FX 580", 3600 },
+{ 0x065A, "Quadro FX 1700M", 5000 },
+{ 0x065B, "GeForce 9400 GT", 2200 },
+{ 0x065C, "Quadro FX 770M", 4000 },
+{ 0x065F, "GeForce G210", 2356 },
+{ 0x06C0, "GeForce GTX 480", 33600 },
+{ 0x06C4, "GeForce GTX 465", 19420 },
+{ 0x06CA, "GeForce GTX 480M", 13600 },
+{ 0x06CD, "GeForce GTX 470", 24280 },
+{ 0x06D1, "Tesla C2050", -1 },
+{ 0x06D2, "Tesla M2070", -1 },
+{ 0x06D8, "Quadro 6000", 27552 },
+{ 0x06D9, "Quadro 5000", 20530 },
+{ 0x06DD, "Quadro 4000", 15200 },
+{ 0x06DE, "Tesla S2050", -1 },
+{ 0x06E0, "GeForce 9300 GE", 2160 },
+{ 0x06E1, "GeForce 9300 GS", 2268 },
+{ 0x06E2, "GeForce 8400", 1800 },
+{ 0x06E3, "GeForce 8400 SE", 1800 },
+{ 0x06E4, "GeForce 8400 GS", 2268 },
+{ 0x06E5, "GeForce 9300M GS", 2200 },
+{ 0x06E6, "GeForce G100", 2150 },
+{ 0x06E7, "GeForce 9300 SE", 1800 },
+{ 0x06E8, "GeForce 9200M GS", 2200 },
+{ 0x06E9, "GeForce 9300M GS", 2200 },
+{ 0x06EA, "Quadro NVS 150M", 2120 },
+{ 0x06EB, "Quadro NVS 160M", 2320 },
+{ 0x06EC, "GeForce G 105M", 2560 },
+{ 0x06ED, "G98-GL", 2200 },
+{ 0x06EF, "GeForce G 103M", 2560 },
+{ 0x06F1, "GeForce G105M", 2560 },
+{ 0x06F8, "Quadro NVS 420", 3300 }, // 2xGPU, 1.5x perf
+{ 0x06F9, "Quadro FX 370 LP", 2160 },
+{ 0x06FA, "Quadro NVS 450", 3300 }, // 2xGPU, 1.5x perf
+{ 0x06FB, "Quadro FX 370M", 2200 },
+{ 0x06FD, "Quadro NVS 295", 2200 },
+{ 0x06FF, "HICx8/16 + Graphics", -1 },
+{ 0x07E0, "GeForce 7150 / nForce 630i", 1260 },
+{ 0x07E1, "GeForce 7100 / nForce 630i", 1200 },
+{ 0x07E2, "GeForce 7050 / nForce 630i", 1000 },
+{ 0x07E3, "GeForce 7050 / nForce 610i", 1000 },
+{ 0x07E5, "GeForce 7050 / nForce 620i", 1000 },
+{ 0x0844, "GeForce 9100M G", 1800 },
+{ 0x0845, "GeForce 8200M G", 1600 },
+{ 0x0846, "GeForce 9200", 2200 },
+{ 0x0847, "GeForce 9100", 2000 },
+{ 0x0848, "GeForce 8300", 2000 },
+{ 0x0849, "GeForce 8200", 2000 },
+{ 0x084A, "nForce 730a", -1 },
+{ 0x084B, "GeForce 9200", 2200 },
+{ 0x084C, "nForce 980a/780a SLI", -1 },
+{ 0x084D, "nForce 750a SLI", -1 },
+{ 0x084F, "GeForce 8100 / nForce 720a", 2000 },
+{ 0x0860, "GeForce 9400", 2320 },
+{ 0x0861, "GeForce 9400", 2320 },
+{ 0x0862, "GeForce 9400M G", 1800 },
+{ 0x0863, "GeForce 9400M", 1800 },
+{ 0x0864, "GeForce 9300", 1800 },
+{ 0x0865, "ION", 1800 },
+{ 0x0866, "GeForce 9400M", 1800 },
+{ 0x0867, "GeForce 9400", 2320 },
+{ 0x0868, "nForce 760i SLI", 2320 },
+{ 0x0869, "GeForce 9400", 2320 },
+{ 0x086A, "GeForce 9400", 2320 },
+{ 0x086C, "GeForce 9300 / nForce 730i", 1800 },
+{ 0x086D, "GeForce 9200", 2200 },
+{ 0x086E, "GeForce 9100M G", 1800 },
+{ 0x086F, "GeForce 8200M G", 1600 },
+{ 0x0870, "GeForce 9400M", 1800 },
+{ 0x0871, "GeForce 9200", 2200 },
+{ 0x0872, "GeForce G102M", 1800 },
+{ 0x0873, "GeForce G102M", 1800 },
+{ 0x0874, "ION", 1800 },
+{ 0x0876, "ION", 1800 },
+{ 0x087A, "GeForce 9400", 2320 },
+{ 0x087D, "ION", 1800 },
+{ 0x087E, "ION LE", 1600 },
+{ 0x087F, "ION LE", 1600 },
+{ 0x08A0, "GeForce 320M", 3600 },
+{ 0x08A1, "MCP89-MZT", -1 },
+{ 0x08A2, "GeForce 320M", 3600 },
+{ 0x08A3, "GeForce 320M", 3600 },
+{ 0x08A4, "GeForce 320M", 3600 },
+{ 0x08B1, "GeForce 300M", 2000 },
+{ 0x08B2, "GeForce 300M", 2000 },
+{ 0x08B3, "MCP89 MM9", -1 },
+{ 0x0A20, "GeForce GT 220", 5000 },
+{ 0x0A21, "D10M2-20", -1 },
+{ 0x0A22, "GeForce 315", 3800 },
+{ 0x0A23, "GeForce 210", 2356 },
+{ 0x0A26, "GeForce 405", 2356 },
+{ 0x0A27, "GeForce 405", 2356 },
+{ 0x0A28, "GeForce GT 230M", 4000 },
+{ 0x0A29, "GeForce GT 330M", 4600 },
+{ 0x0A2A, "GeForce GT 230M", 4000 },
+{ 0x0A2B, "GeForce GT 330M", 4600 },
+{ 0x0A2C, "NVS 5100M", 4400 },
+{ 0x0A2D, "GeForce GT 320M", 4000 },
+{ 0x0A30, "GeForce GT 330M", 4600 },
+{ 0x0A34, "GeForce GT 240M", 4400 },
+{ 0x0A35, "GeForce GT 325M", 3600 },
+{ 0x0A38, "Quadro 400", 3600 },
+{ 0x0A3C, "Quadro FX 880M", 4400 },
+{ 0x0A60, "GeForce G210", 2356 },
+{ 0x0A61, "D10M1-20", -1 },
+{ 0x0A62, "GeForce 205", 2356 },
+{ 0x0A63, "GeForce 310", 2356 },
+{ 0x0A64, "ION", 1800 },
+{ 0x0A65, "GeForce 210", 2356 },
+{ 0x0A66, "GeForce 310", 2356 },
+{ 0x0A67, "GeForce 315", 3800 },
+{ 0x0A68, "GeForce G105M", 2560 },
+{ 0x0A69, "GeForce G105M", 2560 },
+{ 0x0A6A, "NVS 2100M", 2140 },
+{ 0x0A6C, "NVS 3100M", 2400 },
+{ 0x0A6E, "GeForce 305M", 2100 },
+{ 0x0A6F, "ION", 1800 },
+{ 0x0A70, "GeForce 310M", 2500 },
+{ 0x0A71, "GeForce 305M", 2100 },
+{ 0x0A72, "GeForce 310M", 2500 },
+{ 0x0A73, "GeForce 305M", 2100 },
+{ 0x0A74, "GeForce G210M", 2500 },
+{ 0x0A75, "GeForce 310M", 2500 },
+{ 0x0A76, "ION", 1800 },
+{ 0x0A78, "Quadro FX 380 LP", 2356 },
+{ 0x0A7A, "GeForce 315M", 2420 },
+{ 0x0A7C, "Quadro FX 380M", 2500 },
+{ 0x0CA0, "GeForce GT 330", 4320 },
+{ 0x0CA2, "GeForce GT 320", 4320 },
+{ 0x0CA3, "GeForce GT 240", 4400 },
+{ 0x0CA4, "GeForce GT 340", 4400 },
+{ 0x0CA5, "GT 220", 5000 },
+{ 0x0CA7, "GeForce GT 330", 4320 },
+{ 0x0CA8, "GeForce GTS 260M", 4400 },
+{ 0x0CA9, "GeForce GTS 250M", 4000 },
+{ 0x0CAC, "GeForce 315", 3800 },
+{ 0x0CAF, "GeForce GT 335M", 3600 },
+{ 0x0CB0, "GeForce GTS 350M", 4000 },
+{ 0x0CB1, "GeForce GTS 360M", 4400 },
+{ 0x0CBC, "Quadro FX 1800M", 3600 },
+{ 0x0DC0, "GeForce GT 440", 4000 },
+{ 0x0DC4, "GeForce GTS 450", 12530 },
+{ 0x0DC5, "GeForce GTS 450", 12530 },
+{ 0x0DC6, "GeForce GTS 450", 12530 },
+{ 0x0DCD, "GeForce GTS 555M", 10400 },
+{ 0x0DCE, "GeForce GT 555M", 10400 },
+{ 0x0DD1, "GeForce GTX 460M", 16200 },
+{ 0x0DD2, "GeForce GT 445M", 10000 },
+{ 0x0DD3, "GeForce GT 435M", 2600 },
+{ 0x0DD6, "GeForce GT 550M", 4000 },
+{ 0x0DD8, "Quadro 2000", 10000 },
+{ 0x0DDA, "Quadro 2000M", 8800 },
+{ 0x0DE0, "GeForce GT 440", 4000 },
+{ 0x0DE1, "GeForce GT 430", 2800 },
+{ 0x0DE2, "GeForce GT 420", 2800 },
+{ 0x0DE4, "GeForce GT 520", 3240 },
+{ 0x0DE5, "GeForce GT 530", 2800 },
+{ 0x0DE9, "GeForce GT 630M", 3200 },
+{ 0x0DEA, "GeForce 610M", 3600 },
+{ 0x0DEB, "GeForce GT 555M", 10400 },
+{ 0x0DEC, "GeForce GT 525M", 2400 },
+{ 0x0DEE, "GeForce GT 415M", 2000 },
+{ 0x0DF0, "GeForce GT 425M", 2240 },
+{ 0x0DF1, "GeForce GT 420M", 2000 },
+{ 0x0DF2, "GeForce GT 435M", 2600 },
+{ 0x0DF4, "GeForce GT 540M", 2688 },
+{ 0x0DF5, "GeForce GT 525M", 2400 },
+{ 0x0DF6, "GeForce GT 550M", 4000 },
+{ 0x0DF7, "GeForce GT 520M", 2960 },
+{ 0x0DF8, "Quadro 600", 2560 },
+{ 0x0DFA, "Quadro 1000M", 2800 },
+{ 0x0DFC, "NVS 5200M", 2500 },
+{ 0x0E22, "GeForce GTX 460", 16200 },
+{ 0x0E23, "GeForce GTX 460 SE", 20800 },
+{ 0x0E24, "GeForce GTX 460", 21600 },
+{ 0x0E31, "GeForce GTX 485M", 18400 },
+{ 0x0E3A, "Quadro 3000M", 14400 },
+{ 0x0E3B, "Quadro 4000M", 15200 },
+{ 0x0F00, "GeForce GT 630", 7000 },
+{ 0x0F01, "GeForce GT 620", 3240 },
+{ 0x0FC0, "GeForce GT 640", 12800 },
+{ 0x0FC1, "GeForce GT 640", 12800 },
+{ 0x0FC2, "GeForce GT 630", 7000 },
+{ 0x0FC6, "GeForce GTX 650", 16900 },
+{ 0x0FD1, "GeForce GT 650M", 11800 },
+{ 0x0FD2, "GeForce GT 640M", 10000 },
+{ 0x0FD4, "GeForce GTX 660M", 13400 },
+{ 0x0FD5, "GeForce GT 650M", 11800 },
+{ 0x0FD8, "GeForce GT 640M", 10000 },
+{ 0x0FE0, "GeForce GTX 660M", 13400 },
+{ 0x0FE4, "GeForce GT 750M", 15500 },
+{ 0x0FFC, "Quadro K1000M", 13600 },
+{ 0x1004, "GeForce GTX 780", 41400 },
+{ 0x1040, "GeForce GT 520", 3240 },
+{ 0x1042, "GeForce 510", 2100 },
+{ 0x1049, "GeForce GT 620", 3240 },
+{ 0x104A, "GeForce GT 610", 2800 },
+{ 0x104B, "GeForce 9600 GT", 10400 },
+{ 0x1050, "GeForce GT 520M", 2960 },
+{ 0x1051, "GeForce GT 520MX", 3600 },
+{ 0x1054, "GeForce 410M", 2300 },
+{ 0x1055, "GeForce 410M", 2300 },
+{ 0x1056, "NVS 4200M", 3240 },
+{ 0x1057, "NVS 4200M", 3240 },
+{ 0x1058, "GeForce 610M", 3600 },
+{ 0x1080, "GeForce GTX 580", 37060 },
+{ 0x1081, "GeForce GTX 570", 29280 },
+{ 0x1082, "GeForce GTX 560 Ti", 26300 },
+{ 0x1084, "GeForce GTX 560", 25900 },
+{ 0x1086, "GeForce GTX 570", 29280 },
+{ 0x1087, "GeForce GTX 560 Ti", 26300 },
+{ 0x1088, "GeForce GTX 590", 43710 }, // 2xGPU, assume 1.5x perf
+{ 0x1089, "GeForce GTX 580", 37060 },
+{ 0x109A, "Quadro 5010M", 14400 },
+{ 0x10C0, "GeForce 9300 GS", 2268 },
+{ 0x10C3, "GeForce 8400GS", 2268 },
+{ 0x10C4, "ION", 1600 },
+{ 0x10C5, "GeForce 405", 1400 },
+{ 0x10D8, "NVS 300", 2356 },
+{ 0x1180, "GeForce GTX 680", 32200 },
+{ 0x1183, "GeForce GTX 660 Ti", 22000 },
+{ 0x1184, "GeForce GTX 770", 33500 },
+{ 0x1185, "GeForce GTX 660", 19800 },
+{ 0x1187, "GeForce GTX 760", 31360 },
+{ 0x1188, "GeForce GTX 690", 45000 }, // 2xGPU, assume 1.5x perf
+{ 0x1189, "GeForce GTX 670", 25600 },
+{ 0x11A0, "GeForce GTX 680M", 23000 },
+{ 0x11A2, "GeForce GTX 675MX", 19200 },
+{ 0x11A3, "GeForce GTX 680MX", 23000 },
+{ 0x11C0, "GeForce GTX 660", 19800 },
+{ 0x11C2, "GeForce GTX 650 TiBoost", 23500 },
+{ 0x11C6, "GeForce GTX 650 Ti", 14800 },
+{ 0x11E2, "GeForce GTX 765M", 13600 },
+{ 0x1200, "GeForce GTX 560 Ti", 26300 },
+{ 0x1201, "GeForce GTX 560", 25900 },
+{ 0x1205, "GeForce GTX 460 v2", 20800 },
+{ 0x1206, "GeForce GTX 555", 20800 },
+{ 0x1208, "GeForce GTX 560 SE", 17700 },
+{ 0x1210, "GeForce GTX 570M", 13800 },
+{ 0x1211, "GeForce GTX 580M", 19800 },
+{ 0x1212, "GeForce GTX 675M", 19800 },
+{ 0x1213, "GeForce GTX 670M", 14600 },
+{ 0x1241, "GeForce GT 545", 14000 },
+{ 0x1243, "GeForce GT 545", 14000 },
+{ 0x1244, "GeForce GTX 550 Ti", 21600 },
+{ 0x1245, "GeForce GTS 450", 12530 },
+{ 0x1246, "GeForce GT 550M", 2960 },
+{ 0x1247, "GeForce GT 555M", 10400 },
+{ 0x124B, "GeForce GT 640", 12800 },
+{ 0x124D, "GeForce GT 555M", 10400 },
+{ 0x1251, "GeForce GTX 560M", 18600 },
+{ 0, 0, 0 },
+};
+
+
+static const GraphicsDeviceDesc kIntelDevices[] = {
+{ 0x0042, "GMA HD", 1466 },
+{ 0x0046, "GMA HD", 1800 },
+{ 0x004A, "GMA HD", 1466 },
+{ 0x0102, "SandyBridge GT1", 2000 },
+{ 0x0106, "SandyBridge M GT1", 2000 },
+{ 0x010A, "SandyBridge Server", 2000 },
+{ 0x0112, "SandyBridge GT2", 2400 },
+{ 0x0116, "SandyBridge M GT2", 2400 },
+{ 0x0122, "SandyBridge GT2+", 2400 },
+{ 0x0126, "SandyBridge M GT2+", 2400 },
+{ 0x0152, "IvyBridge", 3000 }, // all guesswork, 1.5x SB
+{ 0x0156, "IvyBridge", 3000 },
+{ 0x015A, "IvyBridge", 3000 },
+{ 0x0162, "IvyBridge", 3600 },
+{ 0x0166, "IvyBridge", 3600 },
+{ 0x016A, "IvyBridge", 3600 },
+{ 0x0172, "IvyBridge", 3600 },
+{ 0x0176, "IvyBridge", 3600 },
+{ 0x0412, "HD Graphics 4600", 5400 },
+{ 0x0416, "HD Graphics 4600M", 4800 },
+{ 0x0A16, "HD Graphics 4600M", 4800 },
+{ 0x0A26, "HD Graphics 5000M", 6400 },
+{ 0x1132, "I815", 150 },
+{ 0x2562, "82845G", 200 },
+{ 0x2572, "82865G", 266 },
+{ 0x2582, "915G", 1332 },
+{ 0x2592, "915GM", 800 },
+{ 0x2772, "945G", 1600 },
+{ 0x2776, "945G", 1600 },
+{ 0x2782, "915G", 1332 },
+{ 0x2792, "915GM", 800 },
+{ 0x27A2, "945GM / GMA950", 1000 },
+{ 0x27A6, "945GM / GMA950", 1000 },
+{ 0x27AE, "945GME / GMA950", 1000 },
+{ 0x2972, "946GZ", 1600 },
+{ 0x2973, "946GZ", 1600 },
+{ 0x2982, "82G35", 1067 },
+{ 0x2992, "Q965/Q963", 1600 },
+{ 0x2993, "Q965/Q963", 1600 },
+{ 0x29A2, "G965", 1067 },
+{ 0x29A3, "G965", 1067 },
+{ 0x29B2, "Q35", 1600 },
+{ 0x29B3, "Q35", 1600 },
+{ 0x29C2, "G33/G31", 1600 },
+{ 0x29C3, "G33/G31", 1600 },
+{ 0x29D2, "Q33", 1600 },
+{ 0x29D3, "Q33", 1600 },
+{ 0x2A02, "GM965", 1067 },
+{ 0x2A03, "GM965", 1067 },
+{ 0x2A12, "GME965", 1067 },
+{ 0x2A42, "Mobile 4 Series", 1280 },
+{ 0x2E02, "4 Series", 1280 },
+{ 0x2E12, "4 Series", 1280 },
+{ 0x2E22, "G45/G43", 1280 },
+{ 0x2E32, "4 Series", 1280 },
+{ 0x3577, "I830", 200 },
+{ 0x3582, "I855", 233 },
+{ 0x358E, "I854", 233 },
+{ 0x7121, "I810", 150 },
+{ 0x7123, "I810 DC100", 150 },
+{ 0x7125, "I810 E", 150 },
+{ 0x8108, "GMA 500", 200 },
+{ 0x8109, "GMA 500", 400 },
+{ 0xA001, "GMA 3150", 1067 },
+{ 0xA011, "GMA 3150", 1067 },
+{ 0, 0, 0 },
+};
+
+
+static const GraphicsDeviceDesc kSISDevices[] = {
+{ 0x6330, "Mirage", 268 },
+{ 0x6351, "Mirage 3", 600 },
+{ 0, 0, 0 },
+};
+
+static const GraphicsDeviceDesc kVIADevices[] = {
+{ 0x3371, "Chrome9", 502 },
+{ 0, 0, 0 },
+};
+
+static const GraphicsDeviceDesc kSoftwareDevices[] = {
+{ 0x0405, "Software", 1000 }, // shared ID between Apple Software Renderer and VMWare SVGA
+{ 0, 0, 0 },
+};
+
+struct GraphicsVendorDesc {
+ int vendorID;
+ const GraphicsDeviceDesc* devices;
+};
+
+static const GraphicsVendorDesc kGraphicsVendors[] = {
+ { 0x1002, kATIDevices },
+ { 0x10DE, kNVIDIADevices },
+ { 0x8086, kIntelDevices },
+ { 0x1039, kSISDevices },
+ { 0x1106, kVIADevices },
+ { 0x15AD, kSoftwareDevices },
+ // 3D3D,3DLabs
+ // 102B,Matrox
+ // 5333,S3
+ // 18CA,XGI
+ // 1023,Trident
+ // 104A,ImgTech
+ // 121A,3dfx
+ // 1AB8,Parallels
+};
+
+
+
+
+
+static const GraphicsDeviceDesc* FindGraphicsDeviceDesc (int vendorID, int deviceID)
+{
+ for (size_t iv = 0; iv < ARRAY_SIZE(kGraphicsVendors); ++iv)
+ {
+ if (kGraphicsVendors[iv].vendorID != vendorID)
+ continue;
+ const GraphicsDeviceDesc* dev = kGraphicsVendors[iv].devices;
+ while (dev->deviceID)
+ {
+ if (dev->deviceID == deviceID)
+ return dev;
+ ++dev;
+ }
+ }
+ return NULL;
+}
+
+int GetGraphicsPixelFillrate (int vendorID, int deviceID)
+{
+ const GraphicsDeviceDesc* desc = FindGraphicsDeviceDesc (vendorID, deviceID);
+ return desc ? desc->pixelFillrate : -1;
+}
+
+#endif // GRAPHICS_DEVICES_DB_AVAILABLE
+
diff --git a/Runtime/Misc/GraphicsDevicesDB.h b/Runtime/Misc/GraphicsDevicesDB.h
new file mode 100644
index 0000000..e702aaa
--- /dev/null
+++ b/Runtime/Misc/GraphicsDevicesDB.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#define GRAPHICS_DEVICES_DB_AVAILABLE (UNITY_WIN || UNITY_OSX || UNITY_LINUX)
+
+#if GRAPHICS_DEVICES_DB_AVAILABLE
+int GetGraphicsPixelFillrate (int vendorID, int deviceID);
+#else
+inline int GetGraphicsPixelFillrate (int vendorID, int deviceID)
+{
+ #if (UNITY_XENON || UNITY_PS3)
+ return 4000;
+ #elif (UNITY_WII)
+ return 972;
+ #else
+ return -1;
+ #endif
+}
+#endif
diff --git a/Runtime/Misc/GraphicsScriptingUtility.cpp b/Runtime/Misc/GraphicsScriptingUtility.cpp
new file mode 100644
index 0000000..64c209f
--- /dev/null
+++ b/Runtime/Misc/GraphicsScriptingUtility.cpp
@@ -0,0 +1,36 @@
+#include "GraphicsScriptingUtility.h"
+#include "Runtime/Scripting/ICallString.h"
+
+#if ENABLE_MONO
+# include "Runtime/Mono/MonoIncludes.h"
+# include "Runtime/Scripting/ScriptingUtility.h"
+#endif
+
+ShaderLab::FastPropertyName ScriptingStringToProperty(ICallString& iCallString)
+{
+ if(iCallString.IsNull())
+ return ShaderLab::FastPropertyName();
+
+#if ENABLE_MONO
+ MonoString* msname = iCallString.str;
+ int const kShortString = 255;
+ char namebuf [kShortString+1];
+
+ if( msname->length <= kShortString && FastTestAndConvertUtf16ToAscii(namebuf, mono_string_chars(msname), mono_string_length(msname)))
+ {
+ namebuf [mono_string_length(msname)] = '\0';
+ return ShaderLab::Property(namebuf);
+ }
+
+ char* name = mono_string_to_utf8(msname);
+ ShaderLab::FastPropertyName propertyName(ShaderLab::Property(name));
+ g_free(name);
+
+ return propertyName;
+
+#else
+
+ return ShaderLab::Property(iCallString.AsUTF8().c_str());
+
+#endif
+} \ No newline at end of file
diff --git a/Runtime/Misc/GraphicsScriptingUtility.h b/Runtime/Misc/GraphicsScriptingUtility.h
new file mode 100644
index 0000000..7998df4
--- /dev/null
+++ b/Runtime/Misc/GraphicsScriptingUtility.h
@@ -0,0 +1,12 @@
+#ifndef GRAPHICSSCRIPTINGUTILITY_H_
+#define GRAPHICSSCRIPTINGUTILITY_H_
+
+#include "UnityPrefix.h"
+
+#include "External/shaderlab/Library/FastPropertyName.h"
+
+struct ICallString;
+
+ShaderLab::FastPropertyName ScriptingStringToProperty(ICallString& msname);
+
+#endif \ No newline at end of file
diff --git a/Runtime/Misc/GuiManager.cpp b/Runtime/Misc/GuiManager.cpp
new file mode 100644
index 0000000..ac5816e
--- /dev/null
+++ b/Runtime/Misc/GuiManager.cpp
@@ -0,0 +1,605 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "GuiManager.h"
+#include "DeveloperConsole.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoUtility.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/GUI/GuiState.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include <fstream>
+#include "ReproductionLog.h"
+#endif
+
+#if ENABLE_UNITYGUI
+static GUIManager* s_GUIManager = NULL;
+
+void InitGUIManager ()
+{
+ AssertIf(s_GUIManager != NULL);
+ s_GUIManager = new GUIManager();
+}
+
+void CleanupGUIManager ()
+{
+ AssertIf(s_GUIManager == NULL);
+ delete s_GUIManager;
+ s_GUIManager = NULL;
+}
+
+GUIManager &GetGUIManager () {
+ AssertIf(s_GUIManager == NULL);
+ return *s_GUIManager;
+}
+
+
+GUIManager::GUIManager () {
+ m_CurrentDepth = 1;
+ m_MouseUsed = false;
+ m_RenderingUserGUI = false;
+ m_HasKeyboardControl = false;
+ m_KeyboardDirection = 0;
+ m_CurrentKeyboardBehaviour = 0;
+ m_CurrentBehaviour = NULL;
+ m_LastInputEventTime = 0.0f;
+ m_DidGUIWindowsEatLastEvent = false;
+ #if UNITY_EDITOR
+ m_HasKeyboardOverride = false;
+ m_KeyboardControl = 0;
+ #endif
+ m_mouseButtonsDown = 0;
+}
+
+void GUIManager::AddGUIScript (ListNode_& beh)
+{
+ m_GUIScripts.push_back(beh);
+}
+
+PROFILER_INFORMATION(gGUIRepaintProfile, "GUI.Repaint", kProfilerGUI)
+PROFILER_INFORMATION(gGUIEventProfile, "GUI.ProcessEvents", kProfilerGUI)
+
+void GUIManager::Repaint () {
+ GetInputManager().SetTextFieldInput(false);
+
+ InputEvent ie;
+ ie = m_LastEvent;
+ ie.type = InputEvent::kRepaint;
+
+ DoGUIEvent(ie, false);
+}
+
+bool GUIManager::AnyMouseButtonsDown()
+{
+ return GetGUIManager().m_mouseButtonsDown != 0;
+}
+
+void GUIManager::SetHasKeyboardControl (bool hasKeyboard) {
+ GetGUIManager().m_HasKeyboardControl = true;
+}
+
+void GUIManager::SetKeyboardDirection (int direction) {
+ GetGUIManager().m_KeyboardDirection = direction;
+}
+int GUIManager::GetKeyboardDirection () {
+ return GetGUIManager().m_KeyboardDirection;
+}
+
+#if UNITY_EDITOR
+// function EditorWindow can call to tell UnityGUI if this window has OS-level keyboard focus
+void GUIManager::SetHasKeyboardOverride (int mode) {
+ GetGUIManager().m_HasKeyboardOverride = mode;
+}
+#endif
+
+
+bool GUIManager::CurrentScriptHasKeyboardFocus () {
+ GUIManager &gm = GetGUIManager();
+#if UNITY_EDITOR
+ if (gm.m_HasKeyboardOverride == 1)
+ return true;
+ else if (gm.m_HasKeyboardOverride == 2)
+ return false;
+#endif
+
+ return gm.m_CurrentBehaviour != NULL && gm.m_CurrentKeyboardBehaviour.GetInstanceID() == gm.m_CurrentBehaviour->GetInstanceID();
+}
+void GUIManager::SetKeyboardScriptInstanceID (int instanceID) {
+ GetGUIManager().m_CurrentKeyboardBehaviour.SetInstanceID(instanceID);
+}
+
+// Small wrapper around DoGUI, that handles getting keyboard between the various controls.
+bool GUIManager::CallGUI (GUIManager::SortedScripts::iterator i, InputEvent &e, bool doWindows) {
+ MonoBehaviour *beh = i->beh;
+ m_CurrentBehaviour = beh;
+ bool retval = beh->DoGUI (e, true, true, doWindows, 0);
+ return retval;
+}
+
+void GUIManager::SetCurrentDepth (int depth) { GetGUIManager().m_CurrentDepth = depth; }
+int GUIManager::GetCurrentDepth () { return GetGUIManager().m_CurrentDepth; }
+
+#if UNITY_EDITOR
+void GUIManager::SetEditorGUIInfo (bool hasKeyboardFocus, Vector2f guiPixelOffset) {
+ if (MONO_COMMON.setViewInfo) {
+ void* params[] = { &hasKeyboardFocus, &guiPixelOffset };
+ CallStaticMonoMethod (MONO_COMMON.setViewInfo, params);
+ }
+}
+#endif
+
+void GUIManager::SendQueuedEvents ()
+{
+ #if UNITY_EDITOR
+ if (MONO_COMMON.setViewInfo) {
+ bool hasKeyboardFocus = false;
+ Vector2f screenPosition (0,0);
+ void* params[] = { &hasKeyboardFocus, &screenPosition };
+ CallStaticMonoMethod (MONO_COMMON.setViewInfo, params);
+ }
+ #endif
+
+ while (!m_Events.empty())
+ {
+ DoGUIEvent(m_Events.front(), true);
+ m_Events.pop_front();
+ }
+}
+
+//implemented in monobehaviour.cpp
+void CallGuiUtilityBeginGUI(ScriptingObjectPtr monoEvent,int skin,int instanceID,bool allowGUILayout,ScriptingObjectPtr idList);
+
+bool GUIManager::BeginWindows (InputEvent &event, int skin, int editorWindowID)
+{
+
+ ScriptingObjectPtr monoEvent = CreateMonoInputEvent(event);
+ m_DidGUIWindowsEatLastEvent = false;
+
+ ScriptingObjectPtr idList = ScriptingGetGCHandleTarget(GetGlobalGUIState().m_State.m_IDListHandle);
+
+ CallGuiUtilityBeginGUI(monoEvent, skin, 0, true, idList);
+#if ENABLE_MONO
+ void* params[] = { &skin, idList, &editorWindowID };
+ CallStaticMonoMethod(MONO_COMMON.beginGuiWindows, params);
+#elif UNITY_FLASH
+ FLASH_ASM_WITH_NEWSP("GUI.BeginWindows(%0, marshallmap.getObjectWithId(%1) as UnityEngine.IDList, %2);" : : "r"(skin), "r"(idList), "r"(editorWindowID));
+#endif
+
+ InputEvent ie;
+ MarshallManagedStructIntoNative(monoEvent,&ie);
+ return ie.type == InputEvent::kUsed;
+}
+
+bool GUIManager::GetDidGUIWindowsEatLastEvent () {
+ return GetGUIManager().m_DidGUIWindowsEatLastEvent;
+}
+
+void GUIManager::SetDidGUIWindowsEatLastEvent (bool value) {
+ GetGUIManager().m_DidGUIWindowsEatLastEvent = value;
+}
+
+void GUIManager::EndWindows () {
+ ScriptingObjectPtr idList = ScriptingGetGCHandleTarget(GetGlobalGUIState().m_State.m_IDListHandle);
+#if ENABLE_MONO
+ void* params[] = { idList };
+ CallStaticMonoMethod(MONO_COMMON.endGuiWindows, params);
+#elif UNITY_FLASH
+ FLASH_ASM_WITH_NEWSP("GUI.EndWindows(marshallmap.getObjectWithId(%0));" : : "r"(idList));
+#endif
+}
+
+// A note on how the popup windows work:
+// It's a bit of a hack: The gui system maintains a list of all popup windows that gets rebuilt every frame. Then we have some static functions we need to call to process that list.
+// It's a LOT simpler if we do the EndWindows call from inside EndGUI, hence we have to call it DURING executing a script (even if its static).
+
+// Here's how it goes:
+// We call BeginWindows with a layout event before anything else. This makes them init.
+// after the last layout event, we call EndWindows. Now the GUI system has an up-to-date list of windows.
+
+// During event processing, we call beginWindows just before the first script that lies on a depth > 0, so it can eat mouse clicks, etc....
+// EndWindows can be called at any time here, so we do it on the same script
+// During repaint, we can call BeginWindows at any point, but EndWindows on the LAST script that is in layer > 0. We call them at the same time.
+
+// I _think_ this can be cleaned up a bit, but I'm not yet ready to do so before I have it working in scene views, etc...
+
+struct OldSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth < rhs.depth; }
+};
+
+struct NewSortScript : std::binary_function<GUIManager::SortedScript&, GUIManager::SortedScript&, std::size_t>
+{
+ bool operator () (GUIManager::SortedScript& lhs, GUIManager::SortedScript& rhs) const { return lhs.depth > rhs.depth; }
+};
+
+void GUIManager::DoGUIEvent (InputEvent &e, bool frontToBack)
+{
+ #if ENABLE_PROFILER
+ ProfilerInformation* information = &gGUIEventProfile;
+ if (e.type == InputEvent::kRepaint)
+ information = &gGUIRepaintProfile;
+
+ PROFILER_AUTO(*information, NULL)
+ #endif
+
+#if UNITY_EDITOR
+ if (MONO_COMMON.setKeyboardControl)
+ {
+ void* args[] = { &m_KeyboardControl };
+ CallStaticMonoMethod(MONO_COMMON.setKeyboardControl, args);
+ }
+#endif
+
+ MonoBehaviour* authorizationDialog = GetUserAuthorizationManager().GetAuthorizationDialog();
+ MonoBehaviour* developerConsole = DeveloperConsole::Get();
+
+
+ // Update the lists of which sripts we _actually_ want to execute.
+ if (m_GUIScripts.empty() && authorizationDialog == NULL && !DeveloperConsole::IsVisible())
+ {
+ m_MouseUsed = false;
+ return;
+ }
+
+ // Move the event mouse position away if the screen is locked. We don't want the cursor to interact
+ // with the GUI when it is in an arbitrary, fixed position.
+ if (GetScreenManager().GetLockCursor())
+ e.mousePos = Vector2f (-10000, -10000);
+
+ // ok - first we send them the layout event and find out the layering
+ InputEvent::Type originalType = e.type;
+
+ int handleTab = 0;
+ if (e.type == InputEvent::kKeyDown && (e.character == '\t' || e.character == 25))
+ {
+ handleTab = ((e.modifiers & InputEvent::kShift) == 0) ? 1 : -1;
+#if ENABLE_MONO
+ if (MONO_COMMON.beginTabControlSearch)
+ CallStaticMonoMethod (MONO_COMMON.beginTabControlSearch, NULL);
+#endif
+ }
+
+
+
+ std::vector<PPtr<MonoBehaviour> > layoutedScripts;
+ if (authorizationDialog)
+ layoutedScripts.push_back (authorizationDialog);
+ else
+ {
+ layoutedScripts.reserve(m_GUIScripts.size_slow());
+ SafeListIterator<MonoBehaviour*> guiScriptIterator (m_GUIScripts);
+ while (guiScriptIterator.Next())
+ {
+ MonoBehaviour& beh = **guiScriptIterator;
+
+ if (beh.GetUseGUILayout())
+ layoutedScripts.push_back(&beh);
+ else
+ {
+ m_CurrentDepth = 1;
+ m_HasKeyboardControl = false;
+ beh.DoGUI (e, false, false, false, 0);
+ }
+ }
+ }
+ if (developerConsole)
+ layoutedScripts.push_back (developerConsole);
+ if (layoutedScripts.empty())
+ return;
+
+ e.type = InputEvent::kLayout;
+ BeginWindows (e, 0, 0);
+
+ m_SortedScripts.clear ();
+ int current = 1;
+ for (std::vector<PPtr<MonoBehaviour> >::iterator i = layoutedScripts.begin (); i != layoutedScripts.end ();i ++)
+ {
+ MonoBehaviour *beh = *i;
+ if (beh)
+ {
+ m_CurrentDepth = 1;
+ m_HasKeyboardControl = false;
+ bool doWindows = current == layoutedScripts.size();
+ beh->DoGUI (e, true, true, doWindows, 0);
+ m_SortedScripts.push_back (SortedScript (m_CurrentDepth, beh, m_HasKeyboardControl));
+ current++;
+ }
+ }
+
+// @TODO: Fix sort order for 3.0 by introducing new property
+// if( IsUnity2_6OrHigher() )
+// {
+// NewSortScript sort;
+// // Next, we sort by depth
+// m_SortedScripts.sort (sort);
+// }
+ OldSortScript sort;
+ // Next, we sort by depth
+ m_SortedScripts.sort (sort);
+
+ // If we don't have a keyboard-focused script (or the script has been disabled), we set it to be the topmost script;
+ MonoBehaviour* currentKey = m_CurrentKeyboardBehaviour;
+ bool found = false;
+ if (currentKey != NULL) {
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++) {
+ if (i->beh == currentKey) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found)
+ m_CurrentKeyboardBehaviour = m_SortedScripts.begin()->beh;
+
+ e.type = originalType;
+
+ bool hasSentEndWindows = false;
+ current = 1; // reset the count so we can send the DoWindows.
+ bool eventUsed = false;
+ if (frontToBack)
+ {
+ for (SortedScripts::iterator i = m_SortedScripts.begin(); i != m_SortedScripts.end(); i++)
+ {
+ // If this is the first script in layer 0,
+ bool endWindows = false;
+ if ((current == m_SortedScripts.size() || i->depth > 0) && !hasSentEndWindows)
+ {
+ eventUsed = BeginWindows (e, 0, 0);
+ if (eventUsed)
+ break;
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ current++;
+
+ eventUsed = CallGUI (i, e, endWindows); // Tell the last one to do windows as well
+
+ // If this script used the event, we terminate the loop
+ if (eventUsed)
+ break;
+ }
+
+ // Remove keyboard focus when clicking on empty GUI area
+ // (so text fields don't take away game view input).
+ if (originalType == InputEvent::kMouseDown && !eventUsed)
+ {
+ int noKeyboardControl = 0;
+ #if ENABLE_MONO
+ void* args[] = { &noKeyboardControl };
+ CallStaticMonoMethod(MONO_COMMON.setKeyboardControl, args);
+ #endif
+ }
+
+ // Handle mouse focus: We want the new GUI system to eat any mouse events. This means that we need to check this during repaint or mouseDown/Up
+ // and set a global variable accordingly.
+ if (originalType == InputEvent::kMouseDown || originalType == InputEvent::kMouseUp)
+ m_MouseUsed |= eventUsed;
+ }
+ else
+ {
+ // Flag the mouse as beign unused. During repainting, we will detect if any controls are draw under the mouse and mark
+ // it as used.
+ m_MouseUsed = false;
+ m_RenderingUserGUI = true;
+
+ // It's on purpose I'm iterating backwrds here - used by repainting
+ // this time, you can't bail out of the event processing
+ BeginWindows (e,0,0);
+ for (SortedScripts::iterator i = m_SortedScripts.end(); i != m_SortedScripts.begin();)
+ {
+ i--;
+ bool endWindows = false;
+
+
+ // If this window is the last, OR the next one is above 0 depth, we should do repaint all popup windows NOW
+ if (!hasSentEndWindows) // If we haven't already sent it
+ {
+ if (current == m_SortedScripts.size()) // If this is the last script, we must call it now
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ else
+ {
+ // If this is the last script with a depth > 0, we must call it now.
+ SortedScripts::iterator j = i;
+ j--;
+ if (j->depth <= 0)
+ {
+ hasSentEndWindows = true;
+ endWindows = true;
+ }
+ }
+ }
+ CallGUI (i, e, endWindows);
+ current++;
+ }
+
+ m_RenderingUserGUI = false;
+ }
+
+ if (handleTab != 0 && !eventUsed)
+ {
+#if ENABLE_MONO
+ if (MONO_COMMON.endTabControlSearch)
+ {
+ void* params[] = { &handleTab };
+ CallStaticMonoMethod (MONO_COMMON.endTabControlSearch, params);
+
+ }
+#endif
+ }
+
+#if UNITY_EDITOR
+ if (MONO_COMMON.getKeyboardControl)
+ m_KeyboardControl = ExtractMonoObjectData<int> (CallStaticMonoMethod(MONO_COMMON.getKeyboardControl, NULL));
+#endif
+}
+
+void GUIManager::QueueEvent (InputEvent &ie)
+{
+ QueueEventImmediate(ie);
+}
+
+
+void GUIManager::QueueEventImmediate (InputEvent &ie)
+{
+ // MouseMove events are not sent.
+ // The same info can be obtained from repaint events.
+ if (ie.type == InputEvent::kMouseMove)
+ {
+ // We still use them as last event to update the cursor position.
+ m_LastEvent = ie;
+ return;
+ }
+ if (ie.type == InputEvent::kIgnore)
+ return;
+
+ if ( ie.type == InputEvent::kMouseDown )
+ m_mouseButtonsDown |= (1<<ie.button);
+ else if ( ie.type == InputEvent::kMouseUp )
+ m_mouseButtonsDown &= ~(1<<ie.button);
+
+ switch (ie.type) {
+ case InputEvent::kMouseDown:
+ case InputEvent::kMouseUp:
+ case InputEvent::kKeyDown:
+ ResetCursorFlash ();
+ break;
+ }
+
+ m_LastEvent = ie;
+ m_Events.push_back(ie);
+}
+
+void GUIManager::ResetCursorFlash ()
+{
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ return;
+ #endif
+
+ GetGUIManager().m_LastInputEventTime = GetTimeManager().GetRealtime ();
+}
+
+float GUIManager::GetCursorFlashTime ()
+{
+ return GetGUIManager().m_LastInputEventTime;
+}
+
+bool GUIManager::GetMouseUsed ()
+{
+ return GetGUIManager().m_MouseUsed;
+}
+
+void GUIManager::SetMouseUsed (bool used)
+{
+ // We only allow changing m_MouseUsed during rendering of User GUI (fix for case 387913)
+ if (GetGUIManager().m_RenderingUserGUI)
+ {
+ GetGUIManager().m_MouseUsed = used;
+ }
+}
+
+#if SUPPORT_REPRODUCE_LOG
+
+void WriteInputEvent (InputEvent& event, std::ostream& out)
+{
+ out << (int&)event.type << ' ';
+ WriteFloat(out, event.mousePos.x); out << ' ';
+ WriteFloat(out, event.mousePos.y); out << ' ';
+ WriteFloat(out, event.delta.x); out << ' ';
+ WriteFloat(out, event.delta.y); out << ' ';
+
+ out << event.button << ' ' << event.modifiers << ' ';
+ WriteFloat(out, event.pressure); out << ' ';
+ out << event.clickCount << ' ' << event.character << ' ' << event.keycode << ' ';
+
+ // if (event.commandString)
+ // WriteReproductionString(out, event.commandString);
+ // else
+ // WriteReproductionString(out, "");
+}
+
+void ReadInputEvent (InputEvent& event, std::istream& in, int version)
+{
+ event.Init();
+
+ in >> (int&)event.type;
+ ReadFloat(in, event.mousePos.x);
+ ReadFloat(in, event.mousePos.y);
+ ReadFloat(in, event.delta.x);
+ ReadFloat(in, event.delta.y);
+ in >> event.button >> event.modifiers;
+ ReadFloat(in, event.pressure);
+ in >> event.clickCount >> event.character >> event.keycode;
+
+ //if (version >= 6)
+ //{
+ // std::string commandString;
+ // ReadReproductionString(in, commandString);
+ // event.commandString = new char[commandString.size() + 1];
+ // memcpy(event.commandString, commandString.c_str(), commandString.size() + 1);
+ //}
+}
+
+void GUIManager::WriteLog (std::ofstream& out)
+{
+ out << "Events" << std::endl;
+
+ out << m_Events.size() << std::endl;
+
+ for (int i=0;i<m_Events.size();i++)
+ {
+ InputEvent& event = m_Events[i];
+ WriteInputEvent(event, out);
+ }
+
+ // Hover events seem to require the last event as well!
+ InputEvent& event = m_LastEvent;
+ WriteInputEvent (event, out);
+
+ if (GetReproduceVersion () >= 6)
+ {
+ WriteReproductionString(out, GetInputManager().GetCompositionString());
+ out << (int)(GetInputManager().GetTextFieldInput()) << ' ';
+ }
+
+ out << std::endl;
+}
+
+void GUIManager::ReadLog (std::ifstream& in)
+{
+ CheckReproduceTagAndExit("Events", in);
+
+ int size;
+ in >> size;
+ m_Events.clear();
+ for (int i=0;i<size;i++)
+ {
+ InputEvent event;
+ ReadInputEvent (event, in, GetReproduceVersion());
+ m_Events.push_back(event);
+ }
+
+ // Hover events seem to require the last event as well!
+ ReadInputEvent (m_LastEvent, in, GetReproduceVersion());
+
+ if (GetReproduceVersion () >= 6)
+ {
+ ReadReproductionString(in, GetInputManager().GetCompositionString());
+ int textFieldInput;
+ in >> textFieldInput;
+ GetInputManager().SetTextFieldInput(textFieldInput);
+ }
+}
+#endif
+#endif \ No newline at end of file
diff --git a/Runtime/Misc/InputEvent.cpp b/Runtime/Misc/InputEvent.cpp
new file mode 100644
index 0000000..b27eb17
--- /dev/null
+++ b/Runtime/Misc/InputEvent.cpp
@@ -0,0 +1,162 @@
+#include "UnityPrefix.h"
+#include "InputEvent.h"
+
+InputInterface::~InputInterface () {
+}
+bool InputInterface::PerformDrag (InputEvent &event) {
+ return false;
+}
+int InputInterface::UpdateDrag (InputEvent &event) {
+ return 0;
+}
+
+InputEvent::~InputEvent( )
+{
+ delete []commandString;
+}
+
+void InputEvent::Debug () const
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ Vector2f mousePosition = touch.pos;
+ Vector2f delta = touch.deltaPos;
+#endif
+ std::string buffer;
+ const char *EventNames[] = {
+ "kMouseDown", "kMouseUp", "kMouseMove", "kMouseDrag", "kKeyDown", "kKeyUp",
+ "kScrollWheel", "kRepaint", "kLayout", "kDragUpdated", "kDragPerform", "kIgnore", "kUsed"
+ };
+ bool isMouse = false;
+ bool isKeyb = false;
+ switch (type) {
+ case kKeyDown:
+ case kKeyUp:
+ case kScrollWheel:
+ isKeyb = true;
+ break;
+ case kMouseDown:
+ case kMouseUp:
+ case kMouseMove:
+ case kMouseDrag:
+ isMouse = true;
+ }
+ buffer += Format("Event: %s\n", EventNames[type]);
+ if (isMouse) {
+ buffer += Format(" Pos (%f, %f) \t Delta: (%f, %f)\n", mousePosition.x, mousePosition.y, delta.x, delta.y);
+ buffer += Format(" Button: %d", button);
+ buffer += Format(" Click count: %d", clickCount);
+ } else if (isKeyb) {
+ AssertIf(clickCount != 0);
+ if( character >= 32 && character <= 127 )
+ buffer += Format(" character: '%c'\n", character);
+ else
+ buffer += Format(" character: code %d\n", character);
+ buffer += Format(" keycode: '%d'\n", keycode);
+ }
+ buffer += " Mod: ";
+ if (modifiers & kShift) buffer += "shift ";
+ if (modifiers & kControl) buffer += "ctrl ";
+ if (modifiers & kAlt) buffer += "alt ";
+ if (modifiers & kCommand) buffer += "cmd ";
+ if (modifiers & kNumeric) buffer += "num ";
+ if (modifiers & kCapsLock) buffer += "capslock ";
+ if (modifiers & kFunctionKey) buffer += "fkey ";
+ if (modifiers == 0) buffer += "none ";
+ buffer += "\n";
+ printf_console( "%s", buffer.c_str() );
+}
+
+void InputEvent::Init( )
+{
+#if !ENABLE_NEW_EVENT_SYSTEM
+ mousePosition = Vector2f(0.0F, 0.0F);
+ delta = Vector2f(0.0F, 0.0F);
+ #if UNITY_METRO
+ touchType = kMouseTouch;
+ #endif
+#endif
+ type = InputEvent::kIgnore;
+ keycode = 0;
+ character = 0;
+ button = 0;
+ clickCount = 0;
+ pressure = 0;
+ modifiers = 0;
+ commandString = NULL;
+}
+
+InputEvent::InputEvent( const InputEvent& evt )
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ touch = evt.touch;
+#else
+ mousePosition = evt.mousePosition;
+ delta = evt.delta;
+ #if UNITY_METRO
+ touchType = evt.touchType;
+ #endif
+#endif
+ type = evt.type;
+ button = evt.button;
+ modifiers = evt.modifiers;
+ pressure = evt.pressure;
+ clickCount = evt.clickCount;
+ character = evt.character;
+ keycode = evt.keycode;
+
+ if (evt.commandString)
+ {
+ commandString = new char [strlen(evt.commandString) + 1];
+ memcpy(commandString, evt.commandString, strlen(evt.commandString) + 1);
+ }
+ else
+ {
+ commandString = NULL;
+ }
+}
+
+
+void InputEvent::operator = (const InputEvent& evt)
+{
+#if ENABLE_NEW_EVENT_SYSTEM
+ touch = evt.touch;
+#else
+ mousePosition = evt.mousePosition;
+ delta = evt.delta;
+ #if UNITY_METRO
+ touchType = evt.touchType;
+ #endif
+#endif
+ type = evt.type;
+ button = evt.button;
+ modifiers = evt.modifiers;
+ pressure = evt.pressure;
+ clickCount = evt.clickCount;
+ character = evt.character;
+ keycode = evt.keycode;
+
+ if (commandString)
+ {
+ delete[] commandString;
+ commandString = NULL;
+ }
+
+ if (evt.commandString)
+ {
+ commandString = new char [strlen(evt.commandString) + 1];
+ memcpy(commandString, evt.commandString, strlen(evt.commandString) + 1);
+ }
+}
+
+InputEvent InputEvent::CommandStringEvent (const std::string& editorCommand, bool execute)
+{
+ InputEvent event;
+ event.Init();
+ if (execute)
+ event.type = InputEvent::kExecuteCommand;
+ else
+ event.type = InputEvent::kValidateCommand;
+ event.commandString = new char[editorCommand.size() + 1];
+ memcpy(event.commandString, editorCommand.c_str(), editorCommand.size() + 1);
+ return event;
+}
diff --git a/Runtime/Misc/InputEvent.h b/Runtime/Misc/InputEvent.h
new file mode 100644
index 0000000..31112ee
--- /dev/null
+++ b/Runtime/Misc/InputEvent.h
@@ -0,0 +1,146 @@
+#ifndef INPUTINTERFACE_H
+#define INPUTINTERFACE_H
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Input/GetInput.h"
+#if UNITY_LINUX
+#include "Runtime/GfxDevice/GfxDeviceTypes.h"
+#endif
+#include <iostream>
+
+#ifdef __OBJC__
+ @class NSEvent;
+ @class NSView;
+ @class NSDraggingInfo;
+#endif
+
+/// An input event
+struct InputEvent {
+ InputEvent () { commandString = NULL; }
+#if UNITY_OSX && defined(__OBJC__)
+ InputEvent (NSEvent *src);
+ InputEvent (NSEvent *src, NSView *view, bool cullOffscreenMouseDownEvents = true);
+ InputEvent (id<NSDraggingInfo> draggingInfo, int inType, NSView* view);
+
+ static InputEvent RepaintEvent (NSView *source);
+#endif
+#if UNITY_WIN
+ InputEvent( UINT message, WPARAM wParam, LPARAM lParam, HWND window );
+ #if UNITY_EDITOR
+ InputEvent( int x, int y, DWORD keyFlags, int typ, HWND window );
+ static InputEvent RepaintEvent (HWND window);
+ static InputEvent CommandStringEvent (const std::string& editorCommand, bool execute, HWND window);
+ static InputEvent SimulateKeyPressEvent(HWND window, int keyCode, int modifiers);
+ #endif
+#endif
+#if UNITY_LINUX
+ static InputEvent RepaintEvent (NativeWindow window);
+ InputEvent ( int type, Vector2f location, int code, int state, unsigned long timeStamp );
+ InputEvent ( int type, unsigned int key, unsigned int keycode, unsigned state, Vector2f location );
+#elif UNITY_FLASH || UNITY_WEBGL
+ InputEvent( int type );
+ InputEvent(int eventType, int key, int code, int state);
+#endif
+
+ ~InputEvent( );
+
+ InputEvent( const InputEvent& evt );
+ void operator = (const InputEvent& evt);
+
+ static InputEvent CommandStringEvent (const std::string& editorCommand, bool execute);
+
+ void Debug () const;
+
+ enum Modifiers {
+ kShift = 1 << 0,
+ kControl = 1 << 1,
+ kAlt = 1 << 2,
+ kCommand = 1 << 3,
+ kNumeric = 1 << 4,
+ kCapsLock = 1 << 5,
+ kFunctionKey = 1 << 6
+ };
+ enum TypeEnum {
+ kMouseDown = 0, kMouseUp = 1, kMouseMove = 2, kMouseDrag = 3, kKeyDown=4, kKeyUp=5,
+ kScrollWheel=6, kRepaint=7, kLayout=8, kDragUpdated=9, kDragPerform=10,kDragExited=15, kIgnore=11,kUsed=12,kValidateCommand=13,kExecuteCommand=14,kContextClick=16,
+ kMagnifyGesture=1000, kSwipeGesture=1001, kRotateGesture=1002
+ };
+ enum MouseButton {
+ kLeftButton = 0, kRightButton = 1, kMiddleButton = 2
+ };
+#if UNITY_METRO
+ enum TouchType {
+ kMouseTouch = 0, kFingerTouch = 1
+ };
+#endif
+
+#if UNITY_WINRT
+ // Putted ctor here so I can use TypeEnum...
+ InputEvent (TypeEnum inEventType, Vector2f mouseLocation, int modifiers);
+ InputEvent (TypeEnum inEventType, UInt32 inKey, UInt32 inCharacter, UInt32 state, Vector2f mouseLocation);
+#endif
+ // needs to be size_t, as mono enums are 32-bit or 64-bit depending on architecture
+ typedef size_t Type;
+ Type type; ///< Which type of event.
+#if ENABLE_NEW_EVENT_SYSTEM
+ Touch touch; ///< Touch containing position and delta information
+#else
+ Vector2f mousePosition; ///< Position of mouse events.
+ Vector2f delta; ///< Delta of mouse events.
+ #if UNITY_METRO
+ TouchType touchType;
+ #endif
+#endif
+ int button; ///< mouse button number. (bitfield of MouseButton enum)
+ int modifiers; ///< keyboard modifier flags. (bitfield of Modifiers enum)
+ float pressure; ///< Stylus pressure.
+ int clickCount;
+ UInt16 character; ///< unicode keyboard character (with modifiers).
+ UInt16 keycode; ///< The keyboard scancode of the event.
+ char* commandString;
+
+ // Initialize to ignore event with all values cleared.
+ void Init();
+
+ void Use () { type = kUsed; }
+
+ #ifdef __OBJC__
+ static InputEvent CommandStringEvent (const std::string& editorCommand, bool execute, NSView* view);
+ #endif
+
+private:
+ #if UNITY_OSX && defined (__OBJC__)
+ void GetImmediateMousePosition (NSEvent *event, NSView *view);
+ void Init (NSEvent *src, NSView *view, bool cullOffscreenMouseDownEvents);
+ #endif
+ #if UNITY_WIN && UNITY_EDITOR
+ void DoMouseJumpingTroughScreenEdges(HWND window, Vector2f mousePos, Vector2f& lastMousePos);
+ #endif
+};
+
+/// Semi-Abstract superclass for all things input-related.
+/// Override these functions and return true if you ate the event.
+/// The default implementations just return false.
+class InputInterface {
+ public:
+ virtual ~InputInterface ();
+
+ virtual bool OnInputEvent (InputEvent &event) = 0;
+
+ virtual bool PerformDrag (InputEvent &event);
+ virtual int UpdateDrag (InputEvent &event);
+};
+
+std::string InputEventToString (InputEvent& event);
+void StringToInputEvent (const std::string& inputEvt, InputEvent& evt);
+
+#if UNITY_OSX && GAMERELEASE
+typedef struct _NPCocoaEvent NPCocoaEvent;
+
+void InitWebplayerInputEvent (InputEvent& event, EventRecord* rec);
+void InitEventFromEventRef ( InputEvent& event, EventRef eventRef );
+void InitEventFromEventRecord( InputEvent& event, EventRecord* evt );
+void InitWebplayerInputEventCocoa (InputEvent& event, NPCocoaEvent* cocoaEvent, bool textInputHandledByBrowser);
+#endif
+
+#endif
diff --git a/Runtime/Misc/MeshWelding.cpp b/Runtime/Misc/MeshWelding.cpp
new file mode 100644
index 0000000..2fee400
--- /dev/null
+++ b/Runtime/Misc/MeshWelding.cpp
@@ -0,0 +1,249 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/MeshWelding.h"
+#include "Editor/Src/AssetPipeline/ImportMesh.h"
+
+
+inline UInt32 GetVector3HashValue (const Vector3f& value)
+{
+ const UInt32* h = (const UInt32*)(&value);
+ UInt32 f = (h[0]+h[1]*11-(h[2]*17))&0x7fffffff; // avoid problems with +-0
+ return (f>>22)^(f>>12)^(f);
+}
+
+/*
+ In 12 operations, this code computes the next highest power of 2 for a 32-bit integer. The result may be expressed by the formula 1U << (lg(v - 1) + 1).
+ It would be faster by 2 operations to use the formula and the log base 2 methed that uses a lookup table, but in some situations,
+ lookup tables are not suitable, so the above code may be best.
+ */
+
+inline int nextPowerOfTwo (UInt32 v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v + (v==0);
+}
+
+inline bool CompareBone(const BoneInfluence& lhs, const BoneInfluence& rhs)
+{
+ for (int i=0;i<4;i++)
+ {
+ if (!CompareApproximately(lhs.weight[0], rhs.weight[0]) || lhs.boneIndex[i] != rhs.boneIndex[i])
+ return false;
+ }
+ return true;
+}
+
+
+#if UNITY_EDITOR
+
+/*-----------------------------------------------------------------------//*
+ !
+ * \brief an array of vertex positions
+ * \param p Array of vertex positions (will be modified!)
+ * \param N Number of vertices in array
+ * \return Number of vertices after the welding operation
+ * \note The unique vertices are stored into the beginning of array
+ * 'p'.
+ * \note This welder is "bit-exact", i.e. only duplicate vertices are
+ * removed. For distance-based welding a somewhat more complicated
+ * algorithm needs to be used.
+
+ *-------------------------------------------------------------------------*/
+
+int weld (std::vector<Vector3f>& vertices, dynamic_array<BoneInfluence>& skin, std::vector<ImportBlendShape>& shapes, std::vector<int>& remap)
+{
+ const int NIL = -1; // linked list terminator symbol
+ int outputCount = 0; // # of output vertices
+ int hashSize = nextPowerOfTwo(vertices.size()); // size of the hash table
+ int* hashTable = new int[hashSize + vertices.size()]; // hash table + linked list
+ int* next = hashTable + hashSize; // use bottom part as linked list
+
+ remap.resize(vertices.size());
+
+ memset (hashTable, NIL, (hashSize) * sizeof(int)); // init hash table (NIL = 0xFFFFFFFF so memset works)
+
+ for (int i = 0; i < vertices.size(); i++)
+ {
+ const Vector3f& v = vertices[i];
+ UInt32 hashValue = GetVector3HashValue(v) & (hashSize-1);
+ int offset = hashTable[hashValue];
+ while (offset != NIL)
+ {
+ Assert (offset < i);
+ bool euqals = (vertices[offset] == v);
+
+ if (euqals && !skin.empty() && !CompareBone(skin[i], skin[offset]))
+ euqals = false;
+
+ if (euqals && !shapes.empty())
+ {
+ for (int j = 0; j < shapes.size(); ++j)
+ {
+ if (shapes[j].vertices[i] != shapes[j].vertices[offset])
+ {
+ euqals = false;
+ break;
+ }
+ }
+ }
+
+ if (euqals)
+ break;
+
+ offset = next[offset];
+ }
+
+ if (offset == NIL) // no match found - copy vertex & add to hash
+ {
+ remap[i] = outputCount;
+ vertices[outputCount] = v; // copy vertex
+ if (!skin.empty())
+ skin[outputCount] = skin[i];
+ for (int j = 0; j < shapes.size(); ++j)
+ shapes[j].vertices[outputCount] = shapes[j].vertices[i];
+
+ next[outputCount] = hashTable[hashValue]; // link to hash table
+ hashTable[hashValue] = outputCount++; // update hash heads and increase output counter
+ }
+ else
+ {
+ Assert (offset < i);
+ remap[i] = offset;
+ }
+ }
+
+ delete[] hashTable; // cleanup
+ if (outputCount < vertices.size())
+ {
+ vertices.resize(outputCount);
+ if (!skin.empty())
+ skin.resize_initialized(outputCount);
+ for (int j = 0; j < shapes.size(); ++j)
+ shapes[j].vertices.resize(outputCount);
+ return true;
+ }
+ else
+ return false;
+}
+
+void WeldVertices (ImportMesh& mesh)
+{
+ std::vector<int> remap;
+ if (weld (mesh.vertices, mesh.skin, mesh.shapes, remap))
+ {
+ UInt32* indices = &mesh.polygons[0];
+ for (int i=0;i<mesh.polygons.size();i++)
+ indices[i] = remap[indices[i]];
+ }
+}
+
+#endif
+
+bool WeldVertexArray(dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap)
+{
+ Mesh::BoneInfluenceContainer skin;
+ return WeldVertexArray(vertices, skin, triangles, remap);
+}
+
+bool WeldVertexArray(dynamic_array<Vector3f>& vertices, Mesh::BoneInfluenceContainer& skin, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap)
+{
+ const int NIL = -1; // linked list terminator symbol
+ int outputCount = 0; // # of output vertices
+ int hashSize = nextPowerOfTwo(vertices.size()); // size of the hash table
+ int* hashTable = new int[hashSize + vertices.size()]; // hash table + linked list
+ int* next = hashTable + hashSize; // use bottom part as linked list
+
+ remap.resize_uninitialized(vertices.size());
+
+ memset (hashTable, NIL, (hashSize) * sizeof(int)); // init hash table (NIL = 0xFFFFFFFF so memset works)
+
+ for (int i = 0; i < vertices.size(); i++)
+ {
+ const Vector3f& v = vertices[i];
+ UInt32 hashValue = GetVector3HashValue(v) & (hashSize-1);
+ int offset = hashTable[hashValue];
+ while (offset != NIL)
+ {
+ Assert (offset < i);
+ if (vertices[offset] == v)
+ {
+ if (skin.empty() || CompareBone(skin[i], skin[offset]))
+ break;
+ }
+ offset = next[offset];
+ }
+
+ if (offset == NIL) // no match found - copy vertex & add to hash
+ {
+ remap[i] = outputCount;
+ vertices[outputCount] = v; // copy vertex
+
+ if (!skin.empty())
+ skin[outputCount] = skin[i];
+
+ next[outputCount] = hashTable[hashValue]; // link to hash table
+ hashTable[hashValue] = outputCount++; // update hash heads and increase output counter
+ }
+ else
+ {
+ Assert (offset < i);
+ remap[i] = offset;
+ }
+ }
+
+ delete[] hashTable; // cleanup
+
+ if (outputCount < vertices.size())
+ {
+ vertices.resize_uninitialized(outputCount);
+ if (!skin.empty())
+ skin.resize_uninitialized(outputCount);
+ for (int i=0;i<triangles.size();i++)
+ triangles[i] = remap[triangles[i]];
+ return true;
+ }
+ return false;
+}
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+SUITE (VertexWeldingTests)
+{
+
+ TEST (TestVertexWelding)
+ {
+ Vector3f vertices[] = { Vector3f (0,0,0), Vector3f (1,0,0), Vector3f (1,0,0), Vector3f (0,0,0) };
+ dynamic_array<Vector3f> dVertices; dVertices.assign(vertices, vertices + ARRAY_SIZE(vertices));
+
+ UInt16 indices[] = { 0, 1, 2, 3 };
+ dynamic_array<UInt16> dIndices; dIndices.assign(indices, indices + ARRAY_SIZE(indices));
+
+ dynamic_array<UInt16> remap;
+
+ WeldVertexArray(dVertices, dIndices, remap);
+
+ CHECK_EQUAL(2, dVertices.size());
+ CHECK(Vector3f(0,0,0) == dVertices[0]);
+ CHECK(Vector3f(1,0,0) == dVertices[1]);
+
+ CHECK(0 == dIndices[0]);
+ CHECK(1 == dIndices[1]);
+ CHECK(1 == dIndices[2]);
+ CHECK(0 == dIndices[3]);
+
+ CHECK_EQUAL(4, remap.size());
+ CHECK_EQUAL(0, remap[0]);
+ CHECK_EQUAL(1, remap[1]);
+ CHECK_EQUAL(1, remap[2]);
+ CHECK_EQUAL(0, remap[3]);
+ }
+}
+#endif
diff --git a/Runtime/Misc/MeshWelding.h b/Runtime/Misc/MeshWelding.h
new file mode 100644
index 0000000..10539c2
--- /dev/null
+++ b/Runtime/Misc/MeshWelding.h
@@ -0,0 +1,17 @@
+#ifndef MESH_WELDING_H_
+#define MESH_WELDING_H_
+
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Modules/ExportModules.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/AssetPipeline/ImportMesh.h"
+
+void WeldVertices (ImportMesh& mesh);
+#endif
+
+bool EXPORT_COREMODULE WeldVertexArray(dynamic_array<Vector3f>& vertices, Mesh::BoneInfluenceContainer& skin, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap);
+bool EXPORT_COREMODULE WeldVertexArray(dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap);
+
+
+#endif // UNITY_CUSTOM_ALLOCATOR_H_
diff --git a/Runtime/Misc/MessageParameters.h b/Runtime/Misc/MessageParameters.h
new file mode 100644
index 0000000..97a4669
--- /dev/null
+++ b/Runtime/Misc/MessageParameters.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Runtime/Math/Vector3.h"
+#include <list>
+
+class Collider;
+class Rigidbody;
+struct MonoObject;
+
+struct ContactPoint
+{
+ Collider* collider[2];
+ Vector3f point;
+ Vector3f normal;
+};
+
+struct Collision
+{
+ int status;
+
+ bool flipped;
+
+ Rigidbody* thisRigidbody;
+ Rigidbody* otherRigidbody;
+ Collider* thisCollider;
+ Collider* otherCollider;
+
+ Vector3f impactForceSum;
+ Vector3f frictionForceSum;
+ Vector3f relativeVelocity;
+ typedef std::list<ContactPoint> Contacts;
+ Contacts contacts;
+};
+
+ScriptingObjectPtr ConvertContactToMono (Collision* input);
diff --git a/Runtime/Misc/Player.cpp b/Runtime/Misc/Player.cpp
new file mode 100644
index 0000000..cf4d66b
--- /dev/null
+++ b/Runtime/Misc/Player.cpp
@@ -0,0 +1,2228 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Configuration/UnityConfigureVersion.h"
+#include "Player.h"
+#include "QualitySettings.h"
+#include "PreloadManager.h"
+#if SUPPORT_REPRODUCE_LOG
+#include "ReproductionLog.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#endif
+#include "SaveAndLoadHelper.h"
+#include "Runtime/BaseClasses/ManagerContextLoading.h"
+#include "SceneUnloading.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "PreloadManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "PlayerSettings.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Runtime/Serialize/PathNamePersistentManager.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferUtility.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "GameObjectUtility.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#if ENABLE_UNITYGUI
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/IMGUI/GUIStyle.h"
+#endif
+#if ENABLE_WWW
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h"
+#include "Runtime/Utilities/ReportHardware.h"
+#endif
+#if WEBPLUG
+#include "PlatformDependent/CommonWebPlugin/WebScripting.h"
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h"
+#endif
+#if UNITY_EDITOR
+#include "Editor/Src/Application.h"
+#endif // UNITY_EDITOR
+#include "Runtime/Graphics/ScreenManager.h"
+#include "BuildSettings.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Camera/LODGroupManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "DebugUtility.h"
+#include "Runtime/Filters/Mesh/MeshRenderer.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Player.h"
+#include "CaptureScreenshot.h"
+#include "Runtime/Input/GetInput.h"
+#include "ResourceManager.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Camera/RenderLayers/GUITexture.h"
+#include "Runtime/Profiler/ProfilerHistory.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/GPUProfiler.h"
+#include "Runtime/Profiler/MemoryProfilerStats.h"
+#include "Runtime/Profiler/ProfilerConnection.h"
+#include "Runtime/Profiler/MemoryProfiler.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/BaseClasses/Cursor.h"
+#include "Runtime/Allocator/MemoryManager.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+
+
+#if ENABLE_CACHING
+#include "CachingManager.h"
+#endif
+#include "Runtime/Utilities/RecursionLimit.h"
+#if ENABLE_MOVIES || ENABLE_WEBCAM
+#include "Runtime/Video/BaseVideoTexture.h"
+#endif
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/Camera/IntermediateRenderer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h" // ParticleSystem::UpdateAll ()
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "InputEvent.h"
+#include "SystemInfo.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/Graphics/SubstanceSystem.h"
+#include "Runtime/Filters/Misc/Font.h"
+#include "Runtime/Profiler/DeprecatedFrameStatsProfiler.cpp" // hack until jamplus is finished
+#include "Runtime/Graphics/DrawSplashScreenAndWatermarks.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+
+#if UNITY_WII
+#include "PlatformDependent/Wii/WiiMoviePlayer.h"
+#include "PlatformDependent/Wii/WiiLoadingScreen.h"
+#elif UNITY_XENON
+#include "PlatformDependent/Xbox360/Source/Kinect/Kinect.h"
+#elif UNITY_WIN
+#include "PlatformDependent/Win/WinUtils.h"
+#elif UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#elif UNITY_IPHONE
+#include "PlatformDependent/iPhonePlayer/iphoneNativeEvents.h"
+#endif
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+#include "PlatformDependent/CommonWebPlugin/WebScripting.h"
+#endif
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+#include "Runtime/Input/OnScreenKeyboard.h"
+#endif
+
+#if ENABLE_CLUSTER_SYNC
+#include "Runtime/Interfaces/IClusterRenderer.h"
+#endif
+
+#include <ctype.h>
+#include "Runtime/Interfaces/IAudio.h"
+
+
+static bool PlayerInitEngineLoadData ();
+static bool LoadQueuedWebPlayerData ();
+static int gPluginVersion=0;
+static bool s_InsidePlayerLoop = false;
+
+void (*g_PresentCallback)(bool before);
+
+using namespace std;
+
+static const char* kMainDataSharedAssets = "sharedassets0.assets";
+static const char* kExtraResourcesPath = "Resources/unity_builtin_extra";
+
+#if UNITY_WIN && WEBPLUG
+ static const char* kDefaultResourcePath = "Data/unity default resources";
+#elif UNITY_WIN || UNITY_XENON || UNITY_PS3
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_OSX
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_WII
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_IPHONE
+ static const char* kDefaultResourcePath = "Data/unity default resources";
+#elif UNITY_ANDROID
+ static const char* kDefaultResourcePath = "Data/unity default resources";
+#elif UNITY_PEPPER
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_BB10
+ static const char* kDefaultResourcePath = "unity default resources";
+#elif UNITY_TIZEN
+ static const char* kDefaultResourcePath = "unity default resources";
+#elif UNITY_LINUX
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_FLASH
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+#elif UNITY_WEBGL
+ static const char* kDefaultResourcePath = "Resources/unity_default_resources";
+#else
+ static const char* kDefaultResourcePath = "Resources/unity default resources";
+ #error "Unknown platform"
+#endif
+
+#if WEBPLUG
+#if UNITY_WIN
+static const char* kDefaultOldResourcePath = "Data/unity_web_old";
+#else
+static const char* kDefaultOldResourcePath = "Resources/unity_web_old";
+#endif
+#endif // #if WEBPLUG
+
+#if ENABLE_HARDWARE_INFO_REPORTER
+static HardwareInfoReporter s_HardwareInfoReport;
+#endif
+
+#if ENABLE_GAMECENTER
+namespace GameCenter
+{
+ void ExecuteGameCenterCallbacks();
+}
+#endif
+
+#if ENABLE_MEMORY_TRACKING
+std::auto_ptr<ScopedMemLeakDetector> m_MemLeakDetector = new ScopedMemLeakDetector();
+#endif
+
+struct LevelLoading
+{
+ int m_ActiveLoadedLevel;
+
+ typedef dynamic_array<PPtr<Object> > DontDestroyOnLoadSet;
+ DontDestroyOnLoadSet m_DontDestroyOnLoad;
+
+ public:
+
+ void LoadLevel (int index, const std::string& path, AwakeFromLoadQueue& awakeFromLoadQueue);
+
+ LevelLoading () { ResetLoadLevel (-1); }
+
+ void ResetLoadLevel (int activeLoadedLevel);
+
+ int GetLoadedLevelIndex () { return m_ActiveLoadedLevel; }
+
+ void DontDestroyOnLoad (Object& object)
+ {
+ m_DontDestroyOnLoad.push_back (&object);
+ }
+
+ bool m_hasLateBoundLevel;
+ UnityStr m_lateBoundLevelName;
+ UnityStr m_tempLateBoundLevelName;
+};
+
+
+void LevelLoading::ResetLoadLevel (int activeLoadedLevel)
+{
+ m_DontDestroyOnLoad.clear();
+ m_ActiveLoadedLevel = activeLoadedLevel;
+ m_hasLateBoundLevel = false;
+ m_lateBoundLevelName.clear();
+ m_tempLateBoundLevelName.clear();
+}
+
+static LevelLoading gLoadLevel;
+
+bool GetHasLateBoundLevelFromAssetBundle (const string& name)
+{
+ string levelPath = Format("BuildPlayer-%s", name.c_str());
+ bool exists = GetPersistentManager().HasMemoryOrCachedSerializedFile(levelPath);
+ return exists;
+}
+
+bool GetLevelAndAssetPath (const std::string& levelName, int levelIndex, std::string* outLevelPath, std::string* outAssetPath, int* outIndex)
+{
+ *outLevelPath = "";
+ *outAssetPath = "";
+ *outIndex = -1;
+
+ string levelPath;
+ if (levelIndex != -1)
+ {
+ levelPath = GetBuildSettings().GetLevelPathName (levelIndex);
+ }
+ else
+ {
+ // Late bound level (via asset bundle)
+ if (GetHasLateBoundLevelFromAssetBundle(levelName))
+ {
+ *outLevelPath = Format("BuildPlayer-%s", levelName.c_str());
+ *outAssetPath = Format("BuildPlayer-%s.sharedAssets", levelName.c_str());
+ *outIndex = -1;
+
+ gLoadLevel.m_tempLateBoundLevelName = levelName;
+
+ return true;
+ }
+ // Level included via BuildSettings
+ else
+ {
+ levelIndex = GetBuildSettings().GetLevelIndex(levelName);
+ levelPath = GetBuildSettings().GetLevelPathName (levelIndex);
+ }
+ }
+
+ const char* cantLoadLevelErrorMessage = "Level '%s' (%d) couldn't be loaded because it has not been added to the build settings.\nTo add a level to the build settings use the menu File->Build Settings...";
+ // Verify that the level is accessable
+ #if WEBPLUG
+ const char* cantStreamLoadLevelErrorMessage = "Level '%s' (%d) couldn't be loaded because it has not been streamed in yet. You need to ensure that levels which have not been streamed in yet are not loaded.";
+ if (CompressedFileStream::Get().Find(levelPath) == NULL && !IsFileCreated (levelPath))
+ {
+ if (levelPath.empty ())
+ {
+ ErrorString (Format (cantLoadLevelErrorMessage, levelName.c_str(), levelIndex));
+ }
+ else
+ {
+ ErrorString (Format (cantStreamLoadLevelErrorMessage, levelName.c_str(), levelIndex));
+ }
+ return false;
+ }
+ #else
+ if (!IsFileCreated (levelPath))
+ {
+ ErrorString (Format (cantLoadLevelErrorMessage, levelName.c_str(), levelIndex));
+ return false;
+ }
+ #endif
+
+ // Extract
+ *outLevelPath = levelPath;
+ *outIndex = levelIndex;
+ #if UNITY_EDITOR
+ *outAssetPath = "";
+ #else
+ *outAssetPath = Format("sharedassets%d.assets", levelIndex);
+ #endif
+
+ return true;
+}
+
+int GetPluginVersion()
+{
+ return gPluginVersion;
+}
+
+static int gTargetFrameRate = -1;
+
+static PlayerPause gPlayerPause = kPlayerRunning;
+
+#if !UNITY_EDITOR
+static bool gIsFirstFrame = true;
+static bool gHasFrameToPresent = false;
+#endif
+
+#if WEBPLUG
+static UnityWebStream* gLoadUnityWebData = NULL;
+#endif
+
+#if WEBPLUG || UNITY_EDITOR
+void ResetPlayerInWebPlayer (int level)
+{
+ gLoadLevel.ResetLoadLevel(level);
+ if (GetLODGroupManagerPtr())
+ GetLODGroupManager().ResetLODBias();
+
+
+ StopPreloadManager();
+
+ GetUserAuthorizationManager ().Reset();
+
+ #if WEBPLUG
+ gDisplayFullscreenEscapeTimeout = -1000.0;
+ gTargetFrameRate = -1;
+ AssertIf(gLoadUnityWebData != NULL);
+ #endif
+}
+#endif
+
+#if UNITY_EDITOR
+void ResetPlayerInEditor (int level)
+{
+ ResetPlayerInWebPlayer(level);
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->AudioManagerAwakeFromLoad(kDefaultAwakeFromLoad);
+
+ #if ENABLE_CACHING
+ GetCachingManager().Reset();
+ #endif
+ StopPreloadManager();
+}
+#endif
+
+void DontDestroyOnLoad (Object& object)
+{
+ gLoadLevel.DontDestroyOnLoad(object);
+}
+
+///// @TODO CLEANUP THIS SHIT
+void PlayerLoadLevelFromThread (int levelIndex, const std::string& name, AwakeFromLoadQueue& loadQueue)
+{
+ gLoadLevel.LoadLevel(levelIndex, name, loadQueue);
+}
+
+bool IsLoadingLevel ()
+{
+ return GetPreloadManager().IsLoadingOrQueued();
+}
+
+void SetPlayerPause (PlayerPause pause)
+{
+ if (gPlayerPause == pause)
+ return;
+ if ((kPlayerPaused == gPlayerPause) && (kPlayerPausing == pause))
+ return;
+
+ // Pausing is sometimes called when unity is not yet initialized
+ // Happens on windows gles20 emulator. It seems like the WM_ACTIVATE function can be called
+ // Prior to the context having been created.
+ if (GetBuildSettingsPtr() == NULL)
+ return;
+
+#if ENABLE_AUDIO
+ bool pauseAudio = ( pause != kPlayerRunning );
+#if UNITY_EDITOR
+ pauseAudio = pauseAudio || GetApplication().IsPaused();
+#endif // UNITY_EDITOR
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->SetPause( pauseAudio );
+#endif // ENABLE_AUDIO
+
+ GetTimeManager().SetPause( pause == kPlayerPaused );
+ if (kPlayerPaused == pause)
+ {
+ GetScreenManager().SetCursorInsideWindow(false);
+ GetScreenManager().SetAllowCursorLock(false);
+ }
+
+ if (kPlayerRunning == pause)
+ ResetInputAfterPause();
+
+ gPlayerPause = pause;
+
+ if ((kPlayerRunning == pause) || (kPlayerPaused == pause))
+ {
+ MessageData data;
+ data.SetData((kPlayerPaused == pause), ClassID (bool));
+ SendMessageToEveryone (kPlayerPause, data);
+ }
+}
+
+PlayerPause GetPlayerPause(void)
+{
+ return gPlayerPause;
+}
+
+void SetPlayerFocus(bool focus)
+{
+ if (GetBuildSettingsPtr() == NULL)
+ return;
+
+#if !WEBPLUG
+ // Reset input state if focus is being lost as we don't want such state if we resume focus.
+ if (!focus)
+ ResetInput();
+#endif
+
+ static bool focusEventSupported = (GetBuildSettings().GetIntVersion() > GetNumericVersion("2.6.1f3"));
+
+ if (focusEventSupported)
+ {
+ MessageData data;
+ data.SetData(focus, ClassID(bool));
+ SendMessageToEveryone(kPlayerFocus, data);
+ }
+}
+
+bool NotifyPlayerQuit(bool forceQuit)
+{
+ // We unloaded the data file in the web player already. Dont use any managers at this point
+ if (GetManagerPtrFromContext (ManagerContext::kPlayerSettings) == NULL)
+ return true;
+
+ GetInputManager().QuitApplication();
+
+ SendMessageToEveryone (kPlayerQuit, MessageData());
+
+ // During SendMessageToEveryone (kPlayerQuit, MessageData()); scripts can set m_ShouldQuit to false.
+ // This cancels the quit operation
+ Assert(forceQuit || (!WEBPLUG && !UNITY_EDITOR && !UNITY_IPHONE && !UNITY_WINRT));
+ if (!forceQuit && !GetInputManager().ShouldQuit())
+ return false;
+
+ #if ENABLE_MOVIES || ENABLE_WEBCAM
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->StopVideoTextures ();
+ #endif
+
+ #if WEBPLUG
+ // This will unfortunately kill all external calls issued from OnApplicationQuit.
+ // Not doing this would make them execute on the next player launch though.
+ WebScripting::Get().ClearExternalCalls();
+ #endif
+
+ #if ENABLE_NETWORK
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kNetworkManager, NetworkOnApplicationQuit());
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kMasterServerInterface, NetworkOnApplicationQuit());
+ #endif
+
+ // When exiting player. Reset show cursor
+ GetScreenManager().SetShowCursor(true);
+ GetScreenManager().SetCursorInsideWindow(false);
+ GetScreenManager().SetLockCursor(false);
+ GetScreenManager().SetAllowCursorLock(false);
+
+ // Stop any tasks on the preload manager
+ StopPreloadManager();
+
+ #if WEBPLUG
+ if (gLoadUnityWebData)
+ {
+ gLoadUnityWebData->Release();
+ gLoadUnityWebData = NULL;
+ }
+ #endif
+
+ return true;
+}
+
+void ProcessMouseInWindow()
+{
+ // In Core Animation plugin, rely on MouseEntered/MouseExited events instead.
+ // Otherwise we hide the cursor when our window is not in front, as we cannot find out when that is the case.
+ #if WEBPLUG && UNITY_OSX && !UNITY_PEPPER
+ if (GetScreenManager().IsUsingCoreAnimation())
+ return;
+ #endif
+
+ #if UNITY_WIN && !UNITY_WINRT
+
+ bool inside = false;
+ POINT cursorPosition;
+
+ const BOOL getCursorPositionResult = GetCursorPos(&cursorPosition);
+ //Assert(FALSE != getCursorPositionResult); // happens when windows is locked
+
+ if (FALSE != getCursorPositionResult)
+ {
+ const HWND window = WindowFromPoint(cursorPosition);
+
+ if (NULL != window)
+ {
+ ScreenManagerPlatform& screen = GetScreenManager();
+ if (screen.GetWindow() == window)
+ {
+ POINT pt = cursorPosition;
+ ScreenToClient (window, &pt);
+ inside = (pt.x >= 0 && pt.y >= 0 && pt.x < screen.GetWidth() && pt.y < screen.GetHeight());
+ }
+ }
+ }
+
+ #else
+
+ bool inside = true;
+ Vector2f pos = GetInputManager().GetMousePosition();
+ if (pos.x < 0.0F || pos.x > GetScreenManager().GetWidth())
+ inside = false;
+ if (pos.y < 0.0F || pos.y > GetScreenManager().GetHeight())
+ inside = false;
+
+ #endif
+
+ GetScreenManager().SetCursorInsideWindow(inside);
+}
+
+bool GetPlayerRunInBackground()
+{
+ if(GetPlayerSettingsPtr() == NULL)
+ return false;
+ #if SUPPORT_REPRODUCE_LOG
+ return GetPlayerSettings().runInBackground || RunningReproduction();
+ #endif
+ return GetPlayerSettings().runInBackground;
+}
+
+void SetPlayerRunInBackground(bool runInBackground)
+{
+ if (runInBackground != GetPlayerSettings().runInBackground)
+ {
+ GetPlayerSettings().runInBackground = runInBackground;
+ GetPlayerSettings().SetDirty();
+
+ if (runInBackground)
+ SetPlayerPause(kPlayerRunning);
+ }
+}
+
+#if !WEBPLUG && !UNITY_EDITOR
+static string CalculateStandaloneDataFolder ()
+{
+ #if UNITY_OSX
+
+ // OS X:
+ // - Try AppPath/Contents/Data folder
+ // - Try AppPath/../Data folder
+ string appContentsFolder = AppendPathName (GetApplicationPath (), "Contents/Data");
+ string appDataFolder = AppendPathName (GetApplicationFolder (), "Data");
+ if (IsFileCreated (AppendPathName (appContentsFolder, kMainData)))
+ return appContentsFolder;
+ else if (IsFileCreated (AppendPathName (appDataFolder, kMainData)))
+ return appDataFolder;
+
+ #elif UNITY_WINRT
+
+ return AppendPathName(GetApplicationFolder(), "Data");
+
+ #elif UNITY_WIN || UNITY_LINUX
+
+ // - Try ExeName_Data
+ // - Try Data folder
+ string appPath = GetApplicationPath();
+ appPath = DeletePathNameExtension(appPath);
+ string path = appPath + "_Data";
+ if (IsFileCreated (AppendPathName (path, kMainData)))
+ return path;
+ string appDataFolder = AppendPathName (GetApplicationFolder (), "Data");
+ if (IsFileCreated (AppendPathName (appDataFolder, kMainData)))
+ return appDataFolder;
+
+ #elif UNITY_XENON
+
+ return "game:\\Media";
+
+ #elif UNITY_PS3
+
+ return AppendPathName (GetApplicationFolder (), "Media");
+
+ #elif UNITY_IPHONE
+
+ return AppendPathName(GetApplicationPath(), "Data");
+ #elif UNITY_ANDROID
+ return AppendPathName(GetApplicationPath(), "assets/bin/Data");
+ #elif UNITY_BB10
+ return AppendPathName(GetApplicationFolder(), "app/native/Data");
+ #elif UNITY_TIZEN
+ return AppendPathName(GetApplicationPath(), "data");
+ #elif UNITY_WII
+ return "unity/Data";
+ #elif UNITY_FLASH || UNITY_WEBGL
+ return "";
+ #else
+ #error "Unknown platform"
+ #endif
+
+ return "";
+}
+
+string SelectDataFolder ()
+{
+ static bool s_DataFolderDone = false;
+ static StaticString s_DataFolder;
+ if (!s_DataFolderDone)
+ {
+ s_DataFolder = CalculateStandaloneDataFolder ().c_str ();
+ s_DataFolderDone = true;
+ }
+ return s_DataFolder.c_str ();
+}
+#endif
+
+#if UNITY_STANDALONE
+string GetApplicationNativeLibsPath()
+{
+# if UNITY_OSX
+ return AppendPathName (GetApplicationPath (), "Contents/Frameworks/MonoEmbedRuntime/osx");
+# else
+ string monoBaseFolder = AppendPathName (SelectDataFolder (), "Mono");
+ string x86_64Folder = AppendPathName (monoBaseFolder, "x86_64");
+ string x86Folder = AppendPathName (monoBaseFolder, "x86");
+
+ if (IsDirectoryCreated (x86_64Folder))
+ return x86_64Folder;
+ if (IsDirectoryCreated (x86Folder))
+ return x86Folder;
+ return monoBaseFolder;
+# endif
+}
+#endif
+
+#if UNITY_EDITOR
+string GetApplicationNativeLibsPath()
+{
+# if UNITY_OSX
+ return AppendPathName (GetApplicationPath (), "Contents/Frameworks/Mono/lib");
+# else
+ return AppendPathName (GetApplicationContentsPath (), "Mono/lib");
+# endif
+}
+#endif
+
+static void AddPathRemapsForBuiltinResources (const string& applicationContentsFolderPath)
+{
+ GetPersistentManager ().SetPathRemap ("library/unity default resources", AppendPathName (applicationContentsFolderPath, kDefaultResourcePath));
+
+#if !WEBPLUG
+ // Remap "resources/unity_builtin_extra" to "Resources/unity_builtin_extra" to deal
+ // with case-sensitive file systems.
+ //
+ // In the webplayer, the file is contained in the webstream rather than being bundled
+ // with the player, so no remapping necessary there.
+ std::string extraResourcesLower = kExtraResourcesPath;
+ ToLowerInplace(extraResourcesLower);
+ GetPersistentManager ().SetPathRemap (extraResourcesLower, kExtraResourcesPath);
+#endif
+
+#if WEBPLUG
+ GetPersistentManager ().SetPathRemap ("library/unity_web_old", AppendPathName (applicationContentsFolderPath, kDefaultOldResourcePath));
+#endif
+}
+
+bool PlayerInitEngineNoGraphics (const string& dataFolder, const string& applicationContentsFolderPath)
+{
+/*
+#if ENABLE_THREAD_CHECK_IN_ALLOCS
+ g_ForwardFrameAllocator.SetThreadIDs (Thread::GetCurrentThreadID (), Thread::GetCurrentThreadID ());
+#endif
+*/
+ /// Beta running out!
+ #ifdef TIME_LIMIT
+ if (CheckDate (TIME_LIMIT, "Unity Player beta has run out!"))
+ {
+ printf_console ("Unity Player beta has run out!\n");
+ return false;
+ }
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ BatchInitializeReproductionLog();
+ #endif
+
+ if (!IsFileCreated (AppendPathName (dataFolder, kMainData)))
+ {
+ printf_console ("No mainData file was found, quitting player!\n");
+ return false;
+ }
+
+ InitPathNamePersistentManager();
+
+ File::SetCurrentDirectory (dataFolder);
+
+ AddPathRemapsForBuiltinResources (applicationContentsFolderPath);
+
+ // Initialize Engine
+ if (!InitializeEngineNoGraphics ())
+ {
+ printf_console( "PlayerInitEngineNoGraphics: InitializeEngine failed\n" );
+ return false;
+ }
+
+ string error = PlayerLoadSettingsAndInput(kMainData);
+ if (!error.empty())
+ {
+ #if UNITY_WIN
+ winutils::AddErrorMessage( error.c_str() );
+ #endif
+ printf_console( "PlayerInitEngineNoGraphics settings: %s\n", error.c_str() );
+ return false;
+ }
+
+ return true;
+}
+
+bool PlayerInitEngineGraphics (bool batchmode)
+{
+ // Initialize Engine (eg. Mastercontext, Shaderlab, Setup RTTI, Call Static Initialize functions, Setup messages and Tags)
+ if (!InitializeEngineGraphics (batchmode))
+ {
+ #if UNITY_WIN
+ winutils::AddErrorMessage( "InitializeEngineGraphics failed" );
+ #endif
+ printf_console( "PlayerInitEngineGraphics: InitializeEngineGraphics failed\n" );
+ return false;
+ }
+
+ // Check if GPU is supported
+ std::string gpuMsg = gGraphicsCaps.CheckGPUSupported();
+ if (!gpuMsg.empty())
+ {
+ #if UNITY_WIN
+ winutils::AddErrorMessage (gpuMsg.c_str());
+ #endif
+ printf_console ("PlayerInitEngineGraphics: GPU not supported; %s\n", gpuMsg.c_str());
+ return false;
+ }
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ s_HardwareInfoReport.ReportHardwareInfo();
+ #endif
+
+ // Load all game managers! All global game managers are coming first in the main data
+ // (Global game managers have to be loaded before selecting the screen resolution.
+ // ProjectSettings i used by screen selector and RenderManager, InputManager by screen switching)
+ string error = PlayerLoadGlobalManagers(kMainData);
+ if (!error.empty())
+ {
+ printf_console( "PlayerInitEngineGraphics: %s\n", error.c_str() );
+ return false;
+ }
+
+#if !UNITY_EDITOR
+ // We can't do this in the editor, because this will get the default assets.
+ // When the editor is running, the default assets are often being built, and that
+ // will cause exceptions.
+
+ // By Ian's request moving this and : IAN FIXME YESTERDAY.
+ GUIStyle::SetDefaultFont(NULL);
+#endif
+
+ return true;
+}
+
+bool PlayerInitEngineWebNoGraphics (const string& applicationContentsFolderPath, int pluginversion)
+{
+
+
+ #if SUPPORT_REPRODUCE_LOG && UNITY_OSX
+ BatchInitializeReproductionLog();
+ #endif
+ gPluginVersion = pluginversion;
+
+ InitPathNamePersistentManager();
+
+#if !UNITY_FLASH
+ File::SetCurrentDirectory (applicationContentsFolderPath);
+#endif
+
+ AddPathRemapsForBuiltinResources (applicationContentsFolderPath);
+
+ // Initialize base classes (things that are not dependent on graphics)
+ if (!InitializeEngineNoGraphics ())
+ {
+ printf_console( "PlayerInitEngineNoGraphics: InitializeEngine failed\n" );
+ return false;
+ }
+ return true;
+}
+
+bool PlayerInitEngineWebGraphics ()
+{
+ // Initialize ShaderLab, default resources etc. (things that are dependent on graphics)
+ if (!InitializeEngineGraphics (false))
+ {
+ printf_console( "PlayerInitEngineGraphics: InitializeEngineGraphics failed\n" );
+ return false;
+ }
+
+ // Check if GPU is supported
+ std::string gpuMsg = gGraphicsCaps.CheckGPUSupported();
+ if (!gpuMsg.empty())
+ {
+ printf_console ("PlayerInitEngineGraphics: GPU not supported; %s\n", gpuMsg.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+
+bool PlayerInitEngineWeb (const string& applicationContentsFolderPath, int pluginversion)
+{
+ if (!PlayerInitEngineWebNoGraphics(applicationContentsFolderPath, pluginversion))
+ return false;
+ if (!PlayerInitEngineWebGraphics())
+ return false;
+ return true;
+}
+
+
+static bool PlayerLoadNewSettings ()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ std::string error = PlayerLoadSettingsAndInput(kMainData);
+ if (!error.empty())
+ {
+ printf_console( "PlayerInitEngineReload settings: %s\n", error.c_str() );
+ return false;
+ }
+
+ // Print version number that our content was built with.
+ printf_console( "Loading webdata version: %s\n", GetBuildSettings().GetVersion().c_str() );
+
+ return true;
+}
+#if WEBPLUG
+
+static bool PlayerInitEngineLoadData ()
+{
+ #if UNITY_WIN
+ // Windows web player only calls PlayerInitEngineWebNoGraphics at initialization time;
+ // graphisc part is deferred until here, when we have player settings loaded (to be
+ // able to choose between DX9 & DX11).
+ if (!PlayerInitEngineWebGraphics ())
+ {
+ printf_console("web: failed to initialize graphics\n");
+ return false;
+ }
+
+ #endif
+
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ // In web player, report hardware info after loading BuildSettings
+ s_HardwareInfoReport.ReportHardwareInfo();
+ #endif
+
+
+ // Load all game managers! All global game managers are coming first in the main data
+ // (Global game managers have to be loaded before selecting the screen resolution.
+ // ProjectSettings is used by screen selector and RenderManager, InputManager by screen switching)
+ std::string error = PlayerLoadGlobalManagers(kMainData);
+ if (!error.empty())
+ {
+ printf_console( "PlayerInitEngineReload: %s\n", error.c_str() );
+ return false;
+ }
+
+ // Some graphics caps need to be adjusted based on content build version,
+ // which is only available now.
+ gGraphicsCaps.InitWithBuildVersion();
+
+ return true;
+}
+
+bool PlayerResetPreferences(UnityWebStream& stream, const std::string absoluteURL, bool clearPlayerPrefs /*= true*/)
+{
+ ResetPlayerInWebPlayer(0);
+ gPlayerPause = kPlayerRunning;
+ if (clearPlayerPrefs)
+ PlayerPrefs::Init(absoluteURL);
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ PlayerPrefs::DeleteAll();
+ #endif
+
+ FileStream::SetDataFile(stream.GetFileStream());
+ stream.SetAddStreamedFilesToPersistentManager(true);
+
+ return PlayerLoadNewSettings();
+}
+
+bool PlayerLoadEngineData (UnityWebStream& stream, const std::string srcValue, const std::string absoluteURL)
+{
+ // Load data
+ if (!PlayerInitEngineLoadData())
+ {
+ return false;
+ }
+
+ GetPlayerSettings().srcValue = srcValue;
+ GetPlayerSettings().absoluteURL = absoluteURL;
+ GetUserAuthorizationManager ().Reset();
+ #if SUPPORT_REPRODUCE_LOG
+ ReadWriteAbsoluteUrl(GetPlayerSettings().srcValue, GetPlayerSettings().absoluteURL);
+
+ // Extract dll's to disk when creating reproduction log
+ if (GetReproduceMode() == kGenerateReproduceLog || GetReproduceMode() == kGenerateReproduceLogAndRemapWWW || GetReproduceMode() == kPlaybackReproduceLog)
+ {
+ AssertIf(stream.GetFileStream()->GetType() != kCompressedFileStreamMemoryType);
+
+ for (CompressedFileStreamMemory::iterator i=stream.GetFileStream()->begin();i != stream.GetFileStream()->end();i++)
+ {
+ if (!StrICmp(GetPathNameExtension(i->name), "dll"))
+ {
+ CompressedFileStream::Data& data = *i;
+
+ string path = AppendPathName(GetReproductionDirectory(), data.name);
+
+ MemoryCacherReadBlocks cacher (data.blocks, data.end, kCacheBlockSize);
+ UInt8* dataCpy = (UInt8*)UNITY_MALLOC(kMemTempAlloc, data.GetSize()*sizeof(UInt8));
+ cacher.DirectRead(dataCpy, data.offset, data.GetSize());
+ WriteBytesToFile(dataCpy, data.GetSize(), path);
+ UNITY_FREE(kMemTempAlloc, dataCpy);
+ }
+ }
+ }
+
+ #endif
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->SetPause(kPlayerRunning);
+
+ return true;
+}
+
+bool PlayerLoadWebData (UnityWebStream& data, const std::string srcValue, const std::string absoluteURL, bool clearPlayerPrefs /*= true*/)
+{
+ return PlayerResetPreferences(data, absoluteURL, clearPlayerPrefs) &&
+ PlayerLoadEngineData(data, srcValue, absoluteURL);
+}
+
+
+bool QueuePlayerLoadWebData (UnityWebStream* stream)
+{
+ if (stream != NULL)
+ {
+ gLoadUnityWebData = stream;
+ gLoadUnityWebData->Retain();
+ return true;
+ }
+ else
+ {
+ ErrorString("No Valid Unity Web Stream was found in the download.");
+ return false;
+ }
+}
+
+static bool LoadQueuedWebPlayerData ()
+{
+ if (gLoadUnityWebData)
+ {
+ UnityWebStream* stream = gLoadUnityWebData;
+ gLoadUnityWebData = NULL;
+ if (!stream->IsReadyToPlay())
+ {
+ stream->Release();
+ return false;
+ }
+
+ string srcValue = GetPlayerSettings().srcValue;
+ string absoluteURL = GetPlayerSettings().absoluteURL;
+
+ if (!PlayerWebUnloadReloadable ())
+ {
+ stream->Release();
+ return false;
+ }
+
+ SetUnityWebStream(stream);
+
+ if (!PlayerLoadWebData (*stream, srcValue, absoluteURL, false))
+ {
+ return false;
+ }
+
+ PlayerLoadFirstLevel();
+
+ GetScreenManager().ReapplyWindowRect();
+ }
+
+ return true;
+}
+
+bool PlayerWebUnloadReloadable ()
+{
+ PlayerPrefs::Sync();
+
+
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ {
+ // Pause audio (Handle the case where no data file has been loaded yet)
+ if (GetManagerPtrFromContext (ManagerContext::kPlayerSettings))
+ audioModule->SetPause ( true );
+ }
+
+ gPlayerPause = kPlayerPaused;///WHY????
+
+ NotifyPlayerQuit (true);
+
+ ResetInput();
+
+ StopPreloadManager();
+
+ CleanupAllObjects(true);
+
+ #if ENABLE_MONO && !UNITY_PEPPER
+ if (!CleanupMonoReloadable())
+ return false;
+ #endif
+
+ GetPersistentManager().UnloadMemoryStreams();
+
+ GetUnityWebStream ().Release();
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ GetCachingManager ().Reset();
+ s_HardwareInfoReport.Shutdown();
+ #endif
+
+ SetUnityWebStream(NULL);
+ CompressedFileStream::SetDataFile(NULL);
+
+ return true;
+}
+
+#endif
+
+
+void PlayerLoadFirstLevel ()
+{
+#if UNITY_WII
+ wii::StartLoadingScreen();
+#elif UNITY_ANDROID
+ StartActivityIndicator();
+#endif
+
+ gLoadLevel.ResetLoadLevel(0);
+
+ /////@TODO: Clear preloadmanager queue
+ // Create an empty set of level managers
+
+#if DEBUGMODE
+ // (GlobalGameManagers are already loaded at this point)
+ for (int i=0;i<ManagerContext::kGlobalManagerCount;i++)
+ {
+ if (GetManagerPtrFromContext(i) == NULL)
+ printf_console ("GlobalManager '%s' is not included.\n", GetManagerContext().m_ManagerNames[i]);
+ }
+# endif
+
+ AwakeFromLoadQueue emptyQueue (kMemTempAlloc);
+ LoadManagers(emptyQueue);
+
+ // Load level async
+ PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (kMainData, kMainDataSharedAssets, 0, PreloadLevelOperation::kLoadMainData, true);
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+ op->Release();
+
+ GetTimeManager().ResetTime ();
+
+#if UNITY_WII
+ wii::EndLoadingScreen();
+#elif UNITY_ANDROID
+ StopActivityIndicator();
+#endif
+ //?@TODO MAKE WORK
+}
+
+void PlayerInitState(void)
+{
+ MessageData pauseData;
+ pauseData.SetData((kPlayerPaused == gPlayerPause), ClassID(bool));
+ SendMessageToEveryone(kPlayerPause, pauseData);
+
+ SetPlayerFocus(GetScreenManager().GetIsFocused());
+
+#if ENABLE_CLUSTER_SYNC
+ if(GetIClusterRenderer())
+ GetIClusterRenderer()->InitCluster();
+#endif
+}
+
+// Terminate player app.
+bool PlayerCleanup(bool cleanupEverything, bool forceQuit)
+{
+ gPlayerPause = kPlayerPaused;///WHY????
+
+ if (!NotifyPlayerQuit (forceQuit))
+ {
+ Assert(!forceQuit);
+ gPlayerPause = kPlayerRunning;
+ return false;
+ }
+
+#if ENABLE_CLUSTER_SYNC
+ if(GetIClusterRenderer())
+ GetIClusterRenderer()->ShutdownCluster();
+#endif
+
+ ReleasePreloadManager();
+
+ PlayerPrefs::Sync();
+
+#if ENABLE_PROFILER
+ UnityProfiler::CleanupGfx();
+#endif
+
+ InputShutdown();
+
+ if (cleanupEverything)
+ {
+ CleanupEngine();
+ #if ENABLE_MONO
+ CleanupMono();
+ #endif
+ }
+
+ CleanupPersistentManager();
+
+ #if ENABLE_PROFILER
+
+ #if UNITY_EDITOR
+ ProfilerHistory::Cleanup();
+ #endif
+
+ ProfilerConnection::Cleanup();
+ UnityProfiler::Cleanup();
+ #endif
+
+ #if ENABLE_PLAYERCONNECTION && !UNITY_EDITOR
+ // Send last bits (required for automated tests)
+ PlayerConnection::Get().SendMessage(ANY_PLAYERCONNECTION, GeneralConnection::kApplicationQuitMessage, NULL, 0);
+ PlayerConnection::Get().WaitForFinish();
+ PlayerConnection::Get().DisconnectAll();
+ PlayerConnection::Cleanup();
+ #endif
+
+ #if ENABLE_HARDWARE_INFO_REPORTER
+ s_HardwareInfoReport.Shutdown();
+ #endif
+
+ return true;
+}
+
+bool g_RuntimeInitialized = false;
+void RuntimeInitialize()
+{
+ if(g_RuntimeInitialized)
+ return;
+#if SUPPORT_THREADS
+ Thread::mainThreadId = Thread::GetCurrentThreadID ();
+#endif
+ g_RuntimeInitialized = true;
+ #if ENABLE_MEMORY_MANAGER
+ MemoryManager::StaticInitialize();
+ #endif
+
+ RegisterRuntimeInitializeAndCleanup::ExecuteInitializations();
+
+#if !UNITY_ANDROID
+ // atexit functions are called at process termination - _not_ when dlclose is called and the dynamic library is unloaded.
+ // Also, it seems plenty of platforms call RuntimeCleanup in a controlled fashion, instead of relying on the libc cleanup...
+ atexit(RuntimeCleanup);
+#endif
+}
+
+void RuntimeCleanup()
+{
+ if(!g_RuntimeInitialized)
+ return;
+ g_RuntimeInitialized = false;
+
+ RegisterRuntimeInitializeAndCleanup::ExecuteCleanup();
+
+ File::CleanupClass();
+
+#if ENABLE_MEMORY_MANAGER
+#if !UNITY_WP8 // don't touch memory managers because managed finalizers can try to release memory after cleanup. todo: check if metro relates to this issue
+ GetMemoryManager().FrameMaintenance(true);
+// size_t remainingMemory = GetMemoryManager().GetTotalAllocatedMemory() - GetMemoryManager().GetTotalProfilerMemory();
+// Assert(remainingMemory < 100*1024);
+#if ENABLE_STACKS_ON_ALL_ALLOCS
+ ProfilerString leaks = GetMemoryProfiler()->GetOverview();
+ printf("%s",leaks.c_str());
+#endif
+ MemoryManager::StaticDestroy();
+#endif
+
+#endif // #if ENABLE_MEMORY_MANAGER
+}
+
+static void ResetRandomsAfterLevelLoad ()
+{
+ // reset all internal Random number generators after level load.
+ GlobalCallbacks::Get().resetRandomAfterLevelLoad.Invoke ();
+}
+
+void LevelLoading::LoadLevel (int index, const string& path, AwakeFromLoadQueue& loadQueue)
+{
+#if UNITY_WII
+ wii::StartLoadingScreen();
+#endif
+ set<SInt32> temporaryObjects;
+
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ KeyboardOnScreen::Hide();
+#endif
+
+ // Collect dont destroy on loading objects!
+ DontDestroyOnLoadSet::iterator it=m_DontDestroyOnLoad.begin ();
+ while (it != m_DontDestroyOnLoad.end ())
+ {
+ // This is a better way of doing it.
+ // Object* object = Object::IDToPoint(i->GetInstanceID());
+ Object* object = *it;
+ if (object)
+ {
+ CollectPPtrs (*object, &temporaryObjects);
+ ++it;
+ }
+ else
+ {
+ (*it) = m_DontDestroyOnLoad.back();
+ m_DontDestroyOnLoad.pop_back();
+ }
+ }
+
+ // - Deactivate all game objects that are marked not to be destroyed in scene load
+ /// They are deactivate with kDeactivateToggleForLevelLoad. Some components handle this,
+ /// eg. audio will ignore the deactivate completely.
+ // - Make them all temp so they dont get killed
+ typedef set<PPtr<GameObject> > NeedActivation;
+ NeedActivation needActivation;
+ typedef set<PPtr<Object> > NeedsIsTemporaryReset;
+ NeedsIsTemporaryReset needsIsTemporaryReset;
+
+ for (set<SInt32>::iterator i=temporaryObjects.begin ();i != temporaryObjects.end ();i++)
+ {
+ Object* object = PPtr<Object> (*i);
+ if (object)
+ {
+ #if UNITY_EDITOR
+ // In the editor we never unload assets anyway. Thus we shouldn't mess with assets in that case
+ bool needsDontSaveFlag = !object->TestHideFlag (Object::kDontSave) && !object->IsPersistent();
+ #else
+ // Only mark objects that are set to DontSave
+ bool needsDontSaveFlag = !object->TestHideFlag (Object::kDontSave);
+ #endif
+
+ if (needsDontSaveFlag)
+ {
+ needsIsTemporaryReset.insert (object);
+ // game objects don't apply the same to all components
+ object->SetHideFlagsObjectOnly (object->GetHideFlags() | Object::kDontSave);
+ }
+
+ // We used to Deactivate / Activate game objects between level reloads but now we instead keep all managers that track objects always around.
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ // Deactivate the game!
+ GameObject* go = dynamic_pptr_cast<GameObject*> (object);
+ if (go && go->IsActive ())
+ {
+ needActivation.insert (go);
+ go->Deactivate (kDeprecatedDeactivateToggleForLevelLoad);
+ }
+ }
+ }
+ }
+
+ // Movie textures are assets thus might not get unloaded after a level load.
+ // However since the playback code is on the asset, we should just unpause all videos after loading as a convenience to the user.
+ // (Optimally a movie playback component would be what drives the movie playback code instead, but we are not doing that...)
+ #if ENABLE_MOVIES || ENABLE_WEBCAM
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->PauseVideoTextures ();
+ #endif
+
+ // Load level
+ UnloadGameScene ();
+
+ // Reset the temporary state
+ for (NeedsIsTemporaryReset::iterator i=needsIsTemporaryReset.begin ();i != needsIsTemporaryReset.end ();i++)
+ {
+ Object* object = *i;
+ if (object)
+ {
+ int flags = object->GetHideFlags();
+ flags &= ~Object::kDontSave;
+ // Don't apply the same to all components
+ object->SetHideFlagsObjectOnly (flags);
+ }
+ }
+
+ m_ActiveLoadedLevel = index;
+ m_hasLateBoundLevel = false;
+
+ if (m_ActiveLoadedLevel < 0)
+ {
+ // If the m_ActiveLoadedLevel is -1, this means we're loading from an asset bundle.
+ m_hasLateBoundLevel = true;
+
+ m_lateBoundLevelName = m_tempLateBoundLevelName;
+ m_tempLateBoundLevelName.clear();
+ }
+
+ CompletePreloadManagerLoadLevel (path, loadQueue);
+
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ // Activate the game objects that were marked not to be destroyed on scene load
+ for (NeedActivation::iterator i=needActivation.begin ();i != needActivation.end ();i++)
+ {
+ GameObject* go = *i;
+ if (go != NULL && !go->IsActive ())
+ {
+ go->Activate ();
+ }
+ else
+ {
+ #if UNITY_EDITOR
+ ErrorString("Some objects were marked as DontDestroyOnLoad, but since their parent game objects were not marked as DontDestroyOnLoad they have been destroyed anyway.\nIf you want to keep them around, please ensure their parent is marked as DontDestroyOnLoad or make sure they are unparented before loading a new level.");
+ #endif
+ }
+ }
+ }
+
+ // Send all active gameobjects a kLevelWasLoaded message
+ MessageData msgData;
+ msgData.SetData (index, ClassID (int));
+ SendMessageToEveryone (kLevelWasLoaded, msgData);
+
+ ResetRandomsAfterLevelLoad ();
+ GetTimeManager ().DidFinishLoadingLevel ();
+
+#if UNITY_WII
+ wii::EndLoadingScreen();
+#endif
+}
+
+int PlayerGetLoadedLevel ()
+{
+ return gLoadLevel.GetLoadedLevelIndex();
+}
+
+string PlayerGetLoadedLevelName ()
+{
+ int loadedLevel = PlayerGetLoadedLevel ();
+ // If loadedLevel is invalid, try to check the late bound level.
+ if (loadedLevel >= 0)
+ return GetBuildSettings ().GetLevelName(loadedLevel);
+ else if (gLoadLevel.m_hasLateBoundLevel)
+ return gLoadLevel.m_lateBoundLevelName;
+
+ return string ();
+}
+
+int PlayerGetLevelCount ()
+{
+ return GetBuildSettings ().levels.size ();
+}
+
+void PlayerSendFrameComplete()
+{
+ GetDelayedCallManager ().Update (DelayedCallManager::kEndOfFrame);
+}
+
+void PlayerTakeScreenShots()
+{
+// for now only on iphone we have explicit msaa resolve so we have a place where we can get proper screenshot
+// TODO: proper define?
+#if GAMERELEASE && CAPTURE_SCREENSHOT_AVAILABLE && !UNITY_IPHONE
+ UpdateCaptureScreenshot ();
+ #if SUPPORT_REPRODUCE_LOG
+ CaptureScreenshotReproduction(false);
+ #endif
+#endif
+}
+
+PROFILER_INFORMATION(gRemoteInputProfile, "ProcessRemoteInput", kProfilerOther);
+static void ProcessRemoteInput ()
+{
+ PROFILER_AUTO(gRemoteInputProfile, NULL);
+
+
+#if SUPPORT_IPHONE_REMOTE && UNITY_EDITOR
+ void RemoteInputUpdate();
+ RemoteInputUpdate();
+#endif
+
+}
+
+bool IsInsidePlayerLoop ()
+{
+ return s_InsidePlayerLoop;
+}
+
+PROFILER_INFORMATION(gGraphicsPresent, "Device.Present", kProfilerRender);
+PROFILER_INFORMATION(gFramerateSync, "WaitForTargetFPS", kProfilerVSync);
+PROFILER_INFORMATION(gSubstanceUpdate, "Substance.Update", kProfilerRender)
+PROFILER_INFORMATION(gPlayerLoopPresent, "Graphics.PresentAndSync", kProfilerRender)
+PROFILER_INFORMATION(gPlayerLoopBeginFrame, "Graphics.BeginFrame", kProfilerRender)
+PROFILER_INFORMATION(gUpdatePreloading, "Loading.UpdatePreloading", kProfilerPlayerLoop)
+PROFILER_INFORMATION(gUpdateWebStream, "Loading.UpdateWebStream", kProfilerPlayerLoop)
+PROFILER_INFORMATION(gPlayerCleanupCachedData, "Cleanup Unused Cached Data", kProfilerPlayerLoop)
+
+#if !UNITY_EDITOR
+
+void PresentFrame()
+{
+ PROFILER_AUTO(gGraphicsPresent, NULL);
+#if ((UNITY_LINUX && SUPPORT_X11) || UNITY_OSX) && !UNITY_EDITOR && !WEBPLUG
+ GetScreenManager().SetBlitMaterial();
+#endif
+ GfxDevice& device = GetGfxDevice();
+ device.PresentFrame();
+ GPU_TIMESTAMP();
+ gHasFrameToPresent = false;
+}
+
+void PresentAndSync()
+{
+#if GAMERELEASE
+ #if UNITY_WIN && !UNITY_WINRT
+ if (!IsWindowVisible(GetScreenManager().GetWindow()))
+ return;
+ #endif
+
+ PROFILER_AUTO (gPlayerLoopPresent, NULL)
+
+ if (g_PresentCallback) g_PresentCallback(true);
+
+ PresentFrame();
+
+ if (g_PresentCallback) g_PresentCallback(false);
+#endif
+ ;;gFrameStats.reset();
+}
+
+void PresentBeforeUpdate(GfxDevice::PresentMode presentMode)
+{
+ if (presentMode != GfxDevice::kPresentBeforeUpdate)
+ return;
+
+ // NOTE: updating TimeManager after VBlank potentially leads to stable frame delta times
+ // since there is no OS event pump between them in such case
+ if (gHasFrameToPresent)
+ {
+ #if UNITY_XENON
+ xenon::Kinect::Update(xenon::Kinect::kUT_BeforePresent);
+ #endif
+ PresentAndSync();
+ }
+}
+
+void PresentAfterDraw(GfxDevice::PresentMode presentMode)
+{
+ switch (presentMode)
+ {
+ case GfxDevice::kPresentAfterDraw:
+#if UNITY_XENON
+ xenon::Kinect::Update(xenon::Kinect::kUT_BeforePresent);
+#endif
+ PresentAndSync();
+ break;
+
+ case GfxDevice::kPresentBeforeUpdate:
+ // Present first frame immediately
+ if (gIsFirstFrame)
+ PresentAndSync();
+ break;
+ }
+ gIsFirstFrame = false;
+};
+
+static bool PlayerBeginFrame()
+{
+ GfxDevice& device = GetGfxDevice();
+ for (;;)
+ {
+ // We might already be inside a frame if something was rendered from script during update
+ if (!device.IsInsideFrame())
+ device.BeginFrame();
+
+ // Check if GfxDevice is ready to render
+ if (device.IsValidState())
+ return true;
+
+ // Handle Direct3D lost device
+ if (!device.HandleInvalidState())
+ {
+ LogString ("Skipped rendering frame because GfxDevice is in invalid state (device lost)");
+ return false;
+ }
+ // Loop back to BeginFrame() since we might be outside a frame if the device was lost
+ }
+}
+
+static void PlayerEndFrame(bool present)
+{
+ gHasFrameToPresent = true;
+ if (present)
+ {
+ GetGfxDevice().EndFrame();
+ PresentFrame();
+ }
+}
+
+void PlayerRender(bool present)
+{
+ if (!PlayerBeginFrame())
+ return;
+
+ GetRenderManager().RenderOffscreenCameras();
+ GetRenderManager().RenderCameras();
+
+#if ENABLE_UNITYGUI
+ GetGUIManager().Repaint ();
+#endif
+#if UNITY_WII
+ wii::DrawMovieFrame();
+#endif
+
+ DrawSplashAndWatermarks();
+
+#if RENDER_SOFTWARE_CURSOR
+ Cursors::RenderSoftwareCursor();
+#endif
+
+#if WEBPLUG
+ RenderFullscreenEscapeWarning();
+#endif
+
+ PlayerEndFrame(present);
+}
+
+#endif // !UNITY_EDITOR
+
+
+int GetWantedVSyncCount()
+{
+ // What the user has requested, no clamping to actual caps
+ QualitySettings* qualitySettings = GetQualitySettingsPtr();
+ if (qualitySettings)
+ return qualitySettings->GetCurrent().vSyncCount;
+ return 0;
+}
+
+int GetMaxSupportedVSyncCount()
+{
+ int vSync = gGraphicsCaps.maxVSyncInterval;
+#if UNITY_WIN
+ if (!GetScreenManager().IsFullScreen() &&
+ GetGfxDevice().GetRenderer() == kGfxRendererD3D9)
+ {
+ // D3D9 does not support intervals >1 in windowed mode (case 497116)
+ vSync = std::min(vSync, 1);
+ }
+#endif
+ return vSync;
+}
+
+void PlayerUpdateTime()
+{
+ // Xbox is bound to VBlank and does its own delicate Kinect synchronization.
+ // iOS uses its own VBlank sync
+#if !UNITY_XENON && !UNITY_FLASH && !UNITY_IPHONE && !UNITY_PS3 && !UNITY_WEBGL
+ {
+ int vSyncWanted = GetWantedVSyncCount();
+ int vSyncSupported = GetMaxSupportedVSyncCount();
+ // If interval is non-zero and supported by GfxDevice, don't sync on CPU
+ if (UNITY_EDITOR || vSyncWanted == 0 || vSyncWanted > vSyncSupported)
+ {
+ PROFILER_AUTO(gFramerateSync, NULL);
+ float actualTargetFPS = GetActualTargetFrameRate(vSyncWanted);
+ GetTimeManager().Sync(actualTargetFPS);
+ }
+ }
+#endif
+
+ // Update time, input
+ GetTimeManager().Update();
+}
+
+PROFILER_INFORMATION(gOnMouseHandlers, "Monobehaviour.OnMouse_", kProfilerScripts);
+
+bool PlayerLoop (bool batchMode, bool performRendering, IHookEvent* pHookEvt)
+{
+#if !UNITY_FLASH
+ ReentrancyChecker checker( &s_InsidePlayerLoop );
+ if (!checker.IsOK())
+ {
+ ErrorString ("PlayerLoop called recursively!");
+ return true;
+ }
+#endif
+
+ #if SUPPORT_LOG_ORDER_TRACE
+ if (RunningReproduction())
+ {
+ LogString(Format("Frame %d", GetTimeManager().GetFrameCount()));
+ }
+ #endif
+
+#if ENABLE_CLUSTER_SYNC
+ if(GetIClusterRenderer())
+ GetIClusterRenderer()->SynchronizeCluster();
+#endif
+
+ #if ENABLE_PLAYERCONNECTION && !UNITY_EDITOR
+ PlayerConnection::Get().Poll();
+ #endif
+
+ #if ENABLE_PROFILER && GAMERELEASE
+
+ UnityProfiler::RecordPreviousFrame(kProfilerGame);
+ bool profilerEnabled = UnityProfiler::StartNewFrame(kProfilerGame);
+
+ if (profilerEnabled)
+ {
+ GfxDevice& device = GetGfxDevice();
+ device.BeginFrameStats();
+ }
+
+ #endif // ENABLE_PROFILER && GAMERELEASE
+
+ GPU_TIMESTAMP(); // Initial GPU time
+
+ #if ENABLE_SUBSTANCE
+ {
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ {
+ PROFILER_AUTO(gSubstanceUpdate, NULL)
+ system->Update();
+ }
+ }
+ #endif
+
+ ;;FrameStats::Timestamp ts = 0;
+
+ #if SUPPORT_REPRODUCE_LOG
+ static bool didSetWebplayerSize = false;
+ if (!didSetWebplayerSize)
+ {
+ didSetWebplayerSize = true;
+ WriteWebplayerSize(GetScreenManager().GetWidth(), GetScreenManager().GetHeight());
+ }
+ #endif // SUPPORT_REPRODUCE_LOG
+
+ if (!batchMode)
+ ProcessMouseInWindow();
+
+ // In the editor, clear intermediate renderers before loop. So that in paused state or when resizing windows,
+ // we can still draw the previous ones.
+ #if UNITY_EDITOR
+ GetScene().ClearIntermediateRenderers();
+ #endif
+
+ ClearLines();
+
+ GfxDevice& device = GetGfxDevice();
+ GfxDevice::PresentMode presentMode = device.GetPresentMode();
+ bool playmode = IsWorldPlaying();
+
+ #if !UNITY_EDITOR
+ if (!batchMode)
+ PresentBeforeUpdate(presentMode);
+ #endif
+
+ // Reset frame stats after present (case 496221)
+ if (presentMode == GfxDevice::kPresentBeforeUpdate)
+ device.ResetFrameStats();
+
+ #if SUPPORT_REPRODUCE_LOG
+ RepeatReproductionScreenshot();
+ ReadWriteReproductionTime();
+ #endif
+#if ENABLE_CLUSTER_SYNC
+ if(!GetIClusterRenderer() || GetIClusterRenderer()->IsMasterOfCluster())
+#endif
+ {
+ PlayerUpdateTime();
+ }
+
+ #if ENABLE_WWW
+ {
+ PROFILER_AUTO(gUpdateWebStream, NULL);
+ UnityWebStream::UpdateAllUnityWebStreams();
+ }
+ #endif
+
+
+ ////@TODO: CLeanup code where input is processed after level loading
+ /// All input should be processed prior to level loading
+
+ // In reproduce mode make sure to handle input postprocessor prior to loading a level.
+ // This fixes potential issues where scripts use input in awake from load
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ {
+ if (playmode)
+ GetInputManager ().ProcessInput ();
+ ReadWriteReproductionInput();
+ }
+ #endif
+
+ #if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+ #endif
+
+ {
+ PROFILER_AUTO(gUpdatePreloading, NULL)
+ GetPreloadManager().UpdatePreloading ();
+ }
+
+ if (performRendering)
+ GetScene().NotifyInvisible();
+
+ {
+ PROFILER_AUTO(gPlayerCleanupCachedData, NULL)
+ GetRenderBufferManager().GarbageCollect();
+ TextMeshGenerator2::GarbageCollect();
+ }
+
+ ////@TODO: CLeanup code where input is processed after level loading
+ /// All input should be processed prior to level loading
+ #if SUPPORT_REPRODUCE_LOG
+ if (!RunningReproduction())
+ #endif
+ {
+ if (playmode)
+ GetInputManager ().ProcessInput ();
+ }
+
+ ProcessRemoteInput ();
+
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunStartupFrame);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+
+ /* This is what fixed time stepping is doing
+ float time = GetCurrentTime ();
+ while (fixedTime < time)
+ {
+ fixedTime += fixedDeltaTime;
+ UpdateFixedBehaviours ();
+ UdateDynamicsManager ();
+ }
+ Which means:
+ - fixed timestep is always larger than dynamic timestep
+ - fixed delta time is always the same
+ */
+
+ int stepCounter = 0;
+
+ // Make sure collider positions update when time scale is zero, so raycasts will still work as expected.
+ if (CompareApproximately (GetTimeManager().GetTimeScale(), 0.0f))
+ {
+ CALL_UPDATE_MODULAR(PhysicsRefreshWhenPaused)
+ }
+
+ #if UNITY_XENON
+ xenon::Kinect::Update(xenon::Kinect::kUT_BeforeUserScripts);
+ #endif
+
+#if UNITY_IPHONE
+ iphone::DeliverPlatformEvents();
+#endif
+
+ // Fixed framerate loop (fixed behaviours, dynamics, delayed calling)
+ while (GetTimeManager ().StepFixedTime ())
+ {
+ // Placed here so we ensure it is also called in edit-mode (fix for case 379024: pressing stop did not properly clear fixedStepLines)
+ ClearLines ();
+
+ if (playmode)
+ {
+ if (stepCounter == 0)
+ {
+ CALL_UPDATE_MODULAR(PhysicsResetInterpolatedTransformPosition)
+ CALL_UPDATE_MODULAR(Physics2DResetInterpolatedTransformPosition)
+ }
+
+ #if ENABLE_AUDIO
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->FixedUpdate ();
+ #endif
+
+ // Script.FixedUpdate
+ ;;ts = GetTimestamp();
+ GetFixedBehaviourManager ().Update ();
+ ;;gFrameStats.fixedBehaviourManagerDt += (GetTimestamp() - ts);
+
+ // Animation (Root motion)
+ ;;ts = GetTimestamp();
+ CALL_UPDATE_MODULAR(AnimatorFixedUpdateFKMove)
+ CALL_UPDATE_MODULAR(LegacyAnimationUpdate)
+ ;;gFrameStats.animationUpdateDt += (GetTimestamp() - ts);
+
+ // Physics
+ ;;ts = GetTimestamp();
+ CALL_UPDATE_MODULAR(PhysicsFixedUpdate)
+ CALL_UPDATE_MODULAR(Physics2DFixedUpdate)
+ ;;gFrameStats.fixedPhysicsManagerDt += GetTimestamp() - ts;
+
+ // Animation IK and write bones
+ CALL_UPDATE_MODULAR(AnimatorFixedUpdateRetargetIKWrite)
+
+ // Script Coroutines
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunFixedFrameRate);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+ }
+ stepCounter++;
+ }
+ gFrameStats.fixedUpdateCount = stepCounter;
+
+ // Dynamics, animation, behaviours
+ if (playmode)
+ {
+ CALL_UPDATE_MODULAR(PhysicsUpdate)
+ CALL_UPDATE_MODULAR(Physics2DUpdate)
+ }
+ bool oldTextFocus = GetInputManager().GetTextFieldInput();
+
+#if ENABLE_UNITYGUI
+ GetGUIManager().SendQueuedEvents ();
+#endif
+
+#if ENABLE_NEW_EVENT_SYSTEM
+#if ENABLE_UNITYGUI
+ if (playmode && !GetGUIManager().GetMouseUsed())
+#else
+ if (playmode)
+#endif
+ GetInputManager().SendInputEvents();
+#endif
+
+#if ENABLE_RETAINEDGUI
+ GetGUITracker().SendInputEvents(GetGUIManager().GetQueuedEvents());
+#endif
+
+#if ENABLE_SCRIPTING && SUPPORT_MOUSE
+#if ENABLE_NEW_EVENT_SYSTEM
+ // Deprecated in Unity 4.1. Mouse and touch events will now be sent via C++. Look inside InputManager.cpp.
+ if (playmode && !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+#else
+ if (playmode)
+#endif
+ {
+ if (GetBuildSettings().usesOnMouseEvents)
+ {
+ PROFILER_AUTO(gOnMouseHandlers, NULL)
+ int mouseUsed = GetGUIManager().GetMouseUsed () ? 1 : 0;
+ int skipRTCameras = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1);
+
+ ScriptingInvocation invocation(MONO_COMMON.doSendMouseEvents);
+ invocation.AddInt(mouseUsed);
+ invocation.AddInt(skipRTCameras);
+ invocation.Invoke();
+ }
+ }
+#endif
+
+ CALL_UPDATE_MODULAR(NavMeshUpdate)
+
+ ;;ts = GetTimestamp();
+ GetBehaviourManager ().Update ();
+ ;;gFrameStats.dynamicBehaviourManagerDt += GetTimestamp() - ts;
+
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunDynamicFrameRate);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+
+ // Dynamic Step Animation Update
+ if (playmode)
+ {
+ ;;ts = GetTimestamp();
+
+ CALL_UPDATE_MODULAR(AnimatorUpdateFKMove);
+ CALL_UPDATE_MODULAR(LegacyAnimationUpdate);
+ CALL_UPDATE_MODULAR(AnimatorUpdateRetargetIKWrite);
+
+ ;;gFrameStats.animationUpdateDt += (GetTimestamp() - ts);
+ }
+
+ #if ENABLE_NETWORK
+ if (playmode)
+ {
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kNetworkManager, NetworkUpdate());
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kMasterServerInterface, NetworkUpdate());
+ }
+ #endif
+
+ ParticleSystem::BeginUpdateAll ();
+
+ ;;ts = GetTimestamp();
+ GetLateBehaviourManager ().Update ();
+ ;;gFrameStats.dynamicBehaviourManagerDt += GetTimestamp() - ts;
+
+ ;;ts = GetTimestamp();
+ GetDelayedCallManager ().Update (DelayedCallManager::kRunDynamicFrameRate);
+ ;;gFrameStats.coroutineDt += GetTimestamp() - ts;
+
+ #if ENABLE_AUDIO
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->Update();
+ #endif
+
+ ParticleEmitter::UpdateAllParticleSystems();
+
+ #if ENABLE_PHYSICS
+ if (playmode && performRendering && device.IsValidState())
+ {
+ CALL_UPDATE_MODULAR(PhysicsSkinnedClothUpdate);
+ }
+ #endif
+
+ // We need to sync particle systems here to make sure they update their renderers properly
+ ParticleSystem::EndUpdateAll ();
+
+ RenderManager::UpdateAllRenderers();
+
+ if(pHookEvt)
+ pHookEvt->Execute();
+
+ ;;ts = GetTimestamp();
+ if (performRendering && device.IsValidState()) // skinning touches graphics resources; only do that when device is not lost
+ {
+ SkinnedMeshRenderer::UpdateAllSkinnedMeshes(SkinnedMeshRenderer::kUpdateNonCloth);
+ }
+ ;;gFrameStats.skinMeshUpdateDt += GetTimestamp() - ts;
+
+ #if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN
+ WebScripting::Get().ProcessSendMessages();
+ #endif
+ GetUpdateManager ().Update ();
+
+ if(pHookEvt)
+ pHookEvt->Execute();
+
+ #if ENABLE_MOVIES || ENABLE_WEBCAM
+ if (performRendering && device.IsValidState()) // movies touch graphics resources; only do that when device is not lost
+ {
+ IAudio* audioModule = GetIAudio();
+ if (audioModule)
+ audioModule->UpdateVideoTextures ();
+ }
+ #endif
+
+
+#if UNITY_WII && !WIIWARE
+ void ProcessDVDErrors();
+ ProcessDVDErrors();
+#endif
+
+ // In d3d ref mode only render when taking screenshots
+ #if SUPPORT_REPRODUCE_LOG && GFX_SUPPORTS_D3D9 && ENABLE_FORCE_GFX_RENDERER
+ if (::g_ForceD3D9RefDevice && RunningReproduction() && !GetInputManager().GetKey(SDLK_F5))
+ performRendering = false;
+ #endif
+
+ #if !UNITY_EDITOR
+ if (performRendering)
+ {
+ ;;ts = GetTimestamp();
+
+ // Perform rendering
+ if (!batchMode)
+ PlayerRender(false);
+
+ // We want to send 'end-of-frame' events even if running in batch mode
+ PlayerSendFrameComplete();
+
+ PlayerTakeScreenShots();
+
+ if (!batchMode)
+ {
+ device.EndFrame();
+ PresentAfterDraw(presentMode);
+ }
+
+ ;;gFrameStats.renderDt += GetTimestamp() - ts;
+ } // End rendering
+
+ // In the player, clear intermediate renderers after loop.
+ GetScene().ClearIntermediateRenderers();
+
+ #endif // !UNITY_EDITOR
+
+ #if SUPPORT_REPRODUCE_LOG
+ ReadWriteReproductionEnd();
+ #endif
+
+ GetScreenManager().SetRequestedResolution();
+
+ // Clear the input string and the keydown events at the end of the Loop.
+ // This makes sure all input string is cleared
+ GetInputManager ().InputEndFrame ();
+
+ // Let Fonts uncache characters if they are not used in the next frame.
+ Font::FrameComplete();
+
+ // We entered Text Field input this frame, Game mode input is disable.
+ // Reset axes, so they don't stick.
+ if(GetInputManager().GetTextFieldInput() && !oldTextFocus)
+ GetInputManager ().ResetInputAxes ();
+
+ #if WEBPLUG
+ if (!LoadQueuedWebPlayerData())
+ return false;
+ #endif
+
+ #if THREADED_LOADING_DEBUG
+ static double endFrameTime = 0.0F;
+ float delta = GetTimeSinceStartup() - endFrameTime;
+ endFrameTime = GetTimeSinceStartup();
+ printf_console("FPS %f\n", 1.0F / delta);
+ #endif
+
+ #if ENABLE_PROFILER && GAMERELEASE
+ if (profilerEnabled)
+ {
+ device.EndFrameStats();
+ device.SynchronizeStats();
+ }
+ #endif
+
+ #if ENABLE_MEMORY_MANAGER
+ GetMemoryManager().FrameMaintenance();
+ #endif
+
+ #if ENABLE_GAMECENTER
+ GameCenter::ExecuteGameCenterCallbacks();
+ #endif
+
+ // Reset frame stats after present and UnityProfiler EndFrame() (case 494732)
+ if (presentMode == GfxDevice::kPresentAfterDraw)
+ device.ResetFrameStats();
+
+ return true;
+}
+
+void LevelLoadingLoop()
+{
+#if UNITY_ANDROID
+ // make sure input is being processed to keep those ANR dialogs at bay..
+ InputProcess();
+#endif
+#if UNITY_METRO
+ void UpdateMetroEvents();
+ UpdateMetroEvents();
+#endif
+ Thread::Sleep(0.020f);
+}
+
+
+#if !UNITY_EDITOR && !WEBPLUG
+bool SwitchToStandaloneDefaultSettings()
+{
+ // Reload quality settings (Preferences might have changed)
+ GetQualitySettings().AwakeFromLoad(kDefaultAwakeFromLoad);
+
+#if UNITY_IPHONE
+ // on ios we do not use player prefs for resolution
+ bool success = true;
+#else
+ const PlayerSettings& settings = GetPlayerSettings();
+ int width = PlayerPrefs::GetInt(kResolutionWidth, settings.defaultScreenWidth);
+ int height = PlayerPrefs::GetInt(kResolutionHeight, settings.defaultScreenHeight);
+ bool fullscreen = PlayerPrefs::GetInt(kIsFullScreen, settings.defaultIsFullScreen);
+
+ if (fullscreen &&
+ !(PlayerPrefs::HasKey (kResolutionWidth) && PlayerPrefs::HasKey (kResolutionHeight)) &&
+ settings.defaultIsNativeResolution) {
+ width = GetScreenManager ().GetCurrentResolution ().width;
+ height = GetScreenManager ().GetCurrentResolution ().height;
+ }
+
+ bool success = GetScreenManager ().SetResolutionImmediate (width, height, fullscreen, 0);
+#endif
+ GetScreenManager().SetShowCursor (true);
+ return success;
+}
+#endif
+
+#if UNITY_OSX && !UNITY_EDITOR && !UNITY_PEPPER
+bool SwitchToBatchmode()
+{
+ // Reload quality settings (Preferences might have changed)
+ GetQualitySettings().AwakeFromLoad(kDefaultAwakeFromLoad);
+
+ bool success = GetScreenManager().SwitchToBatchmode();
+ return success;
+}
+#endif
+
+int GetTargetFrameRateFromScripting()
+{
+ return gTargetFrameRate;
+}
+
+int GetTargetFrameRate()
+{
+#if SUPPORT_REPRODUCE_LOG
+ if (HasNormalPlaybackSpeed() || GetReproduceMode() == kGenerateReproduceLog || GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ return 20;
+ else if (GetReproduceMode() == kPlaybackReproduceLog)
+ return 1000;
+#endif
+
+ return gTargetFrameRate;
+}
+
+#if UNITY_IPHONE
+ extern "C" void NotifyFramerateChange(int);
+#endif
+
+void SetTargetFrameRate(int target)
+{
+ gTargetFrameRate = target;
+#if UNITY_FLASH
+ __asm __volatile__("stage.frameRate = %0" : : "r"(target));
+#endif
+
+#if UNITY_IPHONE
+ NotifyFramerateChange(gTargetFrameRate);
+#endif
+
+#if UNITY_WP8
+ extern BridgeInterface::IBridge^ s_WinRTBridge;
+ Assert(s_WinRTBridge);
+ s_WinRTBridge->WP8Utility->MaxFrameRate = gTargetFrameRate;
+#endif
+}
+
+float GetActualTargetFrameRate()
+{
+ return GetActualTargetFrameRate(GetWantedVSyncCount());
+}
+
+float GetActualTargetFrameRate(int vSyncCount)
+{
+#if UNITY_FLASH
+ int fr;
+ __asm __volatile__("%0 = stage.frameRate;" : "=r"(fr));
+ return fr;
+#endif
+
+#if UNITY_WIN && WEBPLUG
+ const int kFullscreenTargetFPS = 200;
+#else
+ const int kFullscreenTargetFPS = 1000;
+#endif
+
+ float framerate = GetTargetFrameRate();
+
+ // Can be a fraction of an odd refresh rate like 59 so we can't use int
+ if (vSyncCount > 0)
+ {
+ int refreshRate = GetScreenManager().GetCurrentResolution().refreshRate;
+ framerate = float(refreshRate > 0 ? refreshRate : 60) / vSyncCount;
+ }
+
+ if( framerate <= 0 )
+ {
+#if WEBPLUG && !UNITY_PEPPER
+ return (GetScreenManagerPtr() && GetScreenManager().IsFullScreen()) ? kFullscreenTargetFPS : 60;
+#else
+ return kFullscreenTargetFPS;
+#endif
+ }
+ else
+ {
+ return std::min<float>(framerate,kFullscreenTargetFPS);
+ }
+}
diff --git a/Runtime/Misc/Player.h b/Runtime/Misc/Player.h
new file mode 100644
index 0000000..2bd6e62
--- /dev/null
+++ b/Runtime/Misc/Player.h
@@ -0,0 +1,153 @@
+#ifndef _PLAYER_H
+#define _PLAYER_H
+
+#include <string>
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Misc/PlayerSettings.h"
+
+// To avoid frozen window because of GPU workload
+class IHookEvent
+{
+public:
+ virtual void Execute() = 0;
+};
+
+class Object;
+class DecompressedDataFile;
+class UnityWebStream;
+class AwakeFromLoadQueue;
+
+// ** Initializing unity
+// 1) InitializeMonoFromMain ("Mono dll path");
+// 2) Optional: SelectDataFolder ();
+// 2) PlayerInitEngine ("Data folder", "app path/Contents");
+// 3) Optional: Display screen selector.
+// The engine is initialized so you can access the input manager at this point.
+// 4) PlayerInitEngineGraphics ();
+// 5) if (!SwitchToStandaloneDefaultSettings())
+// couldnt switch resolution
+// 6) PlayerLoadFirstLevel ();
+bool PlayerInitEngineNoGraphics (const std::string& dataFolder, const std::string& applicationContentsFolderPath);
+bool PlayerInitEngineGraphics (bool batchmode = false);
+std::string SelectDataFolder ();
+void PlayerLoadFirstLevel ();
+void PlayerInitState(void);
+
+/// For reloading a unity player from the web player
+bool PlayerInitEngineWeb (const std::string& applicationContentsFolderPath, int pluginversion);
+bool PlayerInitEngineWebNoGraphics (const string& applicationContentsFolderPath, int pluginversion);
+bool PlayerInitEngineWebGraphics ();
+
+bool PlayerLoadWebData (UnityWebStream& data, const std::string srcValue, const std::string absoluteURL, bool resetPrefs = true);
+bool PlayerResetPreferences(UnityWebStream& stream, const std::string absoluteURL, bool clearPlayerPrefs = true);
+bool PlayerLoadEngineData (UnityWebStream& data, const std::string srcValue, const std::string absoluteURL);
+bool PlayerWebUnloadReloadable ();
+
+/// Force loading a new unityweb data file. Used for streaming unityweb files with the WWW class.
+bool QueuePlayerLoadWebData (UnityWebStream* stream);
+
+/// Render scene without doing anything else
+void PlayerRender(bool present);
+
+/// Running unity every frame
+/// 1) Read input in platform specific way.
+/// GetInput.h on os x. This sets appropriate variables inside InputManager.
+/// 2) PlayerLoop ();
+/// Don't call this is if the player is paused (kPlayerPaused == GetPlayerPause)
+bool PlayerLoop (bool batchmode = false, bool performRendering = true, IHookEvent* pHookEvt=NULL);
+
+/// Cleanup the player (Calls NotifyPlayerQuit)
+/// 1) optional PlayerPrefs::Sync ();
+/// 2) PlayerCleanup ();
+/// 3) ExitToShell();
+/// Returns true if cleanup succeeded.
+/// If forceQuit is true it will always cleanup. (Force quit should be enabled in the web player)
+/// If cleanupEverything is true, it will also delete all objects. This should be enabled
+bool PlayerCleanup(bool cleanupEverything, bool forceQuit);
+
+/// Initialize and destroy the runtime. This has to be called as the first thing on player/engine startup
+/// and as the very last thing before exiting.
+/// All globals and statics should be initialized and cleaned up from these functions
+void RuntimeInitialize();
+void RuntimeCleanup();
+
+struct AutoInitializeAndCleanupRuntime
+{
+ AutoInitializeAndCleanupRuntime(){RuntimeInitialize();}
+ ~AutoInitializeAndCleanupRuntime(){RuntimeCleanup();}
+};
+
+int GetPluginVersion();
+
+/// Sends a script message that the player is about to quit
+bool NotifyPlayerQuit(bool forceQuit);
+
+void ProcessMouseInWindow();
+
+enum PlayerPause
+{
+ kPlayerRunning,
+ kPlayerPausing,
+ kPlayerPaused,
+};
+
+// To pause unity: Call PlayerPause. It notifies all appropriate managers to pause or unpause
+// Also you shouldn't call PlayerLoop until the player is not paused anymoyre
+void SetPlayerPause (PlayerPause pause);
+// Is the player currently paused?
+PlayerPause GetPlayerPause ();
+
+// Notify scripts about lost or received focus
+void SetPlayerFocus(bool focus);
+
+// Sends "frame complete" to scripts
+void PlayerSendFrameComplete();
+
+// Reloads quality settings from preferences
+// Resets resolution
+#if !UNITY_EDITOR && !WEBPLUG
+bool SwitchToStandaloneDefaultSettings();
+#endif
+
+#if UNITY_OSX
+bool SwitchToBatchmode();
+#endif
+
+/// Should the player run in the background?
+bool GetPlayerRunInBackground();
+void SetPlayerRunInBackground(bool runInBackground);
+
+
+// For editor. Are we currently inside of the PlayerLoop?
+// Can be used to detect if we are currently executing game code or editor code in the editor..
+bool IsInsidePlayerLoop ();
+
+/// Makes an object survive when loading a new scene.
+void DontDestroyOnLoad (Object& object);
+
+int PlayerGetLoadedLevel ();
+std::string PlayerGetLoadedLevelName ();
+int PlayerGetLevelCount ();
+bool GetLevelAndAssetPath (const std::string& levelName, int levelIndex, std::string* levelPath, std::string* assetPath, int* index);
+bool GetHasLateBoundLevelFromAssetBundle (const std::string& name);
+
+void ResetPlayerInEditor (int level);
+bool IfWillLoadLevel ();
+bool IsLoadingLevel ();
+
+/// Called from PreloadManager::WaitForAllAsyncOperationsToComplete
+/// while the loading queue is being processed, and the main thread is stalled.
+void LevelLoadingLoop();
+
+float GetActualTargetFrameRate();
+float GetActualTargetFrameRate(int vSyncCount);
+int GetTargetFrameRate();
+int GetTargetFrameRateFromScripting();
+void SetTargetFrameRate(int target);
+
+//// HACK
+void PlayerLoadLevelFromThread (int levelIndex, const std::string& name, AwakeFromLoadQueue& awakeFromLoadQueue);
+
+#endif
diff --git a/Runtime/Misc/PlayerSettings.cpp b/Runtime/Misc/PlayerSettings.cpp
new file mode 100644
index 0000000..0c7b422
--- /dev/null
+++ b/Runtime/Misc/PlayerSettings.cpp
@@ -0,0 +1,436 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "PlayerSettings.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/Graphics/Texture2D.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Utilities/Utility.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/BaseClasses/Cursor.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/ColorSpaceLiveSwitch.h"
+#endif
+
+
+// ----------------------------------------------------------------------
+
+
+// ----------------------------------------------------------------------
+
+static const char* kAspectRatioSerializeNames[kAspectCount] = {
+ "Others",
+ "4:3",
+ "5:4",
+ "16:10",
+ "16:9",
+};
+
+static float kAspectRatioValues[kAspectCount] = {
+ 0.0f,
+ 4.0f/3.0f,
+ 5.0f/4.0f,
+ 16.0f/10.0f,
+ 16.0f/9.0f,
+};
+
+static bool DoesMatchAspectRatio (int width, int height, ResolutionAspect aspect)
+{
+ AssertIf (aspect == kAspectOthers);
+ if (width == 0 || height == 0)
+ return false;
+ float val = float(width) / float(height);
+ return (Abs (val - kAspectRatioValues[aspect]) < 0.05f);
+}
+
+
+template<class TransferFunc>
+void AspectRatios::Transfer (TransferFunc& transfer)
+{
+ // Others is at index 0 (so we can easily add more ratios later),
+ // but we want to display it last in the UI. So serialize all first, then
+ // "Others" one
+ for (int i = 1; i < kAspectCount; ++i)
+ {
+ transfer.Transfer (m_Ratios[i], kAspectRatioSerializeNames[i]);
+ }
+ transfer.Transfer (m_Ratios[0], kAspectRatioSerializeNames[0]);
+ transfer.Align ();
+}
+
+
+bool PlayerSettings::DoesSupportResolution (int width, int height) const
+{
+ // check if matches any enabled ratio except "Others"
+ bool didSupportAnyRatio = false;
+ for (int i = 1; i < kAspectCount; ++i)
+ {
+ if (DoesMatchAspectRatio (width, height, static_cast<ResolutionAspect>(i)))
+ {
+ didSupportAnyRatio = true;
+ if (m_SupportedAspectRatios.m_Ratios[i])
+ return true;
+ }
+ }
+ // if "Others" is enabled and we did not support any of standard aspects -
+ // then we support this weird aspect
+ if (!didSupportAnyRatio && m_SupportedAspectRatios.m_Ratios[kAspectOthers])
+ return true;
+
+ return false;
+}
+
+
+// ----------------------------------------------------------------------
+
+PlayerSettings::PlayerSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_RenderingPath(kRenderPathForward)
+, m_MobileRenderingPath(kRenderPathForward)
+, m_ActiveColorSpace(kGammaColorSpace)
+, m_MTRendering(true)
+, m_MobileMTRendering(false)
+, m_UseDX11(false)
+{
+ #if UNITY_EDITOR
+ companyName = GetDefaultCompanyName ();
+ productName = GetDefaultProductName ();
+ unityRebuildLibraryVersion = 0;
+ unityForwardCompatibleVersion = UNITY_FORWARD_COMPATIBLE_VERSION;
+ unityStandardAssetsVersion = 0;
+ m_EditorOnlyNotPersistent.CurrentColorSpace = (ColorSpace) m_ActiveColorSpace;
+ #endif
+
+ iPhoneBundleIdentifier = "com.Company.ProductName";
+
+ defaultIsFullScreen = true;
+ defaultIsNativeResolution = true;
+ displayResolutionDialog = kShowResolutionDialog;
+ defaultScreenWidth = 1024;
+ defaultScreenHeight = 768;
+ defaultWebScreenWidth = 960;
+ defaultWebScreenHeight = 600;
+ firstStreamedLevelWithResources = 0;
+ androidProfiler = false;
+
+ defaultScreenOrientation = 0;
+ uiAutoRotateToPortrait = uiAutoRotateToPortraitUpsideDown = uiAutoRotateToLandscapeLeft = uiAutoRotateToLandscapeRight = true;
+ uiUseAnimatedAutoRotation = true;
+ uiUse32BitDisplayBuffer = true;
+ uiUse24BitDepthBuffer = true;
+ iosShowActivityIndicatorOnLoading = -1;
+ androidShowActivityIndicatorOnLoading = -1;
+
+ runInBackground = false;
+ captureSingleScreen = false;
+ targetDevice = 2;
+ targetGlesGraphics = 1;
+ targetResolution = 0;
+ accelerometerFrequency = 60;
+ overrideIPodMusic = false;
+ prepareIOSForRecording = false;
+ enableHWStatistics = true;
+ usePlayerLog = true;
+ stripPhysics = false;
+ forceSingleInstance = false;
+ resizableWindow = false;
+ useMacAppStoreValidation = false;
+ macFullscreenMode = kFullscreenWindowWithMenuBarAndDock;
+ gpuSkinning = false;
+ xboxPIXTextureCapture = false;
+ xboxEnableAvatar = false;
+ xboxEnableKinect = false;
+ xboxEnableKinectAutoTracking = false;
+ xboxSpeechDB = 0;
+ xboxEnableFitness = false;
+ xboxEnableHeadOrientation = false;
+ xboxEnableGuest = false;
+
+ wiiHio2Usage = -1;
+ wiiLoadingScreenRectPlacement = 0;
+ wiiLoadingScreenBackground = ColorRGBAf(1.0f, 1.0f, 1.0f, 1.0f);
+ wiiLoadingScreenPeriod = 1000;
+ wiiLoadingScreenFileName = "";
+ wiiLoadingScreenRect = Rectf(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
+void PlayerSettings::InitializeClass()
+{
+ // 2.x -> 3.0 compatibility
+ RegisterAllowNameConversion("PlayerSettings", "defaultWebScreenWidth", "defaultScreenWidthWeb");
+ RegisterAllowNameConversion("PlayerSettings", "defaultWebScreenHeight", "defaultScreenHeightWeb");
+}
+
+void PlayerSettings::PostInitializeClass ()
+{
+ // This will help reset the cursors during a dynamic reload.
+ // For most platforms, this will happen only during initialization,
+ // the manager pointer will be null and this will no-op.
+ if (GetPlayerSettingsPtr ())
+ GetPlayerSettings ().InitDefaultCursors();
+}
+
+
+PlayerSettings::~PlayerSettings ()
+{
+}
+
+void PlayerSettings::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_RenderingPath = clamp (m_RenderingPath, 0, kRenderPathCount-1);
+ m_ActiveColorSpace = clamp (m_ActiveColorSpace, 0, kMaxColorSpace-1);
+}
+
+
+bool PlayerSettings::GetAutoRotationAllowed(int orientation) const
+{
+ switch(orientation)
+ {
+ case 0: return uiAutoRotateToPortrait;
+ case 1: return uiAutoRotateToPortraitUpsideDown;
+ case 2: return uiAutoRotateToLandscapeRight;
+ case 3: return uiAutoRotateToLandscapeLeft;
+
+ default:
+ ErrorString("orientation out of range");
+ return false;
+ }
+}
+
+void PlayerSettings::SetAutoRotationAllowed(int orientation, bool enabled)
+{
+ switch(orientation)
+ {
+ case 0: uiAutoRotateToPortrait = enabled; break;
+ case 1: uiAutoRotateToPortraitUpsideDown = enabled; break;
+ case 2: uiAutoRotateToLandscapeRight = enabled; break;
+ case 3: uiAutoRotateToLandscapeLeft = enabled; break;
+
+ default:
+ ErrorString("orientation out of range, ignoring");
+ break;
+ }
+
+ bool somethingAllowed = uiAutoRotateToPortrait
+ || uiAutoRotateToPortraitUpsideDown
+ || uiAutoRotateToLandscapeRight
+ || uiAutoRotateToLandscapeLeft;
+
+ if( defaultScreenOrientation == 4 && !somethingAllowed )
+ {
+ ErrorString("all orientations are disabled for auto-rotation. Enabling Portrait");
+ uiAutoRotateToPortrait = true;
+ }
+}
+
+void PlayerSettings::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ #if UNITY_EDITOR
+ // product name and identifier can't be empty strings
+ if (companyName.empty ())
+ companyName = GetDefaultCompanyName ();
+ if (productName.empty () || productName == "UnityPlayer")
+ productName = GetDefaultProductName ();
+
+ // Perform color space conversion / cursor init only when applying a setting in the inspector.
+ if (awakeMode == kDefaultAwakeFromLoad && GetPlayerSettingsPtr() == this)
+ {
+ //only perform color space conversion if it was actually changed.
+ if ( GetEditorOnlyNotPersistent().CurrentColorSpace != GetValidatedColorSpace() ) {
+ ColorSpaceLiveSwitch();
+ GetEditorOnlyNotPersistent().CurrentColorSpace = GetValidatedColorSpace();
+ }
+ InitDefaultCursors ();
+ }
+ #else
+ // Try to set the default cursor. We can only do so if we actually have a GFX device
+ // which is not the case when we are currently still starting up (the player settings
+ // are read before the graphics system is initialized).
+ if((awakeMode == kDidLoadThreaded || awakeMode == kDidLoadFromDisk) && IsGfxDevice ())
+ {
+ InitDefaultCursors();
+ }
+ #endif
+}
+
+void PlayerSettings::InitDefaultCursors ()
+{
+ Assert (IsGfxDevice ());
+ Cursors::InitializeCursors (defaultCursor, cursorHotspot);
+}
+
+template<class TransferFunction>
+void PlayerSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion (2);
+
+ transfer.Transfer (androidProfiler, "AndroidProfiler");
+ transfer.Align();
+
+ transfer.Transfer (defaultScreenOrientation, "defaultScreenOrientation");
+
+ transfer.Transfer (targetDevice, "targetDevice");
+ transfer.Transfer (targetGlesGraphics, "targetGlesGraphics");
+
+ transfer.Transfer (targetResolution, "targetResolution");
+ if(targetResolution == 1)
+ targetResolution = 6; // SD -> x0.5
+ else if(targetResolution == 2)
+ targetResolution = 0; // HD -> Native
+
+ transfer.Transfer (accelerometerFrequency, "accelerometerFrequency");
+
+ transfer.Align();
+
+ transfer.Transfer (companyName, "companyName");
+ transfer.Transfer (productName, "productName");
+
+ transfer.Transfer (defaultCursor, "defaultCursor");
+ transfer.Transfer (cursorHotspot, "cursorHotspot");
+
+ transfer.Transfer (defaultScreenWidth, "defaultScreenWidth");
+ transfer.Transfer (defaultScreenHeight, "defaultScreenHeight");
+ transfer.Transfer (defaultWebScreenWidth, "defaultScreenWidthWeb");
+ transfer.Transfer (defaultWebScreenHeight, "defaultScreenHeightWeb");
+ TRANSFER (m_RenderingPath);
+ TRANSFER (m_MobileRenderingPath);
+ TRANSFER (m_ActiveColorSpace);
+ TRANSFER (m_MTRendering);
+ TRANSFER (m_MobileMTRendering);
+ TRANSFER (m_UseDX11);
+
+ transfer.Align();
+
+ transfer.Transfer (iosShowActivityIndicatorOnLoading, "iosShowActivityIndicatorOnLoading");
+ transfer.Transfer (androidShowActivityIndicatorOnLoading, "androidShowActivityIndicatorOnLoading");
+
+ transfer.Transfer (displayResolutionDialog, "displayResolutionDialog");
+
+ transfer.Transfer (uiAutoRotateToPortrait, "allowedAutorotateToPortrait");
+ transfer.Transfer (uiAutoRotateToPortraitUpsideDown, "allowedAutorotateToPortraitUpsideDown");
+ transfer.Transfer (uiAutoRotateToLandscapeRight, "allowedAutorotateToLandscapeRight");
+ transfer.Transfer (uiAutoRotateToLandscapeLeft, "allowedAutorotateToLandscapeLeft");
+ transfer.Transfer (uiUseAnimatedAutoRotation, "useOSAutorotation");
+ transfer.Transfer (uiUse32BitDisplayBuffer, "use32BitDisplayBuffer");
+ transfer.Transfer (uiUse24BitDepthBuffer, "use24BitDepthBuffer");
+ transfer.Align();
+
+ transfer.Transfer (defaultIsFullScreen, "defaultIsFullScreen");
+ transfer.Transfer (defaultIsNativeResolution, "defaultIsNativeResolution");
+ transfer.Transfer (runInBackground, "runInBackground");
+ transfer.Transfer (captureSingleScreen, "captureSingleScreen");
+
+ transfer.Transfer (overrideIPodMusic, "Override IPod Music");
+ transfer.Transfer (prepareIOSForRecording, "Prepare IOS For Recording");
+ transfer.Transfer (enableHWStatistics, "enableHWStatistics");
+ TRANSFER (usePlayerLog);
+ TRANSFER (stripPhysics);
+#if ENABLE_SINGLE_INSTANCE_BUILD_SETTING
+ TRANSFER (forceSingleInstance);
+#endif
+ TRANSFER (resizableWindow);
+ TRANSFER (useMacAppStoreValidation);
+ TRANSFER (gpuSkinning);
+ TRANSFER (xboxPIXTextureCapture);
+ TRANSFER (xboxEnableAvatar);
+ TRANSFER (xboxEnableKinect);
+ TRANSFER (xboxEnableKinectAutoTracking);
+ TRANSFER (xboxEnableFitness);
+ transfer.Align();
+ TRANSFER (macFullscreenMode);
+ TRANSFER (xboxSpeechDB);
+ TRANSFER (xboxEnableHeadOrientation);
+ TRANSFER (xboxEnableGuest);
+ transfer.Align();
+
+ TRANSFER (wiiHio2Usage);
+ TRANSFER (wiiLoadingScreenRectPlacement);
+ TRANSFER (wiiLoadingScreenBackground);
+ TRANSFER (wiiLoadingScreenPeriod);
+ TRANSFER (wiiLoadingScreenFileName);
+ TRANSFER (wiiLoadingScreenRect);
+
+
+ TRANSFER (m_SupportedAspectRatios);
+
+ transfer.Transfer (iPhoneBundleIdentifier, "iPhoneBundleIdentifier");
+
+
+#if UNITY_EDITOR
+ if (!transfer.IsSerializingForGameRelease())
+ {
+ m_EditorOnly.Transfer(transfer);
+
+ /////@TODO: GET RID OF THIS TOO
+
+ transfer.Transfer (firstStreamedLevelWithResources, "firstStreamedLevelWithResources");
+ transfer.Transfer (unityRebuildLibraryVersion, "unityRebuildLibraryVersion", kHideInEditorMask);
+ transfer.Transfer (unityForwardCompatibleVersion, "unityForwardCompatibleVersion", kHideInEditorMask);
+ transfer.Transfer (unityStandardAssetsVersion, "unityStandardAssetsVersion", kHideInEditorMask);
+ }
+#endif
+}
+
+ColorSpace PlayerSettings::GetValidatedColorSpace () const
+{
+ // On xBox, don't check GraphicsCaps, because we need to know the color space when setting up
+ // front/back buffers, at which point GraphicsCaps are not initialized. We know that Xenon supports
+ // sRGB.
+ if (gGraphicsCaps.hasSRGBReadWrite || UNITY_XENON)
+ return static_cast<ColorSpace>(m_ActiveColorSpace);
+ else
+ return kGammaColorSpace;
+}
+
+RenderingPath PlayerSettings::GetRenderingPathRuntime()
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ return GetMobileRenderingPath();
+#else
+ return GetRenderingPath();
+#endif
+}
+
+void PlayerSettings::SetRenderingPathRuntime(RenderingPath rp)
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ SetMobileRenderingPath(rp);
+#else
+ SetRenderingPath(rp);
+#endif
+}
+
+bool PlayerSettings::GetMTRenderingRuntime()
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ return GetMobileMTRendering();
+#else
+ return GetMTRendering();
+#endif
+}
+void PlayerSettings::SetMTRenderingRuntime(bool mtRendering)
+{
+#if UNITY_ANDROID || UNITY_BB10 || UNITY_IPHONE || UNITY_TIZEN
+ SetMobileMTRendering(mtRendering);
+#else
+ SetMTRendering(mtRendering);
+#endif
+}
+
+IMPLEMENT_CLASS_HAS_POSTINIT (PlayerSettings)
+IMPLEMENT_OBJECT_SERIALIZE (PlayerSettings)
+
+GET_MANAGER (PlayerSettings)
+GET_MANAGER_PTR (PlayerSettings)
+
diff --git a/Runtime/Misc/PlayerSettings.h b/Runtime/Misc/PlayerSettings.h
new file mode 100644
index 0000000..5e25d53
--- /dev/null
+++ b/Runtime/Misc/PlayerSettings.h
@@ -0,0 +1,458 @@
+#ifndef PROJECTSETTINGS_H
+#define PROJECTSETTINGS_H
+
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Math/Rect.h"
+#include "Runtime/Camera/RenderLoops/RenderLoopEnums.h"
+#include "Runtime/Math/Vector2.h"
+
+#if UNITY_EDITOR
+#include "Editor/Src/EditorOnlyPlayerSettings.h"
+#endif
+
+class Texture2D;
+
+
+
+enum { kDisableResolutionDialog = 0, kShowResolutionDialog = 1, kShowResolutionDialogDefaultNone = 2 };
+
+enum ResolutionAspect
+{
+ kAspectOthers = 0,
+ kAspect4by3,
+ kAspect5by4,
+ kAspect16by10,
+ kAspect16by9,
+ kAspectCount
+};
+
+enum MacFullscreenMode
+{
+ kCaptureDisplay,
+ kFullscreenWindow,
+ kFullscreenWindowWithMenuBarAndDock,
+};
+
+struct AspectRatios
+{
+ DECLARE_SERIALIZE_NO_PPTR (AspectRatios)
+
+ bool m_Ratios[kAspectCount];
+
+ AspectRatios () { Reset (); }
+
+ void Reset()
+ {
+ for (int i = 0; i < kAspectCount; ++i)
+ m_Ratios[i] = true;
+ }
+};
+
+class PlayerSettings : public GlobalGameManager
+{
+public:
+
+ DECLARE_OBJECT_SERIALIZE (PlayerSettings)
+ REGISTER_DERIVED_CLASS (PlayerSettings, GlobalGameManager)
+ PlayerSettings (MemLabelId label, ObjectCreationMode mode);
+ // ~PlayerSettings (); declared-by-macro
+
+ virtual void CheckConsistency ();
+ void AwakeFromLoad (AwakeFromLoadMode mode);
+
+ bool DoesSupportResolution (int width, int height) const;
+ void InitDefaultCursors ();
+
+ static void InitializeClass ();
+ static void PostInitializeClass ();
+ static void CleanupClass () { }
+
+public:
+
+ UnityStr companyName;
+ UnityStr productName;
+ UnityStr version;
+
+ PPtr<Texture2D> defaultCursor;
+ Vector2f cursorHotspot;
+
+ bool androidProfiler;
+ int defaultScreenOrientation; ///< enum { Portrait = 0, Portrait Upside Down = 1, Landscape Right = 2, Landscape Left = 3, Auto Rotation = 4 }
+ int targetDevice; ///< enum { iPhone Only = 0, iPad Only = 1, iPhone + iPad = 2 }
+ int targetGlesGraphics; ///< enum { OpenGL ES 2.0 = 1, OpenGL ES 3.0 = 2 }
+ int targetResolution; ///< enum { Native (default device resolution) = 0, Auto (Best Performance) = 3, Auto (Best Quality) = 4, 320p (iPhone) = 5, 640p (iPhone Retina Display) = 6, 768p (iPad) = 7 }
+ int accelerometerFrequency; ///< enum { Disabled = 0, 15 Hz = 15, 30 Hz = 30, 60 Hz = 60, 100 Hz = 100 }
+
+ // General
+ int defaultScreenWidth;
+ int defaultScreenHeight;
+
+ int defaultWebScreenWidth;
+ int defaultWebScreenHeight;
+
+ int displayResolutionDialog;///< enum { Disabled = 0, Enabled = 1, Hidden By Default = 2 }
+ AspectRatios m_SupportedAspectRatios;
+
+ int m_RenderingPath; ///< enum { Vertex Lit=0, Forward=1, Deferred Lighting=2 } Rendering path to use.
+ int m_MobileRenderingPath; ///< enum { Vertex Lit=0, Forward=1, Deferred Lighting=2 } Rendering path to use on Mobiles.
+ int m_ActiveColorSpace; ///< enum { Gamma, Linear }
+ bool m_MTRendering;
+ bool m_MobileMTRendering;
+ bool m_UseDX11;
+
+ // TODO: unify probably with ios, though we will ne handle it specifically with EnumPopup or something on editor side
+ // TODO: if someone from editor team wants it - go ahead
+ // values are taken from android.R.attr docs
+ int androidShowActivityIndicatorOnLoading; ///< enum { Don't Show=-1, Large=0, Inversed Large=1, Small=2, Inversed Small=3 }
+ int iosShowActivityIndicatorOnLoading; ///< enum { Don't Show=-1, White Large=0, White=1, Gray=2 }
+
+ // auto-rotation handling. Expose bools on editor side
+ bool uiAutoRotateToPortrait;
+ bool uiAutoRotateToPortraitUpsideDown;
+ bool uiAutoRotateToLandscapeRight;
+ bool uiAutoRotateToLandscapeLeft;
+ bool uiUseAnimatedAutoRotation;
+
+ bool uiUse32BitDisplayBuffer;
+ bool uiUse24BitDepthBuffer;
+
+ bool defaultIsFullScreen;
+ bool defaultIsNativeResolution;
+ bool runInBackground;
+ bool captureSingleScreen;
+
+ bool overrideIPodMusic; ///< Force mixing behavior allowing iPod audio to play with FMOD even if the audio session doesn't usually permit this.
+ bool prepareIOSForRecording; ///< If enabled, the AudioSession of iOS will be initialized in recording mode, thus avoiding delays when initializing the Microphone object.
+
+ bool enableHWStatistics;
+
+ bool usePlayerLog;
+ bool stripPhysics;
+ bool useMacAppStoreValidation;
+ int macFullscreenMode; ///< enum {CaptureDisplay=0, FullscreenWindow=1, FullscreenWindowWithMenuBarAndDock=2}
+ bool forceSingleInstance;
+ bool resizableWindow;
+ bool gpuSkinning;
+ bool xboxPIXTextureCapture; // Xbox360
+ bool xboxEnableAvatar; // Xbox360
+ bool xboxEnableKinect; // Xbox360 Kinect
+ bool xboxEnableKinectAutoTracking; // Xbox360 Kinect
+ UInt32 xboxSpeechDB;
+ bool xboxEnableFitness;
+ bool xboxEnableHeadOrientation;
+ bool xboxEnableGuest;
+
+ int wiiHio2Usage; ///< enum { None=-1, Profiler=0, Automation=1 } Wii Hio2 Usage.
+ int wiiLoadingScreenRectPlacement; ///< enum {TopLeft = 0, TopCenter = 1, TopRight = 2, MiddleLeft = 3, MiddleCenter = 4, MiddlerRight = 5, BottomLeft = 6, BottomCenter = 7, BottomRight = 8} Placement.
+ ColorRGBAf wiiLoadingScreenBackground;
+ int wiiLoadingScreenPeriod;
+ UnityStr wiiLoadingScreenFileName;
+ Rectf wiiLoadingScreenRect;
+ int firstStreamedLevelWithResources;
+
+ UnityStr absoluteURL;// Web player
+ UnityStr srcValue;// Web player
+
+ UnityStr iPhoneBundleIdentifier;
+
+ UnityStr AndroidLicensePublicKey;
+
+ #if UNITY_EDITOR
+ EditorOnlyPlayerSettings m_EditorOnly;
+ EditorOnlyPlayerSettingsNotPersistent m_EditorOnlyNotPersistent;
+
+ ////////@TODO: Move this into a seperate manager!
+ int unityRebuildLibraryVersion;
+ int unityForwardCompatibleVersion;
+ int unityStandardAssetsVersion;
+ #endif
+
+public:
+
+ std::string GetCompanyName () const { return companyName; }
+ void SetCompanyName (std::string value) { companyName = value; SetDirty(); }
+
+ std::string GetProductName () const { return productName; }
+ void SetProductName (std::string value) { productName = value; SetDirty();}
+
+ int GetTargetDevice () const { return targetDevice; }
+ void SetTargetDevice (int value) { targetDevice = value; SetDirty();}
+
+ PPtr<Texture2D> GetDefaultCursor () const { return defaultCursor; }
+ void SetDefaultCursor (PPtr<Texture2D> value) { defaultCursor = value; SetDirty();}
+
+ Vector2f GetCursorHotspot () const { return cursorHotspot; }
+ void SetCursorHotspot (Vector2f value) { cursorHotspot = value; SetDirty();}
+
+ int GetTargetGlesGraphics () const { return targetGlesGraphics; }
+ void SetTargetGlesGraphics (int value) { targetGlesGraphics = value; SetDirty(); }
+
+ int GetTargetResolution () const { return targetResolution; }
+ void SetTargetResolution (int value) { targetResolution = value; SetDirty();}
+
+ int GetAccelerometerFrequency () const { return accelerometerFrequency; }
+ void SetAccelerometerFrequency (int value) { accelerometerFrequency = value; SetDirty();}
+
+ int GetDefaultScreenOrientation () const { return defaultScreenOrientation; }
+ void SetDefaultScreenOrientation (int value) { defaultScreenOrientation = value; SetDirty();}
+
+ bool GetUseAnimatedAutoRotation () const { return uiUseAnimatedAutoRotation; }
+ void SetUseAnimatedAutoRotation (bool value) { uiUseAnimatedAutoRotation = value; SetDirty();}
+
+ bool GetAutoRotationAllowed(int orientation) const;
+ void SetAutoRotationAllowed(int orientation, bool enabled);
+
+ bool GetUse32BitDisplayBuffer() const { return uiUse32BitDisplayBuffer; }
+ void SetUse32BitDisplayBuffer(bool use) { uiUse32BitDisplayBuffer = use; }
+
+ bool GetUse24BitDepthBuffer() const { return uiUse24BitDepthBuffer; }
+ void SetUse24BitDepthBuffer(bool use) { uiUse24BitDepthBuffer = use; }
+
+ int GetIOSShowActivityIndicatorOnLoading() const { return iosShowActivityIndicatorOnLoading; }
+ void SetIOSShowActivityIndicatorOnLoading(int mode) { iosShowActivityIndicatorOnLoading = mode; SetDirty();}
+ int GetAndroidShowActivityIndicatorOnLoading() const { return androidShowActivityIndicatorOnLoading; }
+ void SetAndroidShowActivityIndicatorOnLoading(int mode) { androidShowActivityIndicatorOnLoading = mode; SetDirty();}
+
+ int GetDefaultScreenWidth () const { return defaultScreenWidth; }
+ void SetDefaultScreenWidth (int value) { defaultScreenWidth = value; SetDirty();}
+
+ int GetDefaultScreenHeight () const { return defaultScreenHeight; }
+ void SetDefaultScreenHeight (int value) { defaultScreenHeight = value; SetDirty();}
+
+ int GetDefaultWebScreenWidth () const { return defaultWebScreenWidth; }
+ void SetDefaultWebScreenWidth (int value) { defaultWebScreenWidth = value; SetDirty();}
+
+ int GetDefaultWebScreenHeight () const { return defaultWebScreenHeight; }
+ void SetDefaultWebScreenHeight (int value) { this->defaultWebScreenHeight = value; SetDirty();}
+
+ int GetDisplayResolutionDialog () const { return displayResolutionDialog; }
+ void SetDisplayResolutionDialog (int value) { this->displayResolutionDialog = value; SetDirty();}
+
+ bool AspectRatioEnabled (int aspectRatio) const { return m_SupportedAspectRatios.m_Ratios [aspectRatio]; }
+ void SetAspectRatio (int aspectRatio, bool enabled) { m_SupportedAspectRatios.m_Ratios[aspectRatio] = enabled; SetDirty();}
+
+ bool GetDefaultIsFullScreen () const { return defaultIsFullScreen; }
+ void SetDefaultIsFullScreen (bool value) { defaultIsFullScreen = value; SetDirty(); }
+
+ bool GetDefaultIsNativeResolution () const { return defaultIsNativeResolution; }
+ void SetDefaultIsNativeResolution (bool value) { defaultIsNativeResolution = value; SetDirty(); }
+
+ bool GetRunInBackground () const { return runInBackground; }
+ void SetRunInBackground (bool value) { runInBackground = value; SetDirty(); }
+
+ bool GetStripPhysics () const { return stripPhysics; }
+ void SetStripPhysics (bool value) { stripPhysics = value; SetDirty(); }
+
+ bool GetUsePlayerLog () const { return usePlayerLog; }
+ void SetUsePlayerLog (bool value) { usePlayerLog = value; SetDirty(); }
+
+ bool GetEnableHWStatistics () const { return enableHWStatistics; }
+ void SetEnableHWStatistics (bool value) { enableHWStatistics = value; SetDirty(); }
+
+ bool GetUseMacAppStoreValidation () const { return useMacAppStoreValidation; }
+ void SetUseMacAppStoreValidation (bool value) { useMacAppStoreValidation = value; SetDirty(); }
+
+ int GetMacFullscreenMode () const { return macFullscreenMode; }
+ void SetMacFullscreenMode (int value) { macFullscreenMode = value; SetDirty(); }
+
+ bool GetForceSingleInstance () const { return forceSingleInstance; }
+ void SetForceSingleInstance (bool value) { forceSingleInstance = value; SetDirty(); }
+
+ bool GetResizableWindow () const { return resizableWindow; }
+ void SetResizableWindow (bool value) { resizableWindow = value; SetDirty(); }
+
+ bool GetGPUSkinning() const { return gpuSkinning; }
+ void SetGPUSkinning(bool value) { gpuSkinning = value; SetDirty(); }
+
+ bool GetXboxPIXTextureCapture() const { return xboxPIXTextureCapture; }
+ void SetXboxPIXTextureCapture(bool value) { xboxPIXTextureCapture = value; SetDirty(); }
+
+ bool GetXboxEnableAvatar() const { return xboxEnableAvatar; }
+ void SetXboxEnableAvatar(bool value) { xboxEnableAvatar = value; SetDirty(); }
+ bool GetXboxEnableGuest() const { return xboxEnableGuest; }
+ void SetXboxEnableGuest(bool value) { xboxEnableGuest = value; SetDirty(); }
+
+ bool GetXboxEnableKinect() const { return xboxEnableKinect; }
+ void SetXboxEnableKinect(bool value) { xboxEnableKinect = value; SetDirty(); }
+ bool GetXboxEnableKinectAutoTracking() const { return xboxEnableKinectAutoTracking; }
+ void SetXboxEnableKinectAutoTracking(bool value) { xboxEnableKinectAutoTracking = value; SetDirty(); }
+ UInt32 GetXboxSpeechDB() const { return xboxSpeechDB; }
+ void SetXboxSpeechDB(UInt32 value) { xboxSpeechDB = value; SetDirty(); }
+ bool GetXboxEnableFitness() const { return xboxEnableFitness; }
+ void SetXboxEnableFitness(bool value) { xboxEnableFitness = value; SetDirty(); }
+ bool GetXboxHeadOrientation() const { return xboxEnableHeadOrientation; }
+ void SetXboxHeadOrientation(bool value) { xboxEnableHeadOrientation = value; SetDirty(); }
+
+ bool GetCaptureSingleScreen () const { return captureSingleScreen; }
+ void SetCaptureSingleScreen (bool value) { captureSingleScreen = value; SetDirty(); }
+
+ int GetFirstStreamedLevelWithResources () const { return firstStreamedLevelWithResources; }
+ void SetFirstStreamedLevelWithResources (int value) { this->firstStreamedLevelWithResources = value; SetDirty(); }
+
+ void SetRenderingPath (RenderingPath rp) { m_RenderingPath = rp; SetDirty(); }
+ RenderingPath GetRenderingPath() const { return static_cast<RenderingPath>(m_RenderingPath); }
+
+ void SetMobileRenderingPath (RenderingPath rp) { m_MobileRenderingPath = rp; SetDirty(); }
+ RenderingPath GetMobileRenderingPath() const { return static_cast<RenderingPath>(m_MobileRenderingPath); }
+
+ RenderingPath GetRenderingPathRuntime();
+ void SetRenderingPathRuntime(RenderingPath rp);
+
+ void SetDesiredColorSpace (ColorSpace colorSpace) { m_ActiveColorSpace = colorSpace; SetDirty();}
+ ColorSpace GetDesiredColorSpace () const { return static_cast<ColorSpace> (m_ActiveColorSpace); }
+ ColorSpace GetValidatedColorSpace () const ;
+
+ void SetMTRendering (bool mtRendering) { m_MTRendering = mtRendering; SetDirty(); }
+ bool GetMTRendering () const { return m_MTRendering; }
+
+ void SetMobileMTRendering (bool mtRendering) { m_MobileMTRendering = mtRendering; SetDirty(); }
+ bool GetMobileMTRendering () const { return m_MobileMTRendering; }
+
+ bool GetMTRenderingRuntime();
+ void SetMTRenderingRuntime(bool mtRendering);
+
+
+ void SetUseDX11 (bool v) { m_UseDX11 = v; SetDirty(); }
+ bool GetUseDX11 () const { return m_UseDX11; }
+
+ std::string GetiPhoneBundleIdentifier () const { return iPhoneBundleIdentifier; }
+ void SetiPhoneBundleIdentifier (const std::string& value) { iPhoneBundleIdentifier = value; SetDirty();}
+
+ #if UNITY_EDITOR
+
+ int GetAPICompatibilityLevel () const { return m_EditorOnly.apiCompatibilityLevel; }
+ void SetAPICompatibilityLevel (int value) { m_EditorOnly.apiCompatibilityLevel = value; SetDirty();}
+
+ bool GetStripUnusedMeshComponents() const { return m_EditorOnly.stripUnusedMeshComponents; }
+ void SetStripUnusedMeshComponents (bool v) { m_EditorOnly.stripUnusedMeshComponents = v; SetDirty(); }
+
+ std::string GetAotOptions () const { return m_EditorOnly.aotOptions; }
+ void SetAotOptions (std::string value) { m_EditorOnly.aotOptions = value; SetDirty();}
+
+ std::string GetAndroidKeystoreName () const { return m_EditorOnly.AndroidKeystoreName; }
+ void SetAndroidKeystoreName (std::string value) { m_EditorOnly.AndroidKeystoreName = value; SetDirty();}
+
+ std::string GetAndroidKeystorePass () const { return m_EditorOnlyNotPersistent.AndroidKeystorePass; }
+ void SetAndroidKeystorePass (std::string value) { m_EditorOnlyNotPersistent.AndroidKeystorePass = value; SetDirty();}
+
+ std::string GetAndroidKeyaliasName () const { return m_EditorOnly.AndroidKeyaliasName; }
+ void SetAndroidKeyaliasName (std::string value) { m_EditorOnly.AndroidKeyaliasName = value; SetDirty();}
+
+ std::string GetAndroidKeyaliasPass () const { return m_EditorOnlyNotPersistent.AndroidKeyaliasPass; }
+ void SetAndroidKeyaliasPass (std::string value) { m_EditorOnlyNotPersistent.AndroidKeyaliasPass = value; SetDirty();}
+
+ bool GetAndroidLicenseVerification () const { return !AndroidLicensePublicKey.empty(); }
+
+ int GetUseAPKExpansionFiles () const { return m_EditorOnly.enableAndroidExpansionFiles; }
+ void SetUseAPKExpansionFiles (int value) { m_EditorOnly.enableAndroidExpansionFiles = value; SetDirty();}
+
+ std::string GetiPhoneBundleVersion () const { return m_EditorOnly.iPhoneBundleVersion; }
+ void SetiPhoneBundleVersion (std::string value) { m_EditorOnly.iPhoneBundleVersion = value; SetDirty();}
+
+ int GetiPhoneStrippingLevel () const { return m_EditorOnly.iPhoneStrippingLevel; }
+ void SetiPhoneStrippingLevel (int value) { m_EditorOnly.iPhoneStrippingLevel = value; SetDirty();}
+
+ int GetAndroidBundleVersionCode () const { return m_EditorOnly.AndroidBundleVersionCode; }
+ void SetAndroidBundleVersionCode (int value) { m_EditorOnly.AndroidBundleVersionCode = value; SetDirty();}
+
+ int GetAndroidMinSdkVersion () const { return m_EditorOnly.AndroidMinSdkVersion; }
+ void SetAndroidMinSdkVersion (int value) { m_EditorOnly.AndroidMinSdkVersion = value; SetDirty();}
+
+ int GetAndroidPreferredInstallLocation () const { return m_EditorOnly.AndroidPreferredInstallLocation; }
+ void SetAndroidPreferredInstallLocation (int value) { m_EditorOnly.AndroidPreferredInstallLocation = value; SetDirty();}
+
+ int GetForceAndroidInternetPermission () const { return m_EditorOnly.forceAndroidInternetPermission; }
+ void SetForceAndroidInternetPermission (int value) { m_EditorOnly.forceAndroidInternetPermission = value; SetDirty();}
+
+ int GetForceAndroidSDCardPermission () const { return m_EditorOnly.forceAndroidSDCardPermission; }
+ void SetForceAndroidSDCardPermission (int value) { m_EditorOnly.forceAndroidSDCardPermission = value; SetDirty();}
+
+ int GetCreateAndroidWallpaper () const { return m_EditorOnly.createAndroidWallpaper; }
+ void SetCreateAndroidWallpaper (int value) { m_EditorOnly.createAndroidWallpaper = value; SetDirty();}
+
+ int GetAndroidTargetDevice () const { return m_EditorOnly.AndroidTargetDevice; }
+ void SetAndroidTargetDevice (int value) { m_EditorOnly.AndroidTargetDevice = value; SetDirty();}
+
+ int GetAndroidSplashScreenScale () const { return m_EditorOnly.AndroidSplashScreenScale; }
+ void SetAndroidSplashScreenScale (int value) { m_EditorOnly.AndroidSplashScreenScale = value; SetDirty();}
+
+ std::string GetBlackBerryDeviceAddress () { return m_EditorOnly.blackberryDeviceAddress; }
+ void SetBlackBerryDeviceAddress (std::string value) { m_EditorOnly.blackberryDeviceAddress = value; SetDirty();}
+
+ std::string GetBlackBerryDevicePassword () { return m_EditorOnly.blackberryDevicePassword; }
+ void SetBlackBerryDevicePassword (std::string value) { m_EditorOnly.blackberryDevicePassword = value; SetDirty();}
+
+ std::string GetTizenProductDescription () { return m_EditorOnly.tizenProductDescription; }
+ void SetTizenProductDescription (std::string value) { m_EditorOnly.tizenProductDescription = value; SetDirty();}
+
+ std::string GetTizenProductURL () { return m_EditorOnly.tizenProductURL; }
+ void SetTizenProductURL (std::string value) { m_EditorOnly.tizenProductURL = value; SetDirty();}
+
+ std::string GetTizenCertificatePath () { return m_EditorOnly.tizenCertificatePath; }
+ void SetTizenCertificatePath (std::string value) { m_EditorOnly.tizenCertificatePath = value; SetDirty();}
+
+ std::string GetTizenCertificatePassword () { return m_EditorOnly.tizenCertificatePassword; }
+ void SetTizenCertificatePassword (std::string value) { m_EditorOnly.tizenCertificatePassword = value; SetDirty();}
+
+ int GetiPhoneScriptCallOptimization () const { return m_EditorOnly.iPhoneScriptCallOptimization; }
+ void SetiPhoneScriptCallOptimization (int value) { m_EditorOnly.iPhoneScriptCallOptimization = value; SetDirty();}
+
+ int GetiPhoneSdkVersion () const { return m_EditorOnly.iPhoneSdkVersion; }
+ void SetiPhoneSdkVersion (int value) { m_EditorOnly.iPhoneSdkVersion = value; SetDirty();}
+
+ int GetiPhoneTargetOSVersion () const { return m_EditorOnly.iPhoneTargetOSVersion; }
+ void SetiPhoneTargetOSVersion (int value) { m_EditorOnly.iPhoneTargetOSVersion = value; SetDirty();}
+
+ int GetUIPrerenderedIcon () const { return m_EditorOnly.uIPrerenderedIcon; }
+ void SetUIPrerenderedIcon (int value) { m_EditorOnly.uIPrerenderedIcon = value; SetDirty();}
+
+ int GetUIRequiresPersistentWiFi () const { return m_EditorOnly.uIRequiresPersistentWiFi; }
+ void SetUIRequiresPersistentWiFi (int value) { m_EditorOnly.uIRequiresPersistentWiFi = value; SetDirty();}
+
+ int GetUIStatusBarHidden () const { return m_EditorOnly.uIStatusBarHidden; }
+ void SetUIStatusBarHidden (int value) { m_EditorOnly.uIStatusBarHidden = value; SetDirty();}
+
+ int GetUIStatusBarStyle () const { return m_EditorOnly.uIStatusBarStyle; }
+ void SetUIStatusBarStyle (int value) { m_EditorOnly.uIStatusBarStyle = value; SetDirty();}
+
+ int GetUIExitOnSuspend () const { return m_EditorOnly.uIExitOnSuspend; }
+ void SetUIExitOnSuspend (int value) { m_EditorOnly.uIExitOnSuspend = value; SetDirty();}
+
+ PPtr <Texture2D> GetResolutionDialogBanner () const { return m_EditorOnly.resolutionDialogBanner; }
+ void SetResolutionDialogBanner (PPtr <Texture2D> value) { m_EditorOnly.resolutionDialogBanner = value; SetDirty(); }
+
+ std::vector<int> GetPlatformIconSizes (const std::string& platform) { return m_EditorOnly.GetPlatformIconSizes(platform); }
+ std::vector<PPtr<Texture2D> > GetPlatformIcons (const std::string& platform) { return m_EditorOnly.GetPlatformIcons(platform); }
+ void SetPlatformIcons(const std::string& platform, std::vector<PPtr<Texture2D> > icons) { if (m_EditorOnly.SetPlatformIcons(platform, icons)) SetDirty(); }
+
+ Texture2D* GetPlatformIconForSize (const std::string& platform, int size) { return m_EditorOnly.GetPlatformIconForSize(platform, size); }
+
+ void GetPlatformBatching (BuildTargetPlatform platform, bool* outStaticBatching, bool* outDynamicBatching) const { m_EditorOnly.GetPlatformBatching(platform, outStaticBatching, outDynamicBatching); }
+ void SetPlatformBatching (BuildTargetPlatform platform, bool staticBatching, bool dynamicBatching) { m_EditorOnly.SetPlatformBatching(platform, staticBatching, dynamicBatching); SetDirty(); }
+
+ void SetWebPlayerTemplate (UnityStr value) { if (!m_EditorOnly.m_WebPlayerTemplate.compare(value)) { m_EditorOnly.m_WebPlayerTemplate = value; SetDirty (); } };
+ void SetTemplateCustomKeys (std::vector<std::string> keys) { m_EditorOnly.SetTemplateCustomKeys(keys); SetDirty(); }
+ void SetTemplateCustomValue (std::string key, std::string value) { if (m_EditorOnly.SetTemplateCustomValue(key, value)) SetDirty(); }
+
+ std::vector<std::string> GetTemplateCustomKeys () { return m_EditorOnly.GetTemplateCustomKeys(); }
+ std::string GetWebPlayerTemplate () const { return m_EditorOnly.m_WebPlayerTemplate; }
+ std::string GetTemplateCustomValue (std::string key) { return m_EditorOnly.GetTemplateCustomValue(key); }
+
+ const EditorOnlyPlayerSettings& GetEditorOnly () const { return m_EditorOnly; }
+
+ ///Always marks as dirty
+ EditorOnlyPlayerSettings& GetEditorOnlyForUpdate () { SetDirty(); return m_EditorOnly; }
+ EditorOnlyPlayerSettingsNotPersistent& GetEditorOnlyNotPersistent () { return m_EditorOnlyNotPersistent; }
+
+private:
+ std::vector<EditorOnlyPlayerSettings::IconWithSize> GetBestIconWithSizes (const std::string& platform);
+
+ #endif
+};
+
+PlayerSettings& GetPlayerSettings ();
+PlayerSettings* GetPlayerSettingsPtr ();
+
+#endif
diff --git a/Runtime/Misc/Plugins.cpp b/Runtime/Misc/Plugins.cpp
new file mode 100644
index 0000000..1271c31
--- /dev/null
+++ b/Runtime/Misc/Plugins.cpp
@@ -0,0 +1,231 @@
+#include "UnityPrefix.h"
+#include "Plugins.h"
+
+#if UNITY_PLUGINS_AVAILABLE
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#if UNITY_WIN
+#include "PlatformDependent/Win/PathUnicodeConversion.h"
+#endif
+#if UNITY_OSX || UNITY_LINUX
+#include <dlfcn.h>
+#endif
+#if UNITY_XENON
+#include "Runtime/Mono/MonoIncludes.h"
+#endif
+
+
+const char* FindPluginExecutable (const char* pluginName); // implemented in platform dependent
+
+
+
+// --------------------------------------------------------------------------
+
+static void* LoadPluginExecutable (const char* pluginPath)
+{
+ #if UNITY_WINRT
+ std::wstring widePath;
+ ConvertUnityPathName(pluginPath, widePath);
+ return (void*)LoadPackagedLibrary (widePath.c_str(), 0);
+ #elif (UNITY_WIN && !UNITY_WINRT)
+ std::wstring widePath;
+ ConvertUnityPathName(pluginPath, widePath);
+ return (void*)LoadLibraryW (widePath.c_str());
+
+ #elif UNITY_OSX || UNITY_LINUX
+ return dlopen (pluginPath, RTLD_NOW);
+
+ #elif UNITY_XENON
+ return cached_module_load(pluginPath, MONO_DL_LAZY, 0);
+
+ #else
+ return NULL;
+
+ #endif
+}
+
+static void UnloadPluginExecutable (void* pluginHandle)
+{
+#if (UNITY_WIN || UNITY_WINRT)
+ FreeLibrary ((HMODULE) pluginHandle);
+
+#elif UNITY_OSX || UNITY_LINUX
+ dlclose (pluginHandle);
+
+#else
+ // We don't unload on other platforms. Only really relevant to editor
+ // anyway.
+#endif
+}
+
+static void* LoadPluginFunction (void* pluginHandle, const char* name)
+{
+ #if (UNITY_WIN || UNITY_WINRT)
+ return GetProcAddress ((HMODULE)pluginHandle, name);
+
+ #elif UNITY_OSX || UNITY_LINUX
+ return dlsym (pluginHandle, name);
+
+ #elif UNITY_XENON
+ void* symbol = 0;
+ char* err = mono_dl_symbol((MonoDl*)pluginHandle, name, &symbol);
+ g_free(err);
+ return symbol;
+
+ #else
+ return NULL;
+
+ #endif
+}
+
+
+// --------------------------------------------------------------------------
+
+
+typedef void PluginSetGraphicsDeviceFunc (void* device, int deviceType, int eventType);
+typedef void PluginRenderMarkerFunc (int marker);
+
+struct UnityPlugin {
+ void* pluginHandle;
+ PluginSetGraphicsDeviceFunc* setGraphicsDeviceFunc;
+ PluginRenderMarkerFunc* renderMarkerFunc;
+};
+
+typedef dynamic_array<UnityPlugin> UnityPluginArray;
+static UnityPluginArray g_Plugins;
+
+
+
+static void InitializePlugin (void* pluginHandle)
+{
+ // do nothing if we've already loaded this plugin
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ {
+ if (g_Plugins[i].pluginHandle == pluginHandle)
+ return;
+ }
+
+ UnityPlugin plugin;
+ plugin.pluginHandle = pluginHandle;
+ plugin.setGraphicsDeviceFunc = (PluginSetGraphicsDeviceFunc*)LoadPluginFunction (pluginHandle, "UnitySetGraphicsDevice");
+ plugin.renderMarkerFunc = (PluginRenderMarkerFunc*)LoadPluginFunction (pluginHandle, "UnityRenderEvent");
+ g_Plugins.push_back (plugin);
+
+ if (IsGfxDevice() && plugin.setGraphicsDeviceFunc)
+ {
+ GfxDevice& device = GetGfxDevice();
+ plugin.setGraphicsDeviceFunc (device.GetNativeGfxDevice(), device.GetRenderer(), kGfxDeviceEventInitialize);
+ }
+}
+
+
+void PluginsSetGraphicsDevice (void* device, int deviceType, GfxDeviceEventType eventType)
+{
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ {
+ if (g_Plugins[i].setGraphicsDeviceFunc)
+ {
+ g_Plugins[i].setGraphicsDeviceFunc (device, deviceType, eventType);
+ }
+ }
+}
+
+
+void PluginsRenderMarker (int marker)
+{
+ if (!IsGfxDevice())
+ return;
+ GfxDevice& device = GetRealGfxDevice();
+
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ {
+ if (g_Plugins[i].renderMarkerFunc)
+ {
+ device.InvalidateState();
+ g_Plugins[i].renderMarkerFunc (marker);
+ device.InvalidateState();
+ }
+ }
+}
+
+#if UNITY_EDITOR
+static bool gAllowPlugins = true;
+void SetAllowPlugins (bool allow)
+{
+ gAllowPlugins = allow;
+}
+
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/Backend/ScriptingInvocation.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Editor/Src/Keys/PublicKeys.h"
+
+static bool TryVerifySignature (const char *pluginPath, const unsigned char publicKey[])
+{
+ ScriptingInvocation verifySignature ("UnityEngine", "Security", "VerifySignature");
+ verifySignature.AddString (pluginPath);
+ verifySignature.AddArray (CreateScriptingArray (publicKey, PUBLIC_KEY_SIZE, GetScriptingTypeRegistry ().GetType ("System", "Byte")));
+ if (!MonoObjectToBool (verifySignature.Invoke ()))
+ return false;
+
+ // Additionally enable render to texture
+ GetBuildSettings ().hasRenderTexture = true;
+ return true;
+}
+
+// Explicitly allow plugins signed by approved parties
+static bool VerifySignature (const char *pluginPath)
+{
+ // Qualcomm
+ if (TryVerifySignature (pluginPath, kqualcomm))
+ return true;
+
+ return false;
+}
+#endif
+
+const char* FindAndLoadUnityPlugin (const char* pluginName)
+{
+ const char* pluginPath = FindPluginExecutable (pluginName);
+
+ if (pluginPath == NULL
+#if !UNITY_WINRT
+ || !strcmp(pluginPath, pluginName)
+#endif
+ )
+ return pluginPath; // not found
+
+#if UNITY_EDITOR
+ if (!gAllowPlugins)
+ {
+ if (!VerifySignature (pluginPath))
+ {
+ ErrorString ("License error. This plugin is only supported in Unity Pro!\n");
+ return pluginName;
+ }
+ }
+#endif
+
+ // plugin found, try loading & initializing it
+ void* pluginHandle = LoadPluginExecutable (pluginPath);
+ if (pluginHandle)
+ InitializePlugin (pluginHandle);
+
+ return pluginPath;
+}
+
+
+void UnloadAllPlugins ()
+{
+ for (size_t i = 0; i < g_Plugins.size(); ++i)
+ UnloadPluginExecutable (g_Plugins[i].pluginHandle);
+
+ g_Plugins.clear ();
+}
+
+
+#endif // UNITY_PLUGINS_AVAILABLE
diff --git a/Runtime/Misc/Plugins.h b/Runtime/Misc/Plugins.h
new file mode 100644
index 0000000..1230835
--- /dev/null
+++ b/Runtime/Misc/Plugins.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Runtime/GfxDevice/GfxDevice.h"
+
+void SetAllowPlugins (bool allow);
+
+// Never change the enum values!
+// They are used in low level native plugin interface.
+enum GfxDeviceEventType {
+ kGfxDeviceEventInitialize = 0,
+ kGfxDeviceEventShutdown = 1,
+ kGfxDeviceEventBeforeReset = 2,
+ kGfxDeviceEventAfterReset = 3,
+};
+
+#if UNITY_PLUGINS_AVAILABLE
+const char* FindAndLoadUnityPlugin (const char* pluginName);
+// Used by GfxDevice
+void PluginsSetGraphicsDevice (void* device, int deviceType, GfxDeviceEventType eventType);
+void PluginsRenderMarker (int marker);
+void UnloadAllPlugins ();
+#else
+inline void PluginsSetGraphicsDevice (void* device, int deviceType, GfxDeviceEventType eventType) { }
+inline void PluginsRenderMarker (int marker) { }
+#endif
diff --git a/Runtime/Misc/PreloadManager.cpp b/Runtime/Misc/PreloadManager.cpp
new file mode 100644
index 0000000..a5e1fc6
--- /dev/null
+++ b/Runtime/Misc/PreloadManager.cpp
@@ -0,0 +1,1013 @@
+#include "UnityPrefix.h"
+#include "PreloadManager.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+///////// FUSIONFALL todo: Fix this include by moving it to player i guess
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Threads/ThreadUtility.h"
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Mono/MonoIncludes.h"
+#include "Runtime/Graphics/SubstanceSystem.h"
+#include "Runtime/Misc/AssetBundleUtility.h"
+
+#if UNITY_ANDROID
+ #include "PlatformDependent/AndroidPlayer/EntryPoint.h"
+#endif
+#if UNITY_WII
+#include "PlatformDependent/Wii/WiiLoadingScreen.h"
+#endif
+
+
+PROFILER_INFORMATION (gPreloadLevel, "Application.LoadLevelAsync Integrate", kProfilerLoading);
+PROFILER_INFORMATION (gPreloadBundle, "AssetBundle.LoadAsync Integrate", kProfilerLoading);
+PROFILER_INFORMATION (gIntegrateAssetsInBackground, "Application.Integrate Assets in Background", kProfilerLoading);
+PROFILER_INFORMATION (gAsyncOperationComplete, "Application.WaitForAsyncOperationToComplete", kProfilerLoading);
+
+#if THREADED_LOADING
+ #define THREADED_LOADING_MUTEX_AUTOLOCK(x) Mutex::AutoLock lock (x)
+#else
+ #define THREADED_LOADING_MUTEX_AUTOLOCK(x) {}
+#endif
+
+#define PROFILE_PRELOAD_MANAGER 0
+#if PROFILE_PRELOAD_MANAGER
+#include "Runtime/Input/TimeManager.h"
+#endif
+
+static void GetFileIDsForLoadingScene (const string& pathName, PreloadLevelOperation::LoadingMode loadingMode, vector<LocalIdentifierInFileType>& fileIDs, vector<int>& managerIndices);
+PreloadData::~PreloadData ()
+{}
+
+template<class TransferFunction>
+void PreloadData::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.Transfer (m_Assets, "m_Assets");
+}
+
+IMPLEMENT_OBJECT_SERIALIZE(PreloadData)
+IMPLEMENT_CLASS(PreloadData)
+
+PreloadManager* gPreloadManager = NULL;
+
+PreloadManager& GetPreloadManager()
+{
+ if (gPreloadManager != NULL)
+ return *gPreloadManager;
+ else
+ {
+ gPreloadManager = new PreloadManager();
+ return *gPreloadManager;
+ }
+}
+
+void ReleasePreloadManager()
+{
+ delete gPreloadManager;
+ gPreloadManager = NULL;
+}
+
+void StopPreloadManager()
+{
+ if (gPreloadManager)
+ gPreloadManager->Stop();
+}
+
+PreloadManager::PreloadManager()
+: m_ProcessingOperation (NULL)
+{
+ m_IntegrationOperation = NULL;
+#if ENABLE_MONO
+ m_InitDomain = NULL;
+#endif
+#if THREADED_LOADING
+ m_Thread.SetName ("UnityPreload");
+#endif
+}
+
+PreloadManager::~PreloadManager()
+{
+ Stop();
+}
+
+void PreloadManager::SetThreadPriority(ThreadPriority p)
+{
+#if THREADED_LOADING
+ m_Thread.SetPriority(p);
+#endif
+}
+
+ThreadPriority PreloadManager::GetThreadPriority ()
+{
+#if THREADED_LOADING
+ return m_Thread.GetPriority();
+#else
+ return kNormalPriority;
+#endif
+}
+
+#if UNITY_EDITOR
+void PreloadManager::RemoveStopPlaymodeOperations ()
+{
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ for (int i=0;i<m_PreloadQueue.size();i++)
+ {
+ PreloadManagerOperation* op = m_PreloadQueue[i];
+ if (op && op->IsPlaymodeLoadLevel())
+ {
+ op->CleanupCoroutine();
+ op->Release();
+ m_PreloadQueue.erase(m_PreloadQueue.begin() + i);
+ i--;
+ }
+ }
+}
+#endif
+
+void PreloadManager::Stop ()
+{
+#if THREADED_LOADING
+ m_Thread.SignalQuit();
+
+ {
+ PROFILER_AUTO(gIntegrateAssetsInBackground, NULL)
+ // Wait until loading is complete, to make sure everything is integrated so that we don't leak anything.
+ while (m_Thread.IsRunning())
+ {
+ UpdatePreloadingSingleStep(true);
+ Thread::Sleep(0.01F);
+ }
+ }
+
+ m_Thread.WaitForExit();
+#endif
+
+ InvokeCoroutineCallbacks ();
+
+ AssertIf(!m_CallCoroutineCallbackQueue.empty());
+
+ {
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+
+ for (int i=0;i<m_PreloadQueue.size();i++)
+ {
+ m_PreloadQueue[i]->CleanupCoroutine();
+ m_PreloadQueue[i]->Release();
+ }
+ m_PreloadQueue.clear();
+ m_CallCoroutineCallbackQueue.clear();
+ }
+
+ Assert(m_IntegrationOperation == NULL);
+ Assert(m_ProcessingOperation == NULL);
+ #if ENABLE_MONO
+ m_InitDomain = NULL;
+ #endif
+}
+
+#if THREADED_LOADING
+void* PreloadManager::Run (void* managerPtr)
+{
+#if ENABLE_PROFILER
+ profiler_initialize_thread ("PreloadManager Thread", true);
+#endif
+
+ PreloadManager& manager = *static_cast<PreloadManager*> (managerPtr);
+ manager.Run();
+
+#if ENABLE_PROFILER
+ profiler_cleanup_thread();
+#endif
+
+ return NULL;
+}
+
+void PreloadManager::Run ()
+{
+ #if ENABLE_MONO
+ mono_thread_attach(m_InitDomain);
+ m_InitDomain = NULL;
+ #endif
+
+#if PROFILE_PRELOAD_MANAGER
+ float startTime = GetTimeSinceStartup();
+ #define LOG_PROFILER(x) printf_console(x, m_ProcessingOperation->GetDebugName().c_str(), (GetTimeSinceStartup() - startTime) * 1000.0F); startTime = GetTimeSinceStartup();
+#else
+ #define LOG_PROFILER(x)
+#endif
+
+ while (!m_Thread.IsQuitSignaled())
+ {
+ m_QueueMutex.Lock();
+
+ if (!m_PreloadQueue.empty())
+ {
+ Assert(m_IntegrationOperation == NULL);
+
+ // Find highest priority queued item
+ int index = PreloadManager::FindTopPriorityOperation (m_PreloadQueue);
+
+ // Grab item and remove it from queue
+ Assert(m_ProcessingOperation == NULL);
+ m_ProcessingOperation = m_PreloadQueue[index];
+
+ LOG_PROFILER("Begin Background load: %s [idle time: %f]\n");
+
+ m_PreloadQueue.erase(m_PreloadQueue.begin() + index);
+
+ m_QueueMutex.Unlock();
+
+ m_LoadingMutex.Lock();
+
+ // Process it
+ m_ProcessingOperation->Perform();
+
+ LOG_PROFILER("Async background load complete: %s - %f ms\n");
+
+ bool hasIntegrate = m_ProcessingOperation->HasIntegrateMainThread();
+
+ // Integrate any work into main thread, wait for completion,
+ // so that the persistentmanager is kept clear until the integration thread is done
+ if (hasIntegrate)
+ {
+ AssertIf(m_IntegrationOperation != NULL);
+
+ m_IntegrationOperation = m_ProcessingOperation;
+
+ // This is needed in order to make sure that m_IntegrationOperation is properly synced when
+ // the signaled thread reads the value (otherwise we get an assert - so this is actually to prevent the assert.
+ UnityMemoryBarrier();
+
+ // Temporarily release the loading mutex so that the main thread can do
+ // LockPreloading() without deadlocking.
+ m_LoadingMutex.Unlock ();
+
+ m_IntegrationSemaphore.WaitForSignal();
+ Assert (NULL == m_IntegrationOperation);
+
+ // Re-acquire the loading mutex while we finish the current operation.
+ m_LoadingMutex.Lock ();
+ }
+
+ m_QueueMutex.Lock();
+ // Pass operation over into m_CallCoroutineCallbackQueue, which will finally release it
+ m_CallCoroutineCallbackQueue.push_back(m_ProcessingOperation);
+ //m_ProcessingOperation->Release();
+ LOG_PROFILER("Completed Integration step: %s - %f ms\n");
+ m_ProcessingOperation = NULL;
+
+ m_QueueMutex.Unlock();
+
+ m_LoadingMutex.Unlock();
+ }
+ else
+ {
+ m_QueueMutex.Unlock();
+ Thread::Sleep(0.1F);
+ }
+ }
+ #if ENABLE_MONO
+ mono_thread_detach(mono_thread_current ());
+ #endif
+
+#undef PROFILE_PRELOAD_MANAGER
+}
+
+#endif
+
+bool PreloadManager::IsLoading()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+#if THREADED_LOADING
+ return m_LoadingMutex.IsLocked();
+#else
+ return false;
+#endif
+}
+
+bool PreloadManager::IsLoadingOrQueued()
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ if (IsLoading())
+ return true;
+
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ if (m_ProcessingOperation)
+ return true;
+ return !m_PreloadQueue.empty();
+}
+
+size_t PreloadManager::FindTopPriorityOperation (std::vector<PreloadManagerOperation*>& ops)
+{
+ Assert (!ops.empty ());
+ int index = 0;
+ int highestPriority = ops[0]->GetPriority();
+ for (int i=1;i<ops.size();i++)
+ {
+ if (ops[i]->GetPriority() > highestPriority)
+ {
+ index = i;
+ highestPriority = ops[i]->GetPriority();
+ }
+ }
+
+ return index;
+}
+
+#if !THREADED_LOADING
+void PreloadManager::ProcessPreloadOperation ()
+{
+
+ #if PROFILE_PRELOAD_MANAGER
+ float startTime = GetTimeSinceStartup();
+ #define LOG_PROFILER(x) printf_console(x, m_ProcessingOperation->GetDebugName().c_str(), (GetTimeSinceStartup() - startTime) * 1000.0F); startTime = GetTimeSinceStartup();
+ #else
+ #define LOG_PROFILER(x)
+ #endif
+
+ if (!m_PreloadQueue.empty())
+ {
+ Assert (m_IntegrationOperation == NULL);
+
+ int index = PreloadManager::FindTopPriorityOperation (m_PreloadQueue);
+
+ // Grab item and remove it from queue
+ Assert(m_ProcessingOperation == NULL);
+ m_ProcessingOperation = m_PreloadQueue[index];
+
+ LOG_PROFILER("Begin background load (FAKE): %s [idle time: %f]\n");
+
+ m_PreloadQueue.erase(m_PreloadQueue.begin() + index);
+ m_ProcessingOperation->Perform();
+
+ LOG_PROFILER("Async background load complete (FAKE): %s - %f ms\n");
+
+ // Integrate any work into main thread, wait for completion,
+ // so that the persistentmanager is kept clear until the integration thread is done
+ if (m_ProcessingOperation->HasIntegrateMainThread())
+ {
+ Assert (m_IntegrationOperation == NULL);
+ m_IntegrationOperation = m_ProcessingOperation;
+ }
+
+ m_CallCoroutineCallbackQueue.push_back(m_ProcessingOperation);
+
+ LOG_PROFILER("Completed Integration step: %s - %f ms\n");
+
+ m_ProcessingOperation = NULL;
+ }
+
+ #undef PROFILE_PRELOAD_MANAGER
+}
+
+void PreloadManager::UpdatePreloadingSingleStep (bool stopPreloading)
+{
+ // Do loading
+ ProcessPreloadOperation ();
+
+ // Upload texture data immediately after texture asset was loaded
+ // On some platforms helps to avoid memory peak during level load
+ Texture2D::IntegrateLoadedImmediately();
+
+ // Integrate threaded objects for some time
+ GetPersistentManager().IntegrateThreadedObjects (20.0F);
+
+ // Perform final main thread integration step
+ if (m_IntegrationOperation != NULL && (m_IntegrationOperation->GetAllowSceneActivation() || stopPreloading))
+ {
+ PreloadManagerOperation* operation = m_IntegrationOperation;
+ m_IntegrationOperation = NULL;
+ operation->IntegrateMainThread();
+ }
+
+ // Potentially call out coroutines
+ InvokeCoroutineCallbacks();
+}
+
+#else
+
+void PreloadManager::UpdatePreloadingSingleStep (bool stopPreloading)
+{
+ // Should we start up the preload manager thread? Do it as soon as something needs to be processed
+ if (!m_Thread.IsRunning())
+ {
+ ////@TODO: Also use stopPreloading here...
+ if (!m_PreloadQueue.empty())
+ {
+ AssertIf(m_IntegrationOperation);
+ #if ENABLE_MONO
+ Assert(m_InitDomain == NULL);
+ m_InitDomain = mono_domain_get();
+ #endif
+#if UNITY_WII
+ // On Wii if we set thread priority below normal and don't call Sleep on MainThread, the child thread will never run!
+ // Update : Threaded loading actually works now on Wii, but it requires that allocators should be thread safe, and
+ // it's not the case on Wii, so if there will be plans to make threaded loading work, allocators should be made thread-safe
+ m_Thread.SetPriority(kNormalPriority);
+#else
+ m_Thread.SetPriority(kBelowNormalPriority);
+#endif
+
+ ////@TODO: This is currently a very high value because MonoBehaviours might do funky recursion!
+ // Reduce stacksize sensibly when we stop the madness.
+
+ unsigned stackSize = 512 * 1024;
+ if (UNITY_EDITOR)
+ stackSize = 2 * 1024 * 1024;
+
+#if UNITY_XENON
+ const int kProcessor = 4;
+#else
+ const int kProcessor = 1;
+#endif
+ m_Thread.Run(&PreloadManager::Run, this, stackSize, kProcessor);
+ }
+ }
+
+ // Upload texture data immediately after texture asset was loaded
+ // On some platforms helps to avoid memory peak during level load
+ Texture2D::IntegrateLoadedImmediately();
+
+ // Update substance integration, since we are not in the gameloop here
+ #if ENABLE_SUBSTANCE
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL && !system->AreIntegratingQueuesEmpty())
+ system->Update(stopPreloading);
+ #endif
+
+ // Figure out sensible time for how long we are allowed to integrate assets on the main thread each frame
+ float ms = 4.0F;
+ ThreadPriority p = GetThreadPriority();
+ if (p == kLowPriority)
+ ms = 2.0F;
+ else if (p == kBelowNormalPriority)
+ ms = 4.0F;
+ else if (p == kNormalPriority)
+ ms = 10.0F;
+ else if (p == kHighPriority)
+ ms = 50.0F;
+
+ // Integrate threaded objects for some time
+ GetPersistentManager().IntegrateThreadedObjects(ms / 1000.0F);
+
+ // Perform final main thread integration step
+ if (m_IntegrationOperation != NULL && (m_IntegrationOperation->GetAllowSceneActivation() || stopPreloading))
+ {
+ PreloadManagerOperation* operation = m_IntegrationOperation;
+ m_IntegrationOperation = NULL;
+ operation->IntegrateMainThread();
+
+ // This is needed in order to make sure that m_IntegrationOperation is properly synced when
+ // the signaled thread reads the value
+ UnityMemoryBarrier();
+ m_IntegrationSemaphore.Signal();
+ }
+
+ // Potentially call out coroutines
+ InvokeCoroutineCallbacks();
+}
+#endif
+
+void PreloadManager::InvokeCoroutineCallbacks ()
+{
+ // Grab coroutines that need to be invoked this frame
+ // And invoke them
+ std::vector<PreloadManagerOperation*> callbacks;
+
+ {
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ callbacks.swap(m_CallCoroutineCallbackQueue);
+ }
+
+ for (int i=0;i<callbacks.size();i++)
+ {
+ callbacks[i]->InvokeCoroutine();
+ callbacks[i]->Release();
+ }
+}
+
+
+void PreloadManager::AddToQueue (PreloadManagerOperation* operation)
+{
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ operation->Retain();
+ m_PreloadQueue.push_back(operation);
+}
+
+void PreloadManager::LockPreloading ()
+{
+ #if THREADED_LOADING
+ PROFILER_AUTO(gIntegrateAssetsInBackground, NULL)
+ while (!m_LoadingMutex.TryLock())
+ {
+ UpdatePreloadingSingleStep(false);
+ Thread::Sleep(0.004F);
+ }
+ #endif
+}
+
+void PreloadManager::UnlockPreloading ()
+{
+#if THREADED_LOADING
+ m_LoadingMutex.Unlock();
+#endif
+}
+
+void PreloadManager::WaitForAllAsyncOperationsToComplete()
+{
+ PROFILER_AUTO(gAsyncOperationComplete, NULL)
+
+ while(IsLoadingOrQueued())
+ {
+ UpdatePreloadingSingleStep(false);
+
+ #if THREADED_LOADING
+ if (!GetPersistentManager ().HasThreadedObjectsToIntegrate ())
+ LevelLoadingLoop();
+ #endif
+ }
+}
+
+void PreloadManager::UpdatePreloading()
+{
+ // Check if there are any operation that must be completed right away
+ bool mustCompleteNow = false;
+ {
+ THREADED_LOADING_MUTEX_AUTOLOCK(m_QueueMutex);
+ for (int i=0;i<m_PreloadQueue.size();i++)
+ mustCompleteNow |= m_PreloadQueue[i]->MustCompleteNextFrame();
+ if (m_ProcessingOperation != NULL)
+ mustCompleteNow |= m_ProcessingOperation->MustCompleteNextFrame();
+ }
+
+ if (mustCompleteNow)
+ {
+ WaitForAllAsyncOperationsToComplete ();
+#if UNITY_ANDROID
+ StopActivityIndicator();
+#endif
+ }
+ else
+ {
+ PROFILER_AUTO(gIntegrateAssetsInBackground, NULL)
+ UpdatePreloadingSingleStep (false);
+ }
+}
+
+
+float PreloadManagerOperation::GetProgress ()
+{
+ return m_Progress;
+}
+
+bool PreloadManagerOperation::IsDone ()
+{
+ return m_Complete;
+}
+
+void PreloadLevelOperation::Perform ()
+{
+ // Should indicate threaded loading
+ //GetGlobalAllocators().ActivateLoadingAllocator();
+
+ PersistentManager& pm = GetPersistentManager();
+
+#if UNITY_WII
+ wii::StartLoadingScreen();
+#endif
+
+#if ENABLE_SUBSTANCE
+ // Notify for preload operation
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ system->BeginPreloading();
+#endif
+
+ // When loading a level, grab the PreloadData and extract PPtrs of all assets not contained in the primary assets file
+ if (!m_LevelAssetDataPath.empty())
+ {
+ int preloadInstanceID = pm.GetInstanceIDFromPathAndFileID(m_LevelAssetDataPath, 1);
+ // @TODO: This should be moved to main thread or the preload data should always be killed immediately
+ Object* obj = Object::IDToPointerThreadSafe (preloadInstanceID);
+ if (obj == NULL)
+ obj = pm.ReadObjectThreaded(preloadInstanceID);
+
+ PreloadData* preload = dynamic_pptr_cast<PreloadData*> (obj);
+ if (preload)
+ {
+ int size = preload->m_Assets.size();
+ m_PreloadAssets.resize_uninitialized(size);
+ if (size > 0)
+ {
+ SInt32* target = &m_PreloadAssets[0];
+ PPtr<Object>* source = &preload->m_Assets[0];
+ for (int i=0;i<size;i++)
+ target[i] = source[i].GetInstanceID();
+ }
+ }
+ else
+ {
+ // In The Editor we load scenes that contain no preload data
+ // In the web player Unity 3.3 had a bug where preloaddata was not included in the build.
+ if (!WEBPLUG && !UNITY_EDITOR && m_LoadMode != kLoadAssetBundle)
+ {
+ AssertString("PreloadData is missing. It should always be there.");
+ }
+ }
+ }
+
+// printf_console("Number of Preload assets %d\n", m_PreloadAssets.size());
+// for (int i=0;i<m_PreloadAssets.size();i++)
+// {
+// printf_console("Will load Preload assets %d %s\n", m_PreloadAssets[i], GetPersistentManager().GetPathName(m_PreloadAssets[i]).c_str());
+// }
+
+ // Figure out exactly which objects in the file we need to load
+ // We want to know what we're loading before preloading to indicate the progress properly
+ vector<LocalIdentifierInFileType> fileIDs;
+ vector<int> managerIndices;
+ if (!m_LevelPath.empty())
+ GetFileIDsForLoadingScene(m_LevelPath, m_LoadMode, fileIDs, managerIndices);
+
+ LoadProgress loadProgress (fileIDs.size () + m_PreloadAssets.size (), 0.9f, &m_Progress);
+ // Preload bundle assets & preload external level assets
+ GetPersistentManager().LoadObjectsThreaded(m_PreloadAssets.begin(), m_PreloadAssets.size(), &loadProgress);
+
+ // Load Level
+ if (!m_LevelPath.empty())
+ {
+ /// Load all assets
+ pm.LoadFileCompletelyThreaded(m_LevelAssetDataPath, NULL, NULL, -1, false, &loadProgress);
+
+ // Wait for all assets to be integrated using timeslicing from the main thread
+ pm.AllowIntegrationWithTimeoutAndWait ();
+
+
+ ///@TODO: these two are not needed anymore...
+ std::vector<SInt32> instanceIDs;
+ instanceIDs.resize(fileIDs.size());
+
+ // Lock up to extracting all objects into the AwakeFromLoadQueue
+ // This prevents the main thread from loading and integrating random objects during the level load
+ pm.Lock();
+
+ Assert(!pm.HasThreadedObjectsToIntegrate());
+
+ // Load level
+ pm.LoadFileCompletelyThreaded(m_LevelPath, &fileIDs[0], &instanceIDs[0], fileIDs.size(), true, &loadProgress);
+
+ pm.PrepareAllThreadedObjectsStep1 (m_AwakeFromLoadQueue);
+
+ // Unlock - See above
+ pm.Unlock();
+
+
+ #if ENABLE_SUBSTANCE
+ // Wait Substance finish to integrate
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ system->WaitFinished(&loadProgress);
+ #endif
+
+ m_Progress = 0.9F;
+ }
+ else
+ {
+ // Wait for all assets to be integrated using timeslicing from the main thread
+ pm.AllowIntegrationWithTimeoutAndWait ();
+
+ #if ENABLE_SUBSTANCE
+ // Wait Substance finish to integrate
+ SubstanceSystem* system = GetSubstanceSystemPtr ();
+ if (system != NULL)
+ system->WaitFinished(&loadProgress);
+ #endif
+
+ m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ m_Complete = true;
+ }
+#if UNITY_WII
+ wii::EndLoadingScreen();
+#endif
+}
+
+void PreloadLevelOperation::PreloadBundleSync (AssetBundle& bundle, const std::string& name)
+{
+ // Calculate preload index & size into preload table of asset bundle
+ int index = 0, size = 0;
+ if (name.empty())
+ {
+ index = bundle.m_MainAsset.preloadIndex;
+ size = bundle.m_MainAsset.preloadSize;
+ if (Object::IDToPointer(bundle.m_MainAsset.asset.GetInstanceID()))
+ return;
+ }
+ else
+ {
+ AssetBundle::range range = bundle.GetPathRange(name);
+ if (range.first != range.second)
+ {
+ index = range.first->second.preloadIndex;
+ size = range.first->second.preloadSize;
+ if (Object::IDToPointer(range.first->second.asset.GetInstanceID()))
+ return;
+ }
+ }
+
+ if (size == 0)
+ return;
+
+ PPtr<Object>* source = &bundle.m_PreloadTable[index];
+ for (int i=0;i<size;i++)
+ {
+ Object* forceLoad = source[i];
+ UNUSED(forceLoad);
+ }
+}
+
+bool PreloadLevelOperation::GetAllowSceneActivation ()
+{
+ return m_AllowSceneActivation;
+}
+
+void PreloadLevelOperation::SetAllowSceneActivation (bool allow)
+{
+ m_AllowSceneActivation = allow;
+}
+
+void PreloadLevelOperation::CleanupMemory()
+{
+ m_PreloadAssets.clear();
+ m_AwakeFromLoadQueue.Clear();
+}
+
+
+bool PreloadLevelOperation::HasIntegrateMainThread ()
+{
+ return !m_LevelPath.empty();
+}
+
+void PreloadLevelOperation::IntegrateMainThread ()
+{
+ Texture2D::IntegrateLoadedImmediately();
+
+ if (!m_LevelPath.empty())
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+ if (m_LoadMode == kLoadAdditiveLevel)
+ {
+ PostLoadLevelAdditive (m_LevelPath, m_AwakeFromLoadQueue);
+ }
+ else if (m_LoadMode == kLoadEditorAdditiveLevel)
+ {
+ PostEditorLoadLevelAdditive (m_LevelPath, m_AwakeFromLoadQueue);
+ }
+ else if (m_LoadMode == kLoadLevel)
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+
+ PlayerLoadLevelFromThread(m_LoadLevelIndex, m_LevelPath, m_AwakeFromLoadQueue);
+ }
+ else if (m_LoadMode == kLoadMainData)
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+ CompletePreloadMainData (m_AwakeFromLoadQueue);
+ }
+ #if UNITY_EDITOR
+ else if (m_LoadMode == kOpenSceneEditor || m_LoadMode == kOpenSceneEditorPlaymode)
+ {
+ PROFILER_AUTO(gPreloadLevel, NULL)
+ CompletePreloadManagerLoadLevelEditor (m_LevelPath, m_AwakeFromLoadQueue, m_LoadMode);
+ }
+ #endif
+ else
+ {
+ AssertString("Cant reach");
+ }
+
+ VerifyNothingIsPersistentInLoadedScene(m_LevelPath);
+ }
+ else
+ {
+ PROFILER_AUTO(gPreloadBundle, NULL)
+ GetPersistentManager().IntegrateAllThreadedObjects();
+ }
+
+ CleanupMemory();
+
+ m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ m_Complete = true;
+
+ if (RunningReproduction())
+ {
+ LogString(Format("Completed loading level: '%s' (time: %f, frame: %d)", m_LevelPath.c_str(), GetTimeManager().GetCurTime(), GetTimeManager().GetFrameCount()));
+ }
+}
+
+PreloadLevelOperation* PreloadLevelOperation::LoadLevel (const std::string& levelPath, const std::string& levelAssetPath, int levelIndex, LoadingMode mode, bool mustCompleteNextFrame)
+{
+ if (!mustCompleteNextFrame && !GetBuildSettings().hasPROVersion)
+ {
+ ErrorString("Asynchronous Background loading is only supported in Unity Pro.\nPlease use Application.LoadLevel or Application.LoadLevelAdditive instead.");
+ mustCompleteNextFrame = true;
+ }
+
+ PreloadLevelOperation* operation = new PreloadLevelOperation ();
+ operation->m_LevelPath = levelPath;
+ operation->m_LevelAssetDataPath = levelAssetPath;
+ operation->m_LoadMode = mode;
+ operation->m_LoadLevelIndex = levelIndex;
+ operation->m_MustCompleteNextFrame = mustCompleteNextFrame;
+ #if ENABLE_PROFILER || PROFILE_PRELOAD_MANAGER
+ operation->m_DebugName = "Loading " + levelPath;
+ #endif
+ GetPreloadManager().AddToQueue(operation);
+
+ return operation;
+}
+
+UnloadUnusedAssetsOperation* UnloadUnusedAssetsOperation::UnloadUnusedAssets ()
+{
+ UnloadUnusedAssetsOperation* operation = new UnloadUnusedAssetsOperation ();
+ GetPreloadManager().AddToQueue(operation);
+
+ return operation;
+}
+
+void UnloadUnusedAssetsOperation::IntegrateMainThread ()
+{
+ GarbageCollectSharedAssets(true);
+ m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ m_Complete = true;
+}
+
+PreloadLevelOperation* PreloadLevelOperation::CreateDummy ()
+{
+ PreloadLevelOperation* operation = new PreloadLevelOperation ();
+ operation->m_Progress = 1.0F;
+ UnityMemoryBarrier();
+ operation->m_Complete = true;
+ return operation;
+}
+
+/// @TODO: one string can identify multiple different resources.
+/// We must somehow preload all or make preload tables be shared by name
+PreloadLevelOperation* PreloadLevelOperation::LoadAssetBundle (AssetBundle& bundle, const std::string& name)
+{
+ if (!GetBuildSettings().hasPROVersion)
+ {
+ ErrorString("Asynchronous Background loading is only supported in Unity Pro.\nPlease use AssetBundle.Load instead");
+ return CreateDummy ();
+ }
+
+ PreloadLevelOperation* operation = NULL;
+
+ // Calculate preload index & size into preload table of asset bundle
+ int index = 0, size = 0;
+ if (name.empty())
+ {
+ index = bundle.m_MainAsset.preloadIndex;
+ size = bundle.m_MainAsset.preloadSize;
+ if (Object::IDToPointer(bundle.m_MainAsset.asset.GetInstanceID()))
+ return CreateDummy ();
+ }
+ else
+ {
+ AssetBundle::range range = bundle.GetPathRange(name);
+ if (range.first != range.second)
+ {
+ index = range.first->second.preloadIndex;
+ size = range.first->second.preloadSize;
+ if (Object::IDToPointer(range.first->second.asset.GetInstanceID()))
+ return CreateDummy ();
+ }
+ }
+
+ if (size == 0)
+ return CreateDummy ();
+
+ operation = new PreloadLevelOperation ();
+ #if ENABLE_PROFILER || PROFILE_PRELOAD_MANAGER
+ operation->m_DebugName = "Loading asset bundle asset: " + name;
+ #endif
+
+ operation->m_PreloadAssets.resize_uninitialized(size);
+ operation->m_LoadMode = kLoadAssetBundle;
+
+ SInt32* target = &operation->m_PreloadAssets[0];
+ PPtr<Object>* source = &bundle.m_PreloadTable[index];
+ for (int i=0;i<size;i++)
+ target[i] = source[i].GetInstanceID();
+
+ GetPreloadManager().AddToQueue(operation);
+
+ return operation;
+}
+
+PreloadLevelOperation::PreloadLevelOperation ()
+ : PreloadManagerOperation (),
+ m_AwakeFromLoadQueue(kMemSerialization)
+{
+ m_LoadLevelIndex = -1;
+ m_MustCompleteNextFrame = false;
+ m_AllowSceneActivation = true;
+}
+
+
+PreloadLevelOperation::~PreloadLevelOperation ()
+{
+}
+
+static void GetFileIDsForLoadingScene (const string& pathName, PreloadLevelOperation::LoadingMode operation, vector<LocalIdentifierInFileType>& fileIDs, vector<int>& managerIndices)
+{
+ GetPersistentManager ().Lock();
+ SerializedFile* stream = GetPersistentManager ().GetSerializedFileInternal(pathName);
+ if (stream == NULL)
+ {
+ GetPersistentManager ().Unlock();
+ return;
+ }
+
+ vector<LocalIdentifierInFileType> sourceFileIDs;
+ stream->GetAllFileIDs(&sourceFileIDs);
+ fileIDs.reserve(sourceFileIDs.size());
+
+ #if UNITY_EDITOR
+ int editorExtensionImplClassID = Object::StringToClassID("EditorExtensionImpl");
+ #endif
+
+
+ // GameManager no longer references EditorExtensionImpl. We need to not load EditorExtensionImpl related to game managers.
+ // But we do need to load EditorExtensionImpl of game objects & components in order to grab deprecated data describing prefabs from it.
+ // If we dont they might in turn load the manager, which we need very tight control over when we want them to be loaded.
+ // Sometime in the past EditorExtensionImpl were referenced by GameManager.
+ // So we skip over loading any editor extension impl objects before any scene objects are being loaded.
+ // Loading the editorextension impl of a manager is dangerous because it will load it by dereferencing it's m_Object reference.
+ // Don't load any editor extension impl for any managers
+ // All editor extension impl's in scenes are always serialized directly after their related object.
+ int baseSceneDataIndex = 0;
+ for (int i=0;i<sourceFileIDs.size ();i++)
+ {
+ LocalIdentifierInFileType fileID = sourceFileIDs[i];
+ int classID = stream->GetClassID (fileID);
+
+ if (Object::IsDerivedFromClassID (classID, ClassID (GameManager)))
+ baseSceneDataIndex = i + 1;
+ #if UNITY_EDITOR
+ else if (classID == editorExtensionImplClassID)
+ continue;
+ #endif
+ else
+ break;
+ }
+
+ for (int i=0;i<sourceFileIDs.size ();i++)
+ {
+ LocalIdentifierInFileType fileID = sourceFileIDs[i];
+ int classID = stream->GetClassID (fileID);
+
+ if (Object::IsDerivedFromClassID (classID, ClassID (GlobalGameManager)))
+ continue;
+
+ if (Object::IsDerivedFromClassID (classID, ClassID (LevelGameManager)))
+ {
+ // Additive loaded levels need lightmap settings from the scene file. It will be merged into the active scene.
+ bool loadAdditive = operation == PreloadLevelOperation::kLoadEditorAdditiveLevel || operation == PreloadLevelOperation::kLoadAdditiveLevel;
+ if (loadAdditive && classID != ClassID(LightmapSettings))
+ continue;
+
+ managerIndices.push_back(fileIDs.size());
+ }
+
+ #if UNITY_EDITOR
+ if (classID == editorExtensionImplClassID && i < baseSceneDataIndex)
+ continue;
+ #endif
+
+ fileIDs.push_back(fileID);
+ }
+
+ GetPersistentManager ().Unlock();
+}
+
+
+#undef THREADED_LOADING_MUTEX_AUTOLOCK // Mutex::AutoLock lock (x) / {}
+
+void UnloadUnusedAssetsImmediate (bool includeMonoReferencesAsRoots)
+{
+ GetPreloadManager().LockPreloading();
+ GarbageCollectSharedAssets(includeMonoReferencesAsRoots);
+ GetPreloadManager().UnlockPreloading();
+}
diff --git a/Runtime/Misc/PreloadManager.h b/Runtime/Misc/PreloadManager.h
new file mode 100644
index 0000000..e8aa38f
--- /dev/null
+++ b/Runtime/Misc/PreloadManager.h
@@ -0,0 +1,209 @@
+#pragma once
+
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/BaseClasses/NamedObject.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/Threads/Mutex.h"
+#include "ResourceManager.h"
+#include "Runtime/Threads/Semaphore.h"
+#include "AsyncOperation.h"
+#include "Runtime/Serialize/AwakeFromLoadQueue.h"
+
+struct MonoDomain;
+class PreloadManagerOperation;
+class AssetBundle;
+
+
+class PreloadData : public NamedObject
+{
+ public:
+
+ DECLARE_OBJECT_SERIALIZE (PreloadData)
+ REGISTER_DERIVED_CLASS (PreloadData, NamedObject)
+
+ PreloadData (MemLabelId label, ObjectCreationMode mode) : Super(label, mode) { }
+ // ~PreloadData (); declared-by-macro
+
+ virtual bool ShouldIgnoreInGarbageDependencyTracking () { return true; }
+
+ std::vector<PPtr<Object> > m_Assets;
+};
+
+class PreloadManager
+{
+ #if THREADED_LOADING
+ Thread m_Thread;
+ Semaphore m_IntegrationSemaphore;
+ Mutex m_QueueMutex;
+ Mutex m_LoadingMutex;
+ #endif
+
+ std::vector<PreloadManagerOperation*> m_PreloadQueue;
+ std::vector<PreloadManagerOperation*> m_CallCoroutineCallbackQueue;
+ PreloadManagerOperation* m_ProcessingOperation;
+
+ PreloadManagerOperation* volatile m_IntegrationOperation;
+ #if ENABLE_MONO
+ MonoDomain* m_InitDomain;
+ #endif
+
+ static void* Run (void* managerPtr);
+ void Run ();
+ void ProcessPreloadOperation ();
+ void InvokeCoroutineCallbacks ();
+
+ static size_t FindTopPriorityOperation (std::vector<PreloadManagerOperation*>& ops);
+
+ public:
+
+ PreloadManager();
+ ~PreloadManager();
+
+ bool IsLoading();
+ bool IsLoadingOrQueued();
+ bool IsLoadingOrQueuedLevel();
+
+ void AddToQueue (PreloadManagerOperation* operation);
+
+ void LockPreloading ();
+ void UnlockPreloading ();
+
+ void UpdatePreloading ();
+
+ void SetThreadPriority(ThreadPriority p);
+ ThreadPriority GetThreadPriority ();
+
+#if UNITY_EDITOR
+ void RemoveStopPlaymodeOperations ();
+#endif
+
+ void WaitForAllAsyncOperationsToComplete();
+
+ void Stop();
+
+private:
+
+ void UpdatePreloadingSingleStep (bool stopPreloadManager);
+};
+
+PreloadManager& GetPreloadManager();
+void ReleasePreloadManager();
+void StopPreloadManager();
+
+class PreloadManagerOperation : public AsyncOperation
+{
+ protected:
+ int m_Priority;
+ bool m_Complete;
+ float m_Progress;
+
+ PreloadManagerOperation () { m_Priority = 0; m_Complete = false; m_Progress = 0.0F; }
+
+ public:
+
+ virtual int GetPriority () { return m_Priority; }
+ virtual void SetPriority (int priority) { m_Priority = priority; }
+
+ virtual float GetProgress ();
+
+ /// Returns true when the operation has completed.
+ /// Subclasses must set m_Complete to true from either Perform or IntegrateMainThread.
+ virtual bool IsDone ();
+
+ /// Performs the actual work on the preload manager thread
+ virtual void Perform () = 0;
+
+ /// When complete gives the operation a chance to integrate some work on the main thread,
+ /// without any other preload operation running at the same time
+ virtual void IntegrateMainThread () { }
+
+ /// Override this and return true if you want IntegrateMainThread to be called.
+ virtual bool HasIntegrateMainThread () { return false; }
+
+ /// Do we have to complete the preload operation by the next frame?
+ virtual bool MustCompleteNextFrame () { return false; }
+
+#if UNITY_EDITOR
+ virtual bool IsPlaymodeLoadLevel () { return false; }
+#endif
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () = 0;
+#endif
+};
+
+///@TODO: We should seperate SceneLoading from assetbundle loading code here. Make it into two AsyncOperation classes.
+
+
+class PreloadLevelOperation : public PreloadManagerOperation
+{
+ public:
+
+ static void PreloadBundleSync (AssetBundle& bundle, const std::string& name);
+
+ static PreloadLevelOperation* LoadAssetBundle (AssetBundle& bundle, const std::string& name);
+
+ enum LoadingMode { kLoadLevel = 0, kLoadAdditiveLevel = 1, kLoadMainData = 2, kLoadAssetBundle = 3, kOpenSceneEditor = 3, kOpenSceneEditorPlaymode = 4, kLoadEditorAdditiveLevel = 5 };
+ static PreloadLevelOperation* LoadLevel (const std::string& levelPath, const std::string& levelAssetPath, int levelIndex, LoadingMode mode, bool mustCompleteNextFrame);
+
+
+ virtual void IntegrateMainThread ();
+ virtual bool HasIntegrateMainThread ();
+
+ virtual bool GetAllowSceneActivation ();
+ virtual void SetAllowSceneActivation (bool allow);
+
+ virtual void Perform ();
+
+ virtual bool MustCompleteNextFrame () { return m_MustCompleteNextFrame; }
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () { return m_DebugName; }
+#endif
+
+#if UNITY_EDITOR
+ virtual bool IsPlaymodeLoadLevel () { return m_LoadMode == kLoadLevel || m_LoadMode == kLoadAdditiveLevel; }
+#endif
+
+private:
+ bool m_AllowSceneActivation;
+ dynamic_array<SInt32> m_PreloadAssets;
+ int m_LoadLevelIndex;
+ std::string m_LevelPath;
+ std::string m_LevelAssetDataPath;
+ LoadingMode m_LoadMode;
+ bool m_MustCompleteNextFrame;
+
+ AwakeFromLoadQueue m_AwakeFromLoadQueue;
+
+#if ENABLE_PROFILER
+ std::string m_DebugName;
+#endif
+
+ void CleanupMemory();
+
+ PreloadLevelOperation ();
+ virtual ~PreloadLevelOperation ();
+
+ static PreloadLevelOperation* CreateDummy ();
+};
+
+class UnloadUnusedAssetsOperation : public PreloadManagerOperation
+{
+ UnloadUnusedAssetsOperation () : PreloadManagerOperation () { }
+
+ public:
+
+ static UnloadUnusedAssetsOperation* UnloadUnusedAssets ();
+
+ virtual void IntegrateMainThread ();
+ virtual bool HasIntegrateMainThread () {return true;}
+
+ virtual void Perform () {}
+
+#if ENABLE_PROFILER
+ virtual std::string GetDebugName () { return "Garbage Collect Assets"; }
+#endif
+};
+
+void UnloadUnusedAssetsImmediate (bool managedObjects);
diff --git a/Runtime/Misc/QualitySettings.cpp b/Runtime/Misc/QualitySettings.cpp
new file mode 100644
index 0000000..11916f6
--- /dev/null
+++ b/Runtime/Misc/QualitySettings.cpp
@@ -0,0 +1,644 @@
+#include "UnityPrefix.h"
+#include "QualitySettings.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Utilities/PlayerPrefs.h"
+#include "Runtime/Graphics/Texture.h"
+#include "Runtime/Camera/LightManager.h"
+#include "Runtime/Camera/LODGroupManager.h"
+#include "Runtime/Camera/Shadows.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Utilities/Utility.h"
+#if UNITY_EDITOR
+#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h"
+#include "Editor/Src/LightmapVisualization.h"
+#include "Editor/Platform/Interface/EditorWindows.h"
+#endif
+
+enum { kDefaultQualitySettingCount = 6 };
+
+QualitySettings::QualitySettings(MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ #if UNITY_EDITOR
+ m_PreviousAA = -1;
+ m_PreviousVSync = -1;
+ #endif
+ m_StrippedMaximumLODLevel = 0;
+}
+
+QualitySettings::~QualitySettings()
+{
+}
+
+// defaults to Fastest to Fantastic quality settings
+QualitySettings::QualitySetting::QualitySetting()
+: name("Fastest")
+, pixelLightCount(0)
+, shadows(QualitySettings::kShadowsDisable)
+, shadowResolution(0)
+, shadowProjection(kShadowProjStableFit)
+, shadowCascades(1)
+, shadowDistance(15.0f)
+, blendWeights(1)
+, textureQuality(1)
+, anisotropicTextures(0)
+, antiAliasing(0)
+, vSyncCount(0)
+, softParticles(false)
+, softVegetation(false)
+, lodBias(0.3F)
+, maximumLODLevel(0)
+, particleRaycastBudget(4)
+{
+}
+
+IMPLEMENT_CLASS_HAS_INIT (QualitySettings)
+IMPLEMENT_OBJECT_SERIALIZE (QualitySettings)
+GET_MANAGER (QualitySettings)
+GET_MANAGER_PTR (QualitySettings)
+
+static void InitializeDefaultQualitySettings(QualitySettings::QualitySetting* m_Quality)
+{
+ QualitySettings::QualitySetting qualitySetting;
+
+ // Fastest to Fantastic quality settings
+ m_Quality[0] = m_Quality[1] = m_Quality[2] = m_Quality[3] = m_Quality[4] = m_Quality[5] = qualitySetting;
+
+ m_Quality[1].name = "Fast";
+ m_Quality[1].shadowDistance = 20.0f;
+ m_Quality[1].blendWeights = 2;
+ m_Quality[1].textureQuality = 0;
+ m_Quality[1].lodBias = 0.4F;
+ m_Quality[1].particleRaycastBudget = 16;
+
+ m_Quality[2].name = "Simple";
+ m_Quality[2].pixelLightCount = 1;
+ m_Quality[2].shadows = QualitySettings::kShadowsHardOnly;
+ m_Quality[2].shadowDistance = 20.0f;
+ m_Quality[2].blendWeights = 2;
+ m_Quality[2].textureQuality = 0;
+ m_Quality[2].anisotropicTextures = 1;
+ m_Quality[2].lodBias = 0.70F;
+ m_Quality[2].particleRaycastBudget = 64;
+
+ m_Quality[3].name = "Good";
+ m_Quality[3].pixelLightCount = 2;
+ m_Quality[3].shadows = QualitySettings::kShadowsAll;
+ m_Quality[3].shadowResolution = 1;
+ m_Quality[3].shadowCascades = 2;
+ m_Quality[3].shadowDistance = 40.0f;
+ m_Quality[3].blendWeights = 2;
+ m_Quality[3].textureQuality = 0;
+ m_Quality[3].anisotropicTextures = 1;
+ m_Quality[3].vSyncCount = 1;
+ m_Quality[3].softVegetation = true;
+ m_Quality[3].lodBias = 1.0F;
+ m_Quality[3].particleRaycastBudget = 256;
+
+ m_Quality[4].name = "Beautiful";
+ m_Quality[4].pixelLightCount = 3;
+ m_Quality[4].shadows = QualitySettings::kShadowsAll;
+ m_Quality[4].shadowResolution = 2;
+ m_Quality[4].shadowCascades = 2;
+ m_Quality[4].shadowDistance = 70.0f;
+ m_Quality[4].blendWeights = 4;
+ m_Quality[4].textureQuality = 0;
+ m_Quality[4].anisotropicTextures = 2;
+ m_Quality[4].antiAliasing = 2;
+ m_Quality[4].vSyncCount = 1;
+ m_Quality[4].softParticles = true;
+ m_Quality[4].softVegetation = true;
+ m_Quality[4].lodBias = 1.5F;
+ m_Quality[4].particleRaycastBudget = 1024;
+
+ m_Quality[5].name = "Fantastic";
+ m_Quality[5].pixelLightCount = 4;
+ m_Quality[5].shadows = QualitySettings::kShadowsAll;
+ m_Quality[5].shadowResolution = 2;
+ m_Quality[5].shadowCascades = 4;
+ m_Quality[5].shadowDistance = 150.0f;
+ m_Quality[5].blendWeights = 4;
+ m_Quality[5].textureQuality = 0;
+ m_Quality[5].anisotropicTextures = 2;
+ m_Quality[5].antiAliasing = 2;
+ m_Quality[5].vSyncCount = 1;
+ m_Quality[5].softParticles = true;
+ m_Quality[5].softVegetation = true;
+ m_Quality[5].lodBias = 2.0F;
+ m_Quality[5].particleRaycastBudget = 4096;
+}
+
+#if UNITY_EDITOR
+static void InitializePerPlatformDefaults(QualitySettings::PerPlatformDefaultQuality* platformSettings)
+{
+ platformSettings->clear ();
+ for (int i=1; i<kBuildPlayerTypeCount; i++)
+ {
+ string target = GetBuildTargetGroupName((BuildTargetPlatform)i, false);
+
+ if (target == "")
+ continue;
+
+ QualitySettings::PerPlatformDefaultQuality::iterator found = platformSettings->find(target);
+ if (found == platformSettings->end())
+ {
+ if (target == "iPhone" || target == "Android" || target == "BlackBerry" || target == "Tizen")
+ platformSettings->insert (pair<string,int>(target, 2));
+ else
+ platformSettings->insert (pair<string,int>(target, 3));
+ }
+ }
+}
+#endif
+
+void QualitySettings::Reset()
+{
+ SET_ALLOC_OWNER(this);
+ Super::Reset();
+
+ QualitySetting tempQuality[kDefaultQualitySettingCount];
+ InitializeDefaultQualitySettings(tempQuality);
+#if UNITY_EDITOR
+ InitializePerPlatformDefaults (&m_PerPlatformDefaultQuality);
+#endif
+ m_QualitySettings.assign(tempQuality, tempQuality + kDefaultQualitySettingCount);
+
+ m_CurrentQuality = 3;
+}
+
+void QualitySettings::CheckConsistency ()
+{
+ // Ensure that there is always at least one quality setting.
+ if (m_QualitySettings.empty())
+ {
+ QualitySetting tempQuality[kDefaultQualitySettingCount];
+ InitializeDefaultQualitySettings(tempQuality);
+#if UNITY_EDITOR
+ InitializePerPlatformDefaults (&m_PerPlatformDefaultQuality);
+#endif
+ m_QualitySettings.push_back(tempQuality[3]);
+ }
+
+ for( int i = 0; i < m_QualitySettings.size(); ++i )
+ {
+ QualitySetting &q (m_QualitySettings[i]);
+ q.pixelLightCount = std::max (q.pixelLightCount, 0);
+ q.shadows = clamp<int>( q.shadows, 0, kShadowQualityCount-1 );
+ q.shadowResolution = clamp<int>( q.shadowResolution, 0, kShadowResolutionCount-1 );
+ q.shadowProjection = clamp<int>( q.shadowProjection, 0, kShadowProjectionCount-1 );
+ q.shadowCascades = clamp<int>( q.shadowCascades, 1, 4 );
+ if (q.shadowCascades == 3)
+ q.shadowCascades = 2;
+ if (q.antiAliasing < 2)
+ q.antiAliasing = 0;
+ else if (q.antiAliasing < 4)
+ q.antiAliasing = 2;
+ else if (q.antiAliasing < 8)
+ q.antiAliasing = 4;
+ else
+ q.antiAliasing = 8;
+ q.shadowDistance = std::max ( q.shadowDistance, 0.0f );
+ q.blendWeights = std::min (std::max (q.blendWeights, 1), 4);
+ if( q.blendWeights == 3 )
+ q.blendWeights = 2;
+
+ // in editor inspector we allow set [0..3], but during play we allow any level you want ;-)
+ // set 10 as max in that case as it is lowest mip for 1024 texture - seems enough 8)
+ {
+ #if UNITY_EDITOR
+ if(!IsWorldPlaying())
+ q.textureQuality = std::min (std::max (q.textureQuality, 0), 3);
+ else
+ #endif
+ q.textureQuality = std::min (std::max (q.textureQuality, 0), 10);
+ }
+
+ q.anisotropicTextures = std::min (std::max (q.anisotropicTextures, 0), 3);
+ q.vSyncCount = std::min (std::max (q.vSyncCount, 0), 2);
+ q.lodBias = max(0.0F, q.lodBias);
+ q.maximumLODLevel = clamp(q.maximumLODLevel, 0, 7);
+ }
+
+ m_CurrentQuality = clamp<int>( m_CurrentQuality, 0, m_QualitySettings.size()-1 );
+}
+
+void QualitySettings::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ #if !UNITY_EDITOR
+ // player
+ // careful to not change current quality setting if player prefs entry does not exist
+ m_CurrentQuality = clamp<int>( PlayerPrefs::GetInt("UnityGraphicsQuality", m_CurrentQuality), 0, m_QualitySettings.size()-1 );
+ ApplySettings ();
+ #else
+ // editor
+ PlayerPrefs::SetInt("UnityGraphicsQuality", m_CurrentQuality);
+ ApplySettings (-1, (awakeMode & kDidLoadFromDisk)==0);
+ #endif
+
+}
+
+std::vector<std::string> QualitySettings::GetQualitySettingsNames ()
+{
+ std::vector<std::string> settingNames;
+ for( int i = 0; i < m_QualitySettings.size(); ++i )
+ {
+ settingNames.push_back (m_QualitySettings[i].name);
+ }
+ return settingNames;
+}
+
+void QualitySettings::SetCurrentIndex (int setting, bool applyExpensiveChanges)
+{
+ setting = clamp<int>( setting, 0, m_QualitySettings.size()-1 );
+
+ SetDirty ();
+ int oldIndex = m_CurrentQuality;
+ m_CurrentQuality = setting;
+ PlayerPrefs::SetInt("UnityGraphicsQuality", m_CurrentQuality);
+
+ ApplySettings( oldIndex, applyExpensiveChanges );
+}
+
+bool QualitySettings::SetCurrent (string setting, bool applyExpensiveChanges)
+{
+ for( int i = 0; i < m_QualitySettings.size(); ++i )
+ {
+ if (m_QualitySettings[i].name == setting)
+ {
+ SetCurrentIndex (i, applyExpensiveChanges);
+ return true;
+ }
+ }
+ return false;
+}
+
+void QualitySettings::ApplySettings( int previousIndex, bool applyExpensiveChanges )
+{
+ const QualitySetting &q = GetCurrent();
+
+ Texture::SetAnisoLimit (q.anisotropicTextures);
+ Texture::SetMasterTextureLimit (q.textureQuality);
+ if (GetLODGroupManagerPtr())
+ GetLODGroupManager().SetLODBias (q.lodBias);
+
+ // When playing we have to clamp to the stripped maximum level
+ int lodLevel = q.maximumLODLevel;
+ if (IsWorldPlaying())
+ lodLevel = max(m_StrippedMaximumLODLevel, lodLevel);
+ if (GetLODGroupManagerPtr())
+ GetLODGroupManager().SetMaximumLODLevel (lodLevel);
+
+ bool hasChanged = false;
+ if (applyExpensiveChanges)
+ {
+ #if UNITY_EDITOR
+ if( m_PreviousAA != q.antiAliasing || m_PreviousVSync != q.vSyncCount )
+ hasChanged = true;
+ #else
+ const QualitySetting& old = GetQualityForIndex(previousIndex);
+ if( old.antiAliasing != q.antiAliasing || old.vSyncCount != q.vSyncCount )
+ hasChanged = true;
+ #endif
+ }
+
+ if (hasChanged)
+ {
+ ApplyExpensiveSettings();
+ }
+}
+
+void QualitySettings::ApplyExpensiveSettings()
+{
+#if UNITY_EDITOR
+ GUIView::RecreateAllOnAAChange();
+ m_PreviousAA = GetCurrent().antiAliasing;
+ m_PreviousVSync = GetCurrent().vSyncCount;
+#else
+ // Make sure to use "as requested" values for everything
+ // Otherwise we might lose pending changes (case 559015)
+ ScreenManager& screen = GetScreenManager();
+ screen.RequestResolution(
+ screen.GetWidthAsRequested(), screen.GetHeightAsRequested(),
+ screen.GetFullScreenAsRequested(), screen.GetRefreshRateAsRequested());
+#endif
+}
+
+template<class TransferFunc>
+void QualitySettings::QualitySetting::Transfer (TransferFunc& transfer)
+{
+ transfer.SetVersion(2);
+ TRANSFER_SIMPLE (name);
+ TRANSFER_SIMPLE (pixelLightCount);
+ TRANSFER_SIMPLE (shadows);
+ TRANSFER_SIMPLE (shadowResolution);
+ TRANSFER_SIMPLE (shadowProjection);
+ TRANSFER_SIMPLE (shadowCascades);
+ TRANSFER_SIMPLE (shadowDistance);
+ TRANSFER_SIMPLE (blendWeights);
+ TRANSFER_SIMPLE (textureQuality);
+ TRANSFER_SIMPLE (anisotropicTextures);
+ TRANSFER_SIMPLE (antiAliasing);
+ TRANSFER_SIMPLE (softParticles);
+ transfer.Transfer (softVegetation, "softVegetation", kSimpleEditorMask | kHideInEditorMask);
+ transfer.Align();
+ TRANSFER_SIMPLE (vSyncCount);
+ TRANSFER_SIMPLE (lodBias);
+ TRANSFER_SIMPLE (maximumLODLevel);
+ TRANSFER_SIMPLE (particleRaycastBudget);
+
+ // Unity 3.4 introduced vSyncCount instead of syncToVBL.
+ if(transfer.IsVersionSmallerOrEqual(1))
+ {
+ bool syncToVBL;
+ transfer.Transfer (syncToVBL, "syncToVBL", kSimpleEditorMask | kHideInEditorMask);
+ vSyncCount = (syncToVBL?1:0);
+ }
+ transfer.Align();
+
+ TRANSFER_EDITOR_ONLY (excludedTargetPlatforms);
+}
+
+template<class TransferFunction>
+void QualitySettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ transfer.SetVersion( 5 );
+
+ transfer.Transfer (m_CurrentQuality, "m_CurrentQuality", kSimpleEditorMask);
+
+ TRANSFER_SIMPLE (m_QualitySettings);
+ TRANSFER_EDITOR_ONLY(m_PerPlatformDefaultQuality);
+
+ // Read old default selected default quality settings
+ if (transfer.IsVersionSmallerOrEqual(4))
+ {
+ int m_DefaultStandaloneQuality = 3;
+ int m_DefaultWebPlayerQuality = 3;
+ int m_DefaultMobileQuality = 2;
+
+ TRANSFER_SIMPLE (m_DefaultStandaloneQuality);
+ TRANSFER_SIMPLE (m_DefaultWebPlayerQuality);
+ TRANSFER_SIMPLE (m_DefaultMobileQuality);
+ transfer.Transfer (m_CurrentQuality, "m_EditorQuality", kSimpleEditorMask);
+
+ #if UNITY_EDITOR
+ for (int i=1; i<kBuildPlayerTypeCount; i++)
+ {
+ string target = GetBuildTargetGroupName((BuildTargetPlatform)i, false);
+
+ if (target == "")
+ continue;
+
+ PerPlatformDefaultQuality::iterator found = m_PerPlatformDefaultQuality.find(target);
+ if (found == m_PerPlatformDefaultQuality.end())
+ {
+ if (target == "Web")
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, m_DefaultWebPlayerQuality));
+ else if (target == "Standalone")
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, m_DefaultStandaloneQuality));
+ else if (target == "iPhone" || target == "Android" || target == "BlackBerry" || target == "Tizen")
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, m_DefaultMobileQuality));
+ else
+ m_PerPlatformDefaultQuality.insert (pair<string,int>(target, 3));
+ }
+ }
+ #else
+ #if WEBPLUG
+ m_CurrentQuality = m_DefaultWebPlayerQuality;
+ #elif ( (UNITY_ANDROID) || (UNITY_IPHONE) || (UNITY_BB10) || (UNITY_TIZEN) )
+ m_CurrentQuality = m_DefaultMobileQuality;
+ #else
+ m_CurrentQuality = m_DefaultStandaloneQuality;
+ #endif
+ #endif
+
+ if (m_QualitySettings.size () == 6)
+ {
+ m_QualitySettings[0].name = "Fastest";
+ m_QualitySettings[1].name = "Fast";
+ m_QualitySettings[2].name = "Simple";
+ m_QualitySettings[3].name = "Good";
+ m_QualitySettings[4].name = "Beautiful";
+ m_QualitySettings[5].name = "Fantastic";
+ }
+
+ }
+
+ if (transfer.IsVersionSmallerOrEqual(3))
+ {
+ QualitySetting tempQuality[kDefaultQualitySettingCount];
+ InitializeDefaultQualitySettings(tempQuality);
+#if UNITY_EDITOR
+ InitializePerPlatformDefaults (&m_PerPlatformDefaultQuality);
+#endif
+ transfer.Transfer (tempQuality[0], "Fastest", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[1], "Fast", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[2], "Simple", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[3], "Good", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[4], "Beautiful", kSimpleEditorMask);
+ transfer.Transfer (tempQuality[5], "Fantastic", kSimpleEditorMask);
+
+ // Unity 3.3 only supported Close Fit shadow projection
+ // In Unity 3.4 the default value is Stable Fit shadows.
+ if (transfer.IsVersionSmallerOrEqual(2))
+ {
+ for (int i = 0; i < kDefaultQualitySettingCount; i++)
+ tempQuality[i].shadowProjection = kShadowProjCloseFit;
+ }
+
+ m_QualitySettings.assign(tempQuality, tempQuality + kDefaultQualitySettingCount);
+ }
+
+ if (transfer.IsSerializingForGameRelease ())
+ {
+ TRANSFER(m_StrippedMaximumLODLevel);
+ }
+}
+
+void QualitySettings::SetPixelLightCount(int light)
+{
+ m_QualitySettings[m_CurrentQuality].pixelLightCount = light;
+
+ SetDirty();
+}
+
+void QualitySettings::SetShadowProjection(ShadowProjection shadowProjection)
+{
+ int projection = clamp<int>(shadowProjection, 0, kShadowProjectionCount-1);
+ m_QualitySettings[m_CurrentQuality].shadowProjection = projection;
+ SetDirty();
+}
+
+void QualitySettings::SetShadowCascades(int cascades)
+{
+ // valid cascade counts are 1, 2, 4
+ cascades = clamp(cascades,1,4);
+ if (cascades == 3)
+ cascades = 2;
+
+ m_QualitySettings[m_CurrentQuality].shadowCascades = cascades;
+ SetDirty();
+}
+
+// Used by the SceneView in order to temporarily override the shadow distance when rendering in ortho mode
+void QualitySettings::SetShadowDistanceTemporarily(float shadowDistance)
+{
+ shadowDistance = std::max ( shadowDistance, 0.0f );
+ m_QualitySettings[m_CurrentQuality].shadowDistance = shadowDistance;
+}
+
+void QualitySettings::SetShadowDistance(float shadowDistance)
+{
+ shadowDistance = std::max ( shadowDistance, 0.0f );
+ m_QualitySettings[m_CurrentQuality].shadowDistance = shadowDistance;
+ SetDirty();
+}
+
+void QualitySettings::SetSoftVegetation (bool softVegetation)
+{
+ m_QualitySettings[m_CurrentQuality].softVegetation = softVegetation;
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+}
+
+void QualitySettings::SetSoftParticles (bool soft)
+{
+ m_QualitySettings[m_CurrentQuality].softParticles = soft;
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+}
+
+void QualitySettings::SetVSyncCount (int count)
+{
+ if (m_QualitySettings[m_CurrentQuality].vSyncCount == count)
+ return;
+ m_QualitySettings[m_CurrentQuality].vSyncCount = count;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplyExpensiveSettings();
+}
+
+void QualitySettings::SetAntiAliasing (int aaCount)
+{
+ if (m_QualitySettings[m_CurrentQuality].antiAliasing == aaCount)
+ return;
+ m_QualitySettings[m_CurrentQuality].antiAliasing = aaCount;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplyExpensiveSettings();
+}
+
+void QualitySettings::SetAnisotropicTextures (int aniso)
+{
+ if (m_QualitySettings[m_CurrentQuality].anisotropicTextures == aniso)
+ return;
+ m_QualitySettings[m_CurrentQuality].anisotropicTextures = aniso;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetLODBias (float lodBias)
+{
+ if (m_QualitySettings[m_CurrentQuality].lodBias == lodBias)
+ return;
+ m_QualitySettings[m_CurrentQuality].lodBias = lodBias;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetMaximumLODLevel (int maximumLODLevel)
+{
+ if (m_QualitySettings[m_CurrentQuality].maximumLODLevel == maximumLODLevel)
+ return;
+ m_QualitySettings[m_CurrentQuality].maximumLODLevel = maximumLODLevel;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetParticleRaycastBudget (int particleRaycastBudget)
+{
+ if (m_QualitySettings[m_CurrentQuality].particleRaycastBudget == particleRaycastBudget)
+ return;
+ m_QualitySettings[m_CurrentQuality].particleRaycastBudget = particleRaycastBudget;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty ();
+ ApplySettings();
+}
+
+void QualitySettings::SetBlendWeights(int weights)
+{
+ if (m_QualitySettings[m_CurrentQuality].blendWeights == weights)
+ return;
+ m_QualitySettings[m_CurrentQuality].blendWeights = weights;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty();
+ ApplySettings();
+}
+
+void QualitySettings::SetMasterTextureLimit(int limit)
+{
+ if (m_QualitySettings[m_CurrentQuality].textureQuality == limit)
+ return;
+ m_QualitySettings[m_CurrentQuality].textureQuality = limit;
+ CheckConsistency();
+ // Prevent repaints caused by temporary quality settings changes
+ SetDirty();
+ ApplySettings();
+}
+
+void QualitySettings::InitializeClass()
+{
+}
+
+float QualitySettings::GetShadowDistanceForRendering()
+{
+#if UNITY_EDITOR
+ if (GetLightmapVisualization().GetEnabled())
+ return GetLightmapVisualization().GetShadowDistance();
+ else
+ return GetQualitySettings().GetCurrent().shadowDistance;
+#else
+ return GetQualitySettings().GetCurrent().shadowDistance;
+#endif
+}
+
+#if UNITY_EDITOR
+
+void QualitySettings::SetQualitySettings(const QualitySetting* settings, int size, int currentQuality)
+{
+ m_QualitySettings.assign(settings, settings + size);
+ m_CurrentQuality = currentQuality;
+ CheckConsistency();
+ AwakeFromLoad(kDefaultAwakeFromLoad);
+ SetDirty();
+}
+
+void QualitySettings::DeletePerPlatformDefaultQuality()
+{
+ m_PerPlatformDefaultQuality.clear();
+}
+
+int QualitySettings::GetDefaultQualityForPlatform (const std::string& platformName)
+{
+ PerPlatformDefaultQuality::iterator found = m_PerPlatformDefaultQuality.find(platformName);
+ if (found == m_PerPlatformDefaultQuality.end())
+ return -1;
+ else
+ return found->second;
+}
+
+#endif
diff --git a/Runtime/Misc/QualitySettings.h b/Runtime/Misc/QualitySettings.h
new file mode 100644
index 0000000..e983771
--- /dev/null
+++ b/Runtime/Misc/QualitySettings.h
@@ -0,0 +1,133 @@
+#ifndef QUALITYSETTINGS_H
+#define QUALITYSETTINGS_H
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+enum ShadowProjection
+{
+ kShadowProjCloseFit = 0,
+ kShadowProjStableFit,
+ kShadowProjectionCount
+};
+
+
+class QualitySettings : public GlobalGameManager {
+public:
+ REGISTER_DERIVED_CLASS (QualitySettings, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (QualitySettings)
+
+ QualitySettings(MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+
+ int GetCurrentIndex () const { return m_CurrentQuality;}
+ bool SetCurrent (string setting, bool applyExpensiveChanges);
+ void SetCurrentIndex (int setting, bool applyExpensiveChanges);
+
+ std::vector<std::string> GetQualitySettingsNames ();
+
+ virtual void CheckConsistency ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+
+ void ApplySettings( int previousIndex = -1, bool applyExpensiveChanges = false );
+
+ struct QualitySetting
+ {
+ DECLARE_SERIALIZE (QualitySetting)
+ QualitySetting();
+
+ UnityStr name;
+
+ int pixelLightCount; ///< Number of pixel lights to use
+ int shadows; ///< enum { Disable Shadows = 0, Hard Shadows Only = 1, Hard and Soft Shadows = 2 } Shadow quality
+ int shadowResolution; ///< enum { Low Resolution = 0, Medium Resolution = 1, High Resolution = 2, Very High Resolution = 3 } Shadow resolution
+ int shadowProjection; ///< enum { Close Fit = 0, Stable Fit = 1 } Shadow projection
+ int shadowCascades; ///< enum { No Cascades = 1, Two Cascades = 2, Four Cascades = 4 } Number of cascades for directional light shadows
+ float shadowDistance; ///< Shadow drawing distance
+ int blendWeights; ///< enum { 1 Bone=1, 2 Bones=2, 4 Bones = 4 } Bone count for mesh skinning
+ int textureQuality; ///< enum { Full Res = 0, Half Res = 1, Quarter Res = 2, Eighth Res = 3} Base texture level
+ int anisotropicTextures; ///< enum { Disabled = 0, Per Texture = 1, Forced On = 2 } When to enable anisotropic texturing
+ int antiAliasing; ///< enum { Disabled = 0, 2x Multi Sampling = 2, 4x Multi Sampling = 4, 8x Multi Sampling = 8 } Screen anti aliasing
+ int vSyncCount; ///< enum { Don't Sync = 0, Every VBlank = 1, Every Second VBlank = 2 } Limit refresh rate to avoid tearing
+ bool softParticles; ///< Use soft blending for particles?
+ bool softVegetation; ///< Use soft shading for terrain vegetation?
+ float lodBias;
+ int maximumLODLevel;
+ int particleRaycastBudget; ///< Number of rays to cast for approximate world collisions
+
+ #if UNITY_EDITOR
+ std::vector<UnityStr> excludedTargetPlatforms;
+ #endif
+ };
+
+ enum ShadowQuality
+ {
+ kShadowsDisable = 0,
+ kShadowsHardOnly,
+ kShadowsAll,
+ kShadowQualityCount
+ };
+ enum
+ {
+ kShadowResolutionCount = 4,
+ };
+
+ const QualitySetting& GetCurrent() const { return m_QualitySettings[m_CurrentQuality]; }
+ const QualitySetting& GetQualityForIndex(int index) const { Assert ( index < m_QualitySettings.size() ); return m_QualitySettings[index]; }
+ int GetQualitySettingsCount() const { return m_QualitySettings.size(); }
+
+ void SetPixelLightCount(int light);
+ void SetShadowProjection(ShadowProjection shadowProjection);
+ void SetShadowCascades(int cascades);
+ void SetShadowDistance(float shadowDistance);
+ void SetShadowDistanceTemporarily(float shadowDistance);
+ void SetSoftVegetation (bool softVegetation);
+ void SetSoftParticles (bool soft);
+ void SetVSyncCount (int count);
+ void SetAntiAliasing (int aaSettings);
+ void SetAnisotropicTextures (int aniso);
+ void SetLODBias (float lodBias);
+ void SetMaximumLODLevel (int maximumLODLevel);
+ void SetParticleRaycastBudget (int particleRaycastBudget);
+ void SetBlendWeights(int weights);
+ void SetMasterTextureLimit(int limit);
+
+ /// The stripped maximum LOD level is calculated from the supported quality settings for this platform when entering playmode & building a player.
+ /// It always acts as the absolute minimum for LOD's
+ void SetStrippedMaximumLODLevel (int maximumLODLevel) { m_StrippedMaximumLODLevel = maximumLODLevel; }
+ int GetStrippedMaximumLODLevel () { return m_StrippedMaximumLODLevel; }
+
+ #if UNITY_EDITOR
+ typedef std::map<UnityStr, int> PerPlatformDefaultQuality;
+ void SetQualitySettings(const QualitySetting* settings, int size, int defaultQuality);
+ void DeletePerPlatformDefaultQuality ();
+ int GetDefaultQualityForPlatform (const std::string& platformName);
+ #endif
+
+ static float GetShadowDistanceForRendering();
+
+ static void InitializeClass();
+ static void CleanupClass() {}
+
+private:
+
+ std::vector<QualitySetting> m_QualitySettings;
+
+ int m_StrippedMaximumLODLevel;
+ int m_CurrentQuality;
+
+ void ApplyExpensiveSettings();
+
+ #if UNITY_EDITOR
+ PerPlatformDefaultQuality m_PerPlatformDefaultQuality;
+ int m_PreviousAA;
+
+ int m_PreviousVSync;
+ #endif
+};
+QualitySettings& GetQualitySettings ();
+QualitySettings* GetQualitySettingsPtr ();
+
+
+#endif
diff --git a/Runtime/Misc/RegisterAllClasses.h b/Runtime/Misc/RegisterAllClasses.h
new file mode 100644
index 0000000..102289f
--- /dev/null
+++ b/Runtime/Misc/RegisterAllClasses.h
@@ -0,0 +1,6 @@
+#ifndef REGISTER_ALL_CLASSES_H
+#define REGISTER_ALL_CLASSES_H
+
+void RegisterAllClasses();
+
+#endif
diff --git a/Runtime/Misc/ReproductionLog.cpp b/Runtime/Misc/ReproductionLog.cpp
new file mode 100644
index 0000000..9a84ada
--- /dev/null
+++ b/Runtime/Misc/ReproductionLog.cpp
@@ -0,0 +1,706 @@
+#include "UnityPrefix.h"
+#include "ReproductionLog.h"
+
+#if SUPPORT_REPRODUCE_LOG
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/IMGUI/GUIManager.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "Runtime/Misc/CaptureScreenshot.h"
+#include "Runtime/Misc/Player.h"
+#include "Runtime/Export/WWW.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Utilities/File.h"
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include <fstream>
+#include "Runtime/Math/Random/Random.h"
+
+#if UNITY_OSX || UNITY_LINUX
+#include <sys/stat.h>
+#endif
+
+
+using namespace std;
+struct RemappedWWWStreamData;
+
+static std::ifstream* gReproduceInStream = NULL;
+static std::ofstream* gReproduceOutStream = NULL;
+
+static std::string gReproductionPath;
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+static FILE* gReproduceGfxLogFile = NULL;
+#endif
+int gScreenshotCount = 0;
+bool gDisableScreenShots = false;
+bool gNormalPlaybackSpeed = false;
+bool gRepeatScreenshots = true;
+int gLastScreenshotTime = 0;
+int gReproduceVersion = REPRODUCE_VERSION;
+ReproduceMode gReproMode = kPlaybackUninitialized;
+RemappedWWWStreamData* gRemappedWWWStreamData = NULL;
+Mutex gWaitForCompletionDownloadsMutex;
+set<WWW*> gWaitForCompletionDownloads;
+
+extern Rand gScriptingRand;
+
+ReproduceMode GetReproduceMode()
+{
+ return gReproMode;
+}
+
+void ReproductionWriteExitMessage(int result)
+{
+ if (result == 0)
+ {
+ LogString("Successfully reached end of reproduction log");
+ }
+ else
+ {
+ LogString("Aborting Reproduction playback");
+ }
+}
+
+void ReproductionExitPlayer (int result, bool writeExitMessage)
+{
+ if (writeExitMessage)
+ ReproductionWriteExitMessage(result);
+
+#if UNITY_OSX
+ FinishAllCaptureScreenshot ();
+
+ if (result == 0)
+ {
+ if (!PlayerCleanup(true,true))
+ ErrorString("Failed to clean up player");
+ }
+
+ // On Safari 64-bit, the browser is a seperate process. Take that down as well.
+ CFStringRef pluginHostID = CFSTR("com.apple.WebKit.PluginHost");
+ CFStringRef appID = CFBundleGetIdentifier(CFBundleGetMainBundle());
+ if (CFStringCompare(pluginHostID, appID, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ {
+ printf_console("killing Safari\n");
+ system("killall Safari");
+ }
+#endif
+ exit(result);
+}
+
+void FailReproduction (const std::string& err)
+{
+ ErrorString(Format("%s\nFrame: %d", err.c_str(), GetTimeManager().GetFrameCount()));
+ ReproductionExitPlayer(1);
+}
+
+struct RemappedWWWStreamData
+{
+ string absoluteUrl;
+ string srcValue;
+ int width;
+ int height;
+ vector<pair<string, string> > downloads;
+ int remapCount;
+
+ RemappedWWWStreamData () : remapCount(0) {}
+
+ void Write ()
+ {
+ std::ofstream stream (AppendPathName(gReproductionPath, "WWWRemap.log").c_str());
+ stream.setf(std::ios::fixed,std::ios::floatfield);
+ stream.precision(15);
+
+ stream << "absoluteUrl= " << absoluteUrl << endl;
+ stream << "srcValue= " << srcValue << endl;
+ stream << "width= " << width << endl;
+ stream << "height= " << height << endl;
+ for(int i=0;i<downloads.size();i++)
+ {
+ stream << "WWW= " << downloads[i].first << endl;
+ stream << "Remap= " << downloads[i].second << endl;
+ }
+ }
+
+ void Parse (std::ifstream& stream)
+ {
+ string temp;
+ stream >> temp;
+ if (temp == "absoluteUrl=")
+ stream >> absoluteUrl;
+ else
+ FailReproduction("Failed parsing absolute url" + temp);
+
+ stream >> temp;
+ if (temp == "srcValue=")
+ stream >> srcValue;
+ else
+ FailReproduction("Failed parsing src value" + temp);
+
+ stream >> temp; if (temp != "width=") FailReproduction("Failed parsing width"); stream >> width;
+ stream >> temp; if (temp != "height=") FailReproduction("Failed parsing height"); stream >> height;
+ while (true)
+ {
+ stream >> temp;
+ if (stream.eof())
+ break;
+
+ if (!BeginsWith(temp, "WWW="))
+ FailReproduction ("Failed parsing WWW GOT:" + temp);
+ string url;
+ stream >> url;
+
+ stream >> temp;
+ if (!BeginsWith(temp, "Remap="))
+ FailReproduction ("Failed parsing Remap");
+ string remap;
+ stream >> remap;
+
+ downloads.push_back(make_pair(url, remap));
+ }
+ }
+};
+
+void SetReproduceMode (string reproductionLogFolder)
+{
+ string reproductionLogPath = AppendPathName(reproductionLogFolder, "Reproduction.log");
+ string remapLogPath = AppendPathName(reproductionLogFolder, "WWWRemap.log");
+
+ if (gReproMode == kGenerateReproduceLog || gReproMode == kGenerateReproduceLogAndRemapWWW)
+ {
+ gReproduceOutStream = new std::ofstream (reproductionLogPath.c_str());
+ gReproduceOutStream->setf(std::ios::fixed,std::ios::floatfield);
+ gReproduceOutStream->precision(15);
+
+ if (gReproMode == kGenerateReproduceLogAndRemapWWW)
+ {
+ gRemappedWWWStreamData = new RemappedWWWStreamData();
+ }
+
+ gScriptingRand.SetSeed(0);
+ }
+ else if (gReproMode == kPlaybackReproduceLog)
+ {
+ gReproduceInStream = new std::ifstream (reproductionLogPath.c_str());
+ gReproduceInStream->setf(std::ios::fixed,std::ios::floatfield);
+ gReproduceInStream->precision(15);
+
+ std::ifstream stream (remapLogPath.c_str());
+ if (stream.is_open())
+ {
+ stream.setf(std::ios::fixed,std::ios::floatfield);
+ stream.precision(15);
+
+ gRemappedWWWStreamData = new RemappedWWWStreamData();
+ gRemappedWWWStreamData->Parse(stream);
+ }
+
+ gScriptingRand.SetSeed(0);
+ }
+}
+
+void ReproduceVersion()
+{
+ if (gReproMode == kGenerateReproduceLog || gReproMode == kGenerateReproduceLogAndRemapWWW)
+ {
+ //set version
+ *gReproduceOutStream << "Version" << " " << REPRODUCE_VERSION;
+ }
+ else if (gReproMode == kPlaybackReproduceLog)
+ {
+ //get version
+
+ if (gReproduceInStream->peek() == 'V')
+ {
+ std::string testVersion;
+ *gReproduceInStream >> testVersion;
+ *gReproduceInStream >> gReproduceVersion;
+ }
+ else
+ {
+ gReproduceVersion = 1;
+ }
+ }
+}
+
+int GetReproduceVersion()
+{
+ return gReproduceVersion;
+}
+
+string GetScreenshotPath()
+{
+ string reproductionPathTemp = AppendPathName( gReproductionPath, "/Images/" );
+ string imgOut = Format("%d.png", gScreenshotCount);
+ reproductionPathTemp = AppendPathName( reproductionPathTemp, imgOut );
+ gScreenshotCount++;
+
+ return reproductionPathTemp;
+}
+
+void CaptureScreenshotReproduction(bool manual)
+{
+ if(manual)
+ QueueScreenshot (GetScreenshotPath(), 0);
+
+ if(gDisableScreenShots)
+ return;
+
+ if(GetInputManager().GetKeyDown(SDLK_F6) && gReproduceOutStream)
+ gRepeatScreenshots = !gRepeatScreenshots;
+
+ if(gReproduceInStream || gReproduceOutStream)
+ {
+ if(GetInputManager().GetKey(SDLK_F5))
+ {
+ QueueScreenshot (GetScreenshotPath(), 0);
+ #if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+ fclose(gReproduceGfxLogFile);
+ gReproduceGfxLogFile = NULL;
+ #endif
+ }
+ }
+}
+
+bool HasNormalPlaybackSpeed()
+{
+ return gNormalPlaybackSpeed;
+}
+
+void BatchInitializeReproductionLog ()
+{
+ if (gReproMode != kPlaybackUninitialized)
+ return;
+ gReproMode = kNormalPlayback;
+
+ std::string instructionsFilePath;
+#if UNITY_OSX
+ string result = getenv ("HOME");
+ if (!result.empty())
+ {
+ instructionsFilePath = AppendPathName( result, "Library/Logs/Unity/GlobalPlaybackInstructions.log");
+ }
+#elif UNITY_WINRT
+ // ?!-
+#elif UNITY_WIN
+ const char* tempPath = ::getenv("TEMP");
+ if (!tempPath)
+ tempPath = "C:";
+ instructionsFilePath = AppendPathName (tempPath, "UnityGlobalPlaybackInstructions.log");
+#elif UNITY_LINUX
+ string result = getenv ("HOME");
+ if (!result.empty())
+ {
+ instructionsFilePath = AppendPathName( result, ".unity/GlobalPlaybackInstructions.log");
+ }
+#else
+#error "Unknown platform"
+#endif
+
+ if (!instructionsFilePath.empty())
+ {
+ //@TODO: this won't work on Windows when user's name has non-ASCII characters!
+ std::ifstream instructionsFile (instructionsFilePath.c_str());
+
+ if (instructionsFile.is_open())
+ {
+ std::string line;
+ std::vector<std::string> lines;
+
+ while (!instructionsFile.eof())
+ {
+ std::getline (instructionsFile,line);
+ lines.push_back (line);
+ }
+
+ gReproductionPath = lines[0];
+
+ // The real instructionsfile is read to determine reproduction mode and playback.log path, other variables should be added here
+ string reproductionPlayerLogPathComplete = AppendPathName(gReproductionPath, "PlayerComplete.log");
+ LogOutputToSpecificFile(reproductionPlayerLogPathComplete.c_str());
+
+ for (size_t i = 0; i < lines.size(); i++)
+ {
+ if(strcmp(lines[i].c_str(),"-reproduceInput") == 0)
+ gReproMode = kPlaybackReproduceLog;
+ else if(strcmp(lines[i].c_str(),"-recordInput") == 0)
+ gReproMode = kGenerateReproduceLog;
+ else if(strcmp(lines[i].c_str(),"-recordInputAndRemapWWW") == 0)
+ gReproMode = kGenerateReproduceLogAndRemapWWW;
+
+ if(!strcmp(lines[i].c_str(),"-disableScreenshots"))
+ gDisableScreenShots = true;
+ if(!strcmp(lines[i].c_str(),"-normalPlaybackSpeed"))
+ gNormalPlaybackSpeed = true;
+
+ // Do we want REF D3D9 device?
+ #if GFX_SUPPORTS_D3D9 && ENABLE_FORCE_GFX_RENDERER
+ if(!strcmp(lines[i].c_str(),"-force-d3d9-ref"))
+ ::g_ForceD3D9RefDevice = true;
+ #endif
+ }
+
+ if (gReproMode == kNormalPlayback)
+ {
+ ErrorString("Failed setting Reproduction mode from reproduction log file");
+ }
+
+ SetReproduceMode (gReproductionPath);
+ ReproduceVersion();
+
+ // Kill the instructions file, so that it wont get reused next time you run Unity
+ instructionsFile.close();
+ DeleteFileOrDirectory(instructionsFilePath);
+ }
+ }
+}
+
+void CheckReproduceTagAndExit(const std::string& tag, std::ifstream& stream)
+{
+ if (!CheckReproduceTag(tag, stream))
+ FailReproduction("Failed to read: " + tag);
+}
+
+bool CheckReproduceTag(const std::string& tag, std::ifstream& stream)
+{
+ int position = stream.tellg();
+ string temp;
+ stream >> temp;
+ if (temp == tag)
+ return true;
+
+ stream.seekg(position, ios::beg);
+ return false;
+}
+
+
+
+void ReadWriteAbsoluteUrl(UnityStr& srcValue, UnityStr& absoluteUrl)
+{
+ if (gRemappedWWWStreamData)
+ {
+ if (GetReproduceMode() == kPlaybackReproduceLog)
+ {
+ absoluteUrl = gRemappedWWWStreamData->absoluteUrl;
+ srcValue = gRemappedWWWStreamData->srcValue;
+ }
+ else
+ {
+ gRemappedWWWStreamData->absoluteUrl = absoluteUrl;
+ gRemappedWWWStreamData->srcValue = srcValue;
+ }
+ }
+}
+
+void WriteWebplayerSize(int width, int height)
+{
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ gRemappedWWWStreamData->width = width;
+ gRemappedWWWStreamData->height = height;
+ }
+}
+
+#if ENABLE_WWW
+
+void CleanupWWW (WWW* www)
+{
+ gWaitForCompletionDownloadsMutex.Lock();
+ gWaitForCompletionDownloads.erase(www);
+ gWaitForCompletionDownloadsMutex.Unlock();
+}
+
+
+
+bool ShouldWaitForCompletedDownloads ()
+{
+ if (!RunningReproduction())
+ return false;
+
+ gWaitForCompletionDownloadsMutex.Lock();
+ set<WWW*>& oldSet = gWaitForCompletionDownloads;
+ set<WWW*> newSet;
+ bool wait = false;
+ for (set<WWW*>::iterator i=oldSet.begin();i!=oldSet.end();++i)
+ {
+ WWW& www = **i;
+
+ #if WWW_USE_BROWSER
+ WWWBrowser* browser = static_cast<WWWBrowser*> (&www);
+ if (browser)
+ browser->ForceProgressDownload();
+ #endif
+
+ if(!www.IsDone() && ( www.GetUnityWebStream() == NULL || www.GetUnityWebStream()->GetProgressUntilLoadable() < 1 ))
+ {
+ newSet.insert(&www);
+ wait = true;
+ }
+ }
+ oldSet = newSet;
+ gWaitForCompletionDownloadsMutex.Unlock();
+
+ return wait;
+}
+
+void CreateWWWReproduce(WWW* www, const std::string& url, std::string& remappedUrl, int& postLength)
+{
+ remappedUrl = url;
+
+ if (RunningReproduction())
+ {
+ gWaitForCompletionDownloadsMutex.Lock();
+ gWaitForCompletionDownloads.insert(www);
+ gWaitForCompletionDownloadsMutex.Unlock();
+ }
+
+
+ // Play back from remap data
+ if (gRemappedWWWStreamData && GetReproduceMode() == kPlaybackReproduceLog)
+ {
+ int downloadNumber = gRemappedWWWStreamData->remapCount;
+ if (downloadNumber >= gRemappedWWWStreamData->downloads.size())
+ {
+ FailReproduction("Failed remapping download because the content is trying to downloading more WWW than the original reproduction log did:" + url);
+ return;
+ }
+
+ remappedUrl = Format("WWW-%d.bin", downloadNumber);
+ postLength = 0;
+
+ if (gRemappedWWWStreamData->downloads[downloadNumber].first != url)
+ FailReproduction("Failed remapping download because the content is trying to download a different url than it originally did:" + url + "\nexpected: " + gRemappedWWWStreamData->downloads[downloadNumber].first);
+ if (gRemappedWWWStreamData->downloads[downloadNumber].second != remappedUrl && gRemappedWWWStreamData->downloads[downloadNumber].second != string("INCOMPLETE"))
+ FailReproduction("Failed remapping download because the remapped name is not matching correctly:" + url);
+ gRemappedWWWStreamData->remapCount++;
+ }
+ // Setup remap data from downloaded content
+ else if (gRemappedWWWStreamData && GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ *www->GetReproRemapCount() = gRemappedWWWStreamData->remapCount;
+ gRemappedWWWStreamData->downloads.push_back(make_pair(url, string("INCOMPLETE")));
+ gRemappedWWWStreamData->remapCount++;
+ }
+
+}
+
+void CompleteWWWReproduce(WWW* www, const std::string& url, const UInt8* buffer, int size)
+{
+ // Store downloaded www data
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ int downloadNumber = *www->GetReproRemapCount();
+ string fileName = Format("WWW-%d.bin", downloadNumber);
+ if (!WriteBytesToFile(buffer, size, AppendPathName(gReproductionPath, fileName)))
+ FailReproduction("Failed writing completed WWW");
+
+ gRemappedWWWStreamData->downloads[downloadNumber].second = fileName;
+ }
+}
+
+#endif
+
+void ReproduceWriteMainDataFile(const UInt8* buffer, int size)
+{
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ {
+ if (!WriteBytesToFile(buffer, size, AppendPathName(gReproductionPath, "main.unity3d")))
+ FailReproduction("Failed writing completed WWW");
+ }
+}
+
+void ReadWriteReproductionInput()
+{
+ if (gReproduceInStream)
+ {
+ GetInputManager().ReadLog(*gReproduceInStream);
+ GetGUIManager().ReadLog(*gReproduceInStream);
+ }
+ else if (gReproduceOutStream)
+ {
+ GetInputManager().WriteLog(*gReproduceOutStream);
+ GetGUIManager().WriteLog(*gReproduceOutStream);
+ }
+
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+ if(GetInputManager().GetKey(SDLK_F5))
+ {
+ string reproductionPathTemp = AppendPathName( gReproductionPath, "/Images/" );
+ string imgOut = Format("%d.png", gScreenshotCount);
+ reproductionPathTemp = AppendPathName( reproductionPathTemp, imgOut );
+ gReproduceGfxLogFile = fopen(reproductionPathTemp.c_str(), "w");
+ }
+#endif
+}
+
+void ReadWriteReproductionTime()
+{
+ if (gReproduceInStream)
+ GetTimeManager().ReadLog(*gReproduceInStream);
+ else if (gReproduceOutStream)
+ {
+ GetTimeManager().WriteLog(*gReproduceOutStream);
+ }
+}
+
+std::ifstream* GetReproduceInStream ()
+{
+ return gReproduceInStream;
+}
+
+std::ofstream* GetReproduceOutStream ()
+{
+ return gReproduceOutStream;
+}
+
+void RepeatReproductionScreenshot()
+{
+ //making repeating screenshots
+ //adjust modulus to make it repeat screenshotting often
+ if (gRepeatScreenshots && gReproduceOutStream)
+ {
+ bool takeScreenshot = false;
+ int ms = RoundfToIntPos(GetTimeSinceStartup() * 1000);
+ if (ms - gLastScreenshotTime > 500)
+ {
+ gLastScreenshotTime = ms;
+ takeScreenshot = true;
+ }
+ GetInputManager().SetKeyState(SDLK_F5, takeScreenshot);
+ }
+}
+
+void PlayerCleanupReproduction()
+{
+ if (GetReproduceMode() == kGenerateReproduceLogAndRemapWWW)
+ gRemappedWWWStreamData->Write();
+
+ if (gReproduceOutStream)
+ gReproduceOutStream->flush();
+}
+
+bool gShouldExit = false;
+
+bool ShouldExitReproduction()
+{
+ return gShouldExit;
+}
+
+void ReadWriteReproductionEnd()
+{
+ if (gReproduceInStream)
+ {
+ string test;
+ *gReproduceInStream >> test;
+ if (test != "EndOfFrame")
+ {
+ ErrorString("Error reading reproduce EndOfFrame tag in Reproduction log. Forwarding to next EndOfFrame.");
+
+ while (!gReproduceInStream->eof() && test != "EndOfFrame")
+ {
+ *gReproduceInStream >> test;
+ }
+ }
+
+ if (gReproduceInStream->eof())
+ {
+ delete gReproduceInStream;
+ gReproduceInStream = NULL;
+ gShouldExit = true;
+ }
+ }
+ else if (gReproduceOutStream)
+ {
+ *gReproduceOutStream << "EndOfFrame";
+ }
+}
+
+void WriteReproductionString (std::ostream& out, const std::string& value)
+{
+ int size = value.size();
+ out << size;
+ for (int i=0;i<size;i++)
+ out << ' ' << (int)value[i];
+ out << std::endl;
+}
+
+void ReadReproductionString (std::istream& in, string& value)
+{
+ int size = 0;
+ in >> size;
+ value.resize(size);
+ for (int i=0;i<value.size();i++)
+ {
+ int val = 0;
+ in >> val;
+ value[i] = val;
+ }
+}
+
+#if SUPPORT_REPRODUCE_LOG_GFX_TRACE
+void LogToScreenshotLog(string str)
+{
+ if (gReproduceGfxLogFile != NULL)
+ {
+ fprintf(gReproduceGfxLogFile, str.c_str());
+ fprintf(gReproduceGfxLogFile, "\n");
+ }
+}
+#endif
+
+bool RunningReproduction()
+{
+ if (gReproduceInStream != NULL || gReproduceOutStream != NULL)
+ return true;
+ else
+ return false;
+}
+
+std::string GetReproductionDirectory()
+{
+ return gReproductionPath;
+}
+
+
+void WriteFloat (std::ostream& out, float& value)
+{
+ int intValue = RoundfToInt(value * 1000.0);
+ out << intValue;
+ value = intValue / 1000.0;
+}
+
+void ReadFloat (std::istream& in, float& value)
+{
+ int intValue = 0;
+ in >> intValue;
+ value = intValue / 1000.0;
+}
+
+void WriteFloat (std::ostream& out, double& value)
+{
+ int intValue = RoundfToInt(value * 1000.0);
+ out << intValue;
+ value = intValue / 1000.0;
+}
+
+void ReadFloat (std::istream& in, double& value)
+{
+ int intValue = 0;
+ in >> intValue;
+ value = intValue / 1000.0;
+}
+
+void WriteBigFloat (std::ostream& out, double& value)
+{
+ SInt64 intValue = RoundfToInt(value * 1000000.0);
+ out << intValue;
+ value = intValue / 1000000.0;
+}
+
+void ReadBigFloat (std::istream& in, double& value)
+{
+ SInt64 intValue = 0;
+ in >> intValue;
+ value = intValue / 1000000.0;
+}
+
+#endif
diff --git a/Runtime/Misc/ReproductionLog.h b/Runtime/Misc/ReproductionLog.h
new file mode 100644
index 0000000..b729572
--- /dev/null
+++ b/Runtime/Misc/ReproductionLog.h
@@ -0,0 +1,80 @@
+#ifndef REPRODUCTIONLOG_H
+#define REPRODUCTIONLOG_H
+
+#include "Configuration/UnityConfigure.h"
+
+#if SUPPORT_REPRODUCE_LOG
+
+#define REPRODUCE_VERSION 6
+
+enum ReproduceMode { kPlaybackUninitialized = -1,
+ kNormalPlayback = 0,
+ kGenerateReproduceLog = 1,
+ kPlaybackReproduceLog = 2,
+ kGenerateReproduceLogAndRemapWWW = 3 };
+
+class WWW;
+
+ReproduceMode GetReproduceMode();
+int GetReproduceVersion();
+void CaptureScreenshotReproduction(bool manual);
+bool HasNormalPlaybackSpeed();
+void BatchInitializeReproductionLog();
+void ReadWriteAbsoluteUrl(UnityStr& srcValue, UnityStr& absoluteUrl);
+void WriteWebplayerSize(int width, int height);
+void ReadWriteReproductionInput();
+void ReadWriteReproductionTime();
+
+void CreateWWWReproduce(WWW* www, const std::string& url, std::string& remappedUrl, int &postSize);
+void CompleteWWWReproduce(WWW* www, const std::string& url, const UInt8* buffer, int size);
+
+void ReproductionWriteExitMessage(int result);
+void ReproductionExitPlayer (int result, bool writeExitMessage=true);
+bool ShouldExitReproduction();
+
+std::string GetReproductionDirectory();
+void RepeatReproductionScreenshot();
+void ReadWriteReproductionEnd();
+bool RunningReproduction();
+FILE* GetReproduceOutputLogFile();
+void PlayerCleanupReproduction();
+void ReproduceWriteMainDataFile(const UInt8* buffer, int size);
+
+void WriteReproductionString (std::ostream& out, const std::string& value);
+void ReadReproductionString (std::istream& in, std::string& value);
+
+bool ShouldWaitForCompletedDownloads ();
+void CleanupWWW (WWW* www);
+
+
+std::ifstream* GetReproduceInStream ();
+std::ofstream* GetReproduceOutStream ();
+
+int GetReproduceVersion ();
+bool CheckReproduceTag(const std::string& tag, std::ifstream& stream);
+void CheckReproduceTagAndExit(const std::string& tag, std::ifstream& stream);
+
+void FailReproduction (const std::string& err);
+
+void WriteFloat (std::ostream& out, float& value);
+void ReadFloat (std::istream& in, float& value);
+
+void WriteFloat (std::ostream& out, double& value);
+void ReadFloat (std::istream& in, double& value);
+
+void WriteBigFloat (std::ostream& out, double& value);
+void ReadBigFloat (std::istream& in, double& value);
+
+#else
+
+inline bool RunningReproduction() { return false; }
+
+#endif
+
+#if SUPPORT_REPRODUCE_LOG && SUPPORT_REPRODUCE_LOG_GFX_TRACE
+void LogToScreenshotLog(std::string str);
+#else
+#define LogToScreenshotLog(x)
+#endif
+
+#endif //REPRODUCTIONLOG_H
diff --git a/Runtime/Misc/ResourceManager.cpp b/Runtime/Misc/ResourceManager.cpp
new file mode 100644
index 0000000..c30dc38
--- /dev/null
+++ b/Runtime/Misc/ResourceManager.cpp
@@ -0,0 +1,1292 @@
+#include "UnityPrefix.h"
+#include "ResourceManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+#include "Runtime/Utilities/InitializeAndCleanup.h"
+#include "Configuration/UnityConfigureOther.h"
+#include "Runtime/Misc/BuildSettings.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Mono/MonoManager.h"
+#endif
+#if UNITY_EDITOR
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/AssetPipeline/MonoCompilationPipeline.h"
+#include <vector>
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Editor/Src/Graphs/GraphUtils.h"
+#include "Editor/Src/EditorModules.h"
+#include "ResourceManagerGUIDs.h"
+#endif
+
+using namespace std;
+
+const char* kResourcePath = "Library/unity default resources";
+const char* kOldWebResourcePath = "Library/unity_web_old";
+
+static bool s_AreResourcesInitialized;
+
+#if UNITY_EDITOR
+static const char* kDefaultResourcesSource = "Resources/unity default resources";
+
+static const char* kEditorDefaultResourcesSource = "Resources/unity editor resources";
+const char* kEditorResourcePath = "Library/unity editor resources";
+const char* kDefaultExtraResourcesPath = "Resources/unity_builtin_extra";
+
+static BuiltinResourceManager* gBuiltinExtraResourceManager;
+
+static void RegisterBuiltinEditorScript (int instanceID, const char* className);
+static void RegisterBuiltinEditorInternalScript (int instanceID, const char* className);
+static void DoRegisterBuiltinEditorScript (int instanceID, const char* className, const char* _namespace, const char* assemblyName, bool isEditorScript);
+
+void BuiltinResourceManager::InitializeExtraResources ()
+{
+ m_ResourcePath = kDefaultExtraResourcesPath;
+ m_RequiredHideFlags = Object::kNotEditable;
+ m_AllowResourceManagerAccess = true;
+
+ // Issue RegisterResource shader calls in the expected loading order:
+ // if any shader references another (e.g. UsePass), the referenced must come first!
+
+ // Regular opaque
+ RegisterShader (6, "Normal-VertexLit.shader", "VertexLit");
+ RegisterShader (1, "Normal-DiffuseFast.shader", "Legacy Shaders/Diffuse Fast");
+ RegisterShader (7, "Normal-Diffuse.shader", "Diffuse");
+ RegisterShader (2, "Normal-Bumped.shader", "Bumped Diffuse");
+ RegisterShader (3, "Normal-Glossy.shader", "Specular");
+ RegisterShader (4, "Normal-BumpSpec.shader", "Bumped Specular");
+ RegisterShader (5, "Normal-DiffuseDetail.shader", "Diffuse Detail");
+ // Self-Illumin
+ RegisterShader (14, "Illumin-VertexLit.shader", "Self-Illumin/VertexLit");
+ RegisterShader (10, "Illumin-Diffuse.shader", "Self-Illumin/Diffuse");
+ RegisterShader (11, "Illumin-Bumped.shader", "Self-Illumin/Bumped Diffuse");
+ RegisterShader (12, "Illumin-Glossy.shader", "Self-Illumin/Specular");
+ RegisterShader (13, "Illumin-BumpSpec.shader", "Self-Illumin/Bumped Specular");
+ // Reflective
+ RegisterShader (24, "Reflect-VertexLit.shader", "Reflective/VertexLit");
+ RegisterShader (25, "Reflect-BumpNolight.shader", "Reflective/Bumped Unlit");
+ RegisterShader (20, "Reflect-Diffuse.shader", "Reflective/Diffuse");
+ RegisterShader (21, "Reflect-Bumped.shader", "Reflective/Bumped Diffuse");
+ RegisterShader (26, "Reflect-BumpVertexLit.shader", "Reflective/Bumped VertexLit");
+ RegisterShader (22, "Reflect-Glossy.shader", "Reflective/Specular");
+ RegisterShader (23, "Reflect-BumpSpec.shader", "Reflective/Bumped Specular");
+ // Transparent
+ RegisterShader (34, "Alpha-VertexLit.shader", "Transparent/VertexLit");
+ RegisterShader (30, "Alpha-Diffuse.shader", "Transparent/Diffuse");
+ RegisterShader (31, "Alpha-Bumped.shader", "Transparent/Bumped Diffuse");
+ RegisterShader (32, "Alpha-Glossy.shader", "Transparent/Specular");
+ RegisterShader (33, "Alpha-BumpSpec.shader", "Transparent/Bumped Specular");
+ // Transparent/Cutout
+ RegisterShader (50, "AlphaTest-VertexLit.shader", "Transparent/Cutout/VertexLit");
+ RegisterShader (51, "AlphaTest-Diffuse.shader", "Transparent/Cutout/Diffuse");
+ RegisterShader (52, "AlphaTest-Bumped.shader", "Transparent/Cutout/Bumped Diffuse");
+ RegisterShader (53, "AlphaTest-Glossy.shader", "Transparent/Cutout/Specular");
+ RegisterShader (54, "AlphaTest-BumpSpec.shader", "Transparent/Cutout/Bumped Specular");
+ // Parallax
+ RegisterShader (8, "Normal-Parallax.shader", "Parallax Diffuse");
+ RegisterShader (9, "Normal-ParallaxSpec.shader", "Parallax Specular");
+ RegisterShader (15, "Illumin-Parallax.shader", "Self-Illumin/Parallax Diffuse");
+ RegisterShader (16, "Illumin-ParallaxSpec.shader", "Self-Illumin/Parallax Specular");
+ RegisterShader (27, "Reflect-Parallax.shader", "Reflective/Parallax Diffuse");
+ RegisterShader (28, "Reflect-ParallaxSpec.shader", "Reflective/Parallax Specular");
+ RegisterShader (35, "Alpha-Parallax.shader", "Transparent/Parallax Diffuse");
+ RegisterShader (36, "Alpha-ParallaxSpec.shader", "Transparent/Parallax Specular");
+
+ // Misc
+ RegisterShader (100, "Decal.shader", "Decal");
+ RegisterShader (101, "Flare.shader", "FX/Flare");
+ RegisterShader (103, "skybox cubed.shader", "RenderFX/Skybox Cubed");
+ RegisterShader (104, "skybox.shader", "RenderFX/Skybox");
+
+ // Particle shaders
+ RegisterShader (200, "Particle Add.shader", "Particles/Additive");
+ RegisterShader (201, "Particle AddMultiply.shader", "Particles/~Additive-Multiply");
+ RegisterShader (202, "Particle AddSmooth.shader", "Particles/Additive (Soft)");
+ RegisterShader (203, "Particle Alpha Blend.shader", "Particles/Alpha Blended");
+ //RegisterResource (204, "Particle Alpha Blend Deprecated.shader", "Shader"); // obsolete
+ RegisterShader (205, "Particle Multiply.shader", "Particles/Multiply");
+ RegisterShader (206, "Particle MultiplyDouble.shader", "Particles/Multiply (Double)");
+ RegisterShader (207, "Particle Premultiply Blend.shader", "Particles/Alpha Blended Premultiply");
+ RegisterShader (208, "Particle VertexLit Blended.shader", "Particles/VertexLit Blended");
+
+#if ENABLE_SPRITES
+ // Sprite shaders
+ RegisterShader (10800, "Sprites-Diffuse.shader", "Sprites/Diffuse");
+#endif
+
+ // Various
+ RegisterShader (10512, "AlphaTest-SoftEdgeUnlit.shader", "Transparent/Cutout/Soft Edge Unlit");
+
+ // Terrain engine
+ RegisterShader (10500, "TerrainShaders/Details/VertexLit.shader", "Hidden/TerrainEngine/Details/Vertexlit", false);
+ RegisterShader (10501, "TerrainShaders/Details/WavingGrass.shader", "Hidden/TerrainEngine/Details/WavingDoublePass", false);
+ RegisterShader (10502, "TerrainShaders/Details/WavingGrassBillboard.shader", "Hidden/TerrainEngine/Details/BillboardWavingDoublePass", false);
+ RegisterShader (10505, "TerrainShaders/Splats/FirstPass.shader", "Nature/Terrain/Diffuse");
+ RegisterShader (10503, "TerrainShaders/Splats/AddPass.shader", "Hidden/TerrainEngine/Splatmap/Lightmap-AddPass", false);
+ RegisterShader (10507, "TerrainShaders/Trees/BillboardTree.shader", "Hidden/TerrainEngine/BillboardTree", false);
+ //RegisterResource (10513, "TerrainShaders/Trees/SH-Tree Leaves customLit.shader", "Shader"); // not used anymore
+ //RegisterResource (10514, "TerrainShaders/Trees/SH-Tree Bark customLit.shader", "Shader"); // not used anymore
+
+ // 2.x lightmap shaders
+ RegisterShader (40, "Lightmap-VertexLit.shader", "Legacy Shaders/Lightmapped/VertexLit");
+ RegisterShader (41, "Lightmap-Diffuse.shader", "Legacy Shaders/Lightmapped/Diffuse");
+ RegisterShader (42, "Lightmap-Bumped.shader", "Legacy Shaders/Lightmapped/Bumped Diffuse");
+ RegisterShader (43, "Lightmap-Glossy.shader", "Legacy Shaders/Lightmapped/Specular");
+ RegisterShader (44, "Lightmap-BumpSpec.shader", "Legacy Shaders/Lightmapped/Bumped Specular");
+
+ // 2.x tree shaders
+ RegisterShader (10508, "Nature/SoftOcclusion/TreeSoftOcclusionBarkRendertex.shader", "Hidden/Nature/Tree Soft Occlusion Bark Rendertex", false);
+ RegisterShader (10509, "Nature/SoftOcclusion/TreeSoftOcclusionBark.shader", "Nature/Tree Soft Occlusion Bark");
+ RegisterShader (10510, "Nature/SoftOcclusion/TreeSoftOcclusionLeavesRendertex.shader", "Hidden/Nature/Tree Soft Occlusion Leaves Rendertex", false);
+ RegisterShader (10511, "Nature/SoftOcclusion/TreeSoftOcclusionLeaves.shader", "Nature/Tree Soft Occlusion Leaves");
+
+ // Tree Creator shaders
+ RegisterShader (10600, "Nature/TreeCreator/TreeCreatorBark.shader", "Nature/Tree Creator Bark");
+ RegisterShader (10601, "Nature/TreeCreator/TreeCreatorLeaves.shader", "Nature/Tree Creator Leaves");
+ RegisterShader (10602, "Nature/TreeCreator/TreeCreatorBarkRendertex.shader", "Hidden/Nature/Tree Creator Bark Rendertex", false);
+ RegisterShader (10603, "Nature/TreeCreator/TreeCreatorLeavesRendertex.shader", "Hidden/Nature/Tree Creator Leaves Rendertex", false);
+ RegisterShader (10604, "Nature/TreeCreator/TreeCreatorBarkOptimized.shader", "Hidden/Nature/Tree Creator Bark Optimized", false);
+ RegisterShader (10605, "Nature/TreeCreator/TreeCreatorLeavesOptimized.shader", "Hidden/Nature/Tree Creator Leaves Optimized", false);
+
+ RegisterShader (10606, "Nature/TreeCreator/TreeCreatorLeavesFast.shader", "Nature/Tree Creator Leaves Fast");
+ RegisterShader (10607, "Nature/TreeCreator/TreeCreatorLeavesFastOptimized.shader", "Hidden/Nature/Tree Creator Leaves Fast Optimized", false);
+
+ // Terrain shaders
+ RegisterShader (10620, "Nature/Terrain/TerrBumpFirstPass.shader", "Nature/Terrain/Bumped Specular");
+ RegisterShader (10621, "Nature/Terrain/TerrBumpAddPass.shader", "Hidden/Nature/Terrain/Bumped Specular AddPass", false);
+
+ // Mobile optimized shaders
+ RegisterShader (10700, "Mobile/Mobile-Skybox.shader", "Mobile/Skybox");
+ RegisterShader (10701, "Mobile/Mobile-VertexLit.shader", "Mobile/VertexLit");
+ RegisterShader (10703, "Mobile/Mobile-Diffuse.shader", "Mobile/Diffuse");
+ RegisterShader (10704, "Mobile/Mobile-Bumped.shader", "Mobile/Bumped Diffuse");
+ RegisterShader (10705, "Mobile/Mobile-BumpSpec.shader", "Mobile/Bumped Specular");
+ RegisterShader (10706, "Mobile/Mobile-BumpSpec-1DirectionalLight.shader", "Mobile/Bumped Specular (1 Directional Light)");
+ RegisterShader (10707, "Mobile/Mobile-VertexLit-OnlyDirectionalLights.shader", "Mobile/VertexLit (Only Directional Lights)");
+ RegisterShader (10708, "Mobile/Mobile-Lightmap-Unlit.shader", "Mobile/Unlit (Supports Lightmap)");
+
+ RegisterShader (10720, "Mobile/Mobile-Particle-Add.shader", "Mobile/Particles/Additive");
+ RegisterShader (10721, "Mobile/Mobile-Particle-Alpha.shader", "Mobile/Particles/Alpha Blended");
+ RegisterShader (10722, "Mobile/Mobile-Particle-Alpha-VertexLit.shader", "Mobile/Particles/VertexLit Blended");
+ RegisterShader (10723, "Mobile/Mobile-Particle-Multiply.shader", "Mobile/Particles/Multiply");
+
+ // Unlit shaders
+ RegisterShader (10750, "Unlit/Unlit-Alpha.shader", "Unlit/Transparent");
+ RegisterShader (10751, "Unlit/Unlit-AlphaTest.shader", "Unlit/Transparent Cutout");
+ RegisterShader (10752, "Unlit/Unlit-Normal.shader", "Unlit/Texture");
+
+ // Materials & textures
+ RegisterResource (10300, "Default-Particle.psd", "Texture2D");
+ RegisterResource (10301, "Default-Particle.mat", "Material");
+ RegisterResource (10302, "Default-Diffuse.mat", "Material");
+
+ m_Resources.sort();
+}
+
+static void RemapShadersToDefaultExtraResources ()
+{
+ // We have moved some shaders from builtin to default resources extra,
+ // make sure we don't break any backwards compatibility by remapping all references to them on load
+ // in the editor.
+
+ const int ids[] = {
+ 6, 1, 7, 2, 3, 4, 5, // regular opaque
+ 14, 10, 11, 12, 13, // self-illumin
+ 24, 25, 20, 21, 26, 22, 23, // reflective
+ 34, 30, 31, 32, 33, // transparent
+ 50, 51, 52, 53, 54, // transparent/cutout
+ 8, 9, 15, 16, 27, 28, 35, 36, // parallax
+ 100, 101, 103, 104, // misc
+ 200, 201, 202, 203, 205, 206, 207, 208, // particles
+ 10512, // various
+ 10500, 10501, 10502, 10505, 10503, 10507, // terrain engine
+ 40, 41, 42, 43, 44, // 2.x lightmap shaders
+ 10508, 10509, 10510, 10511, // 2.x tree shaders
+ 10600, 10601, 10602, 10603, 10604, 10605, // Tree Creator shaders
+ 10300, 10301, 10302, // default textures & materials
+ };
+
+ PersistentManager& pm = GetPersistentManager();
+ for (int i = 0; i < ARRAY_SIZE(ids); ++i)
+ {
+ pm.RemapInstanceIDOnLoad(kResourcePath, ids[i], kDefaultExtraResourcesPath, ids[i]);
+ }
+}
+
+
+void BuiltinResourceManager::GetResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources)
+{
+ for (Resources::iterator i = m_Resources.begin(); i != m_Resources.end(); ++i)
+ {
+ if (i->classID != classID || !i->userVisible)
+ continue;
+ // Cache and store the name, so we don't have to load the actual object
+ // each time we do GetResourcesOfClass
+ if (i->cachedDisplayName.empty())
+ {
+ NamedObject* no = dynamic_instanceID_cast<NamedObject*>(i->cachedInstanceID);
+ i->cachedDisplayName = no ? no->GetName() : "<no name>";
+ }
+ outResources.push_back (std::make_pair(i->cachedDisplayName, i->cachedInstanceID));
+ }
+}
+
+void BuiltinResourceManager::GetBuiltinResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources)
+{
+ GetBuiltinResourceManager().GetResourcesOfClass (classID, outResources);
+ GetBuiltinExtraResourceManager().GetResourcesOfClass (classID, outResources);
+}
+
+#endif // #if UNITY_EDITOR
+
+
+#if WEBPLUG
+static void RemapShadersToOldWebResources ()
+{
+ const int ids[] = {
+ 6, 1, 7, 2, 3, 4, 5, // regular opaque
+ 14, 10, 11, 12, 13, // self-illumin
+ 24, 25, 20, 21, 26, 22, 23, // reflective
+ 34, 30, 31, 32, 33, // transparent
+ 50, 51, 52, 53, 54, // transparent/cutout
+ 8, 9, 15, 16, 27, 28, 35, 36, // parallax
+ 100, 101, 103, 104, // misc
+ 200, 201, 202, 203, 205, 206, 207, 208, // particles
+ 10512, // various
+ 10500, 10501, 10502, 10505, 10503, 10507, // terrain engine
+ 10300, 10301, 10302, // default textures & materials
+ };
+
+ PersistentManager& pm = GetPersistentManager();
+ for (int i = 0; i < ARRAY_SIZE(ids); ++i)
+{
+ pm.RemapInstanceIDOnLoad(kResourcePath, ids[i], kOldWebResourcePath, ids[i]);
+ }
+}
+#endif // #if WEBPLUG
+
+
+#if WEBPLUG || UNITY_EDITOR
+void BuiltinResourceManager::InitializeOldWebResources ()
+{
+ Assert (m_Resources.empty ());
+
+ m_ResourcePath = kOldWebResourcePath;
+ m_RequiredHideFlags = Object::kHideAndDontSave | Object::kHideInspector;
+ m_AllowResourceManagerAccess = true;
+
+ RegisterShader (6, "Normal-VertexLit.shader", "VertexLit");
+ RegisterShader (1, "Normal-DiffuseFast.shader", "Legacy Shaders/Diffuse Fast");
+ RegisterShader (7, "Normal-Diffuse.shader", "Diffuse");
+ RegisterShader (2, "Normal-Bumped.shader", "Bumped Diffuse");
+ RegisterShader (3, "Normal-Glossy.shader", "Specular");
+ RegisterShader (4, "Normal-BumpSpec.shader", "Bumped Specular");
+ RegisterShader (5, "Normal-DiffuseDetail.shader", "Diffuse Detail");
+
+ RegisterShader (14, "Illumin-VertexLit.shader", "Self-Illumin/VertexLit");
+ RegisterShader (10, "Illumin-Diffuse.shader", "Self-Illumin/Diffuse");
+ RegisterShader (11, "Illumin-Bumped.shader", "Self-Illumin/Bumped Diffuse");
+ RegisterShader (12, "Illumin-Glossy.shader", "Self-Illumin/Specular");
+ RegisterShader (13, "Illumin-BumpSpec.shader", "Self-Illumin/Bumped Specular");
+
+ RegisterShader (24, "Reflect-VertexLit.shader", "Reflective/VertexLit");
+ RegisterShader (25, "Reflect-BumpNolight.shader", "Reflective/Bumped Unlit");
+ RegisterShader (20, "Reflect-Diffuse.shader", "Reflective/Diffuse");
+ RegisterShader (21, "Reflect-Bumped.shader", "Reflective/Bumped Diffuse");
+ RegisterShader (26, "Reflect-BumpVertexLit.shader", "Reflective/Bumped VertexLit");
+ RegisterShader (22, "Reflect-Glossy.shader", "Reflective/Specular");
+ RegisterShader (23, "Reflect-BumpSpec.shader", "Reflective/Bumped Specular");
+
+ RegisterShader (34, "Alpha-VertexLit.shader", "Transparent/VertexLit");
+ RegisterShader (30, "Alpha-Diffuse.shader", "Transparent/Diffuse");
+ RegisterShader (31, "Alpha-Bumped.shader", "Transparent/Bumped Diffuse");
+ RegisterShader (32, "Alpha-Glossy.shader", "Transparent/Specular");
+ RegisterShader (33, "Alpha-BumpSpec.shader", "Transparent/Bumped Specular");
+
+ RegisterShader (50, "AlphaTest-VertexLit.shader", "Transparent/Cutout/VertexLit");
+ RegisterShader (51, "AlphaTest-Diffuse.shader", "Transparent/Cutout/Diffuse");
+ RegisterShader (52, "AlphaTest-Bumped.shader", "Transparent/Cutout/Bumped Diffuse");
+ RegisterShader (53, "AlphaTest-Glossy.shader", "Transparent/Cutout/Specular");
+ RegisterShader (54, "AlphaTest-BumpSpec.shader", "Transparent/Cutout/Bumped Specular");
+
+ RegisterShader (8, "Normal-Parallax.shader", "Parallax Diffuse");
+ RegisterShader (9, "Normal-ParallaxSpec.shader", "Parallax Specular");
+ RegisterShader (15, "Illumin-Parallax.shader", "Self-Illumin/Parallax Diffuse");
+ RegisterShader (16, "Illumin-ParallaxSpec.shader", "Self-Illumin/Parallax Specular");
+ RegisterShader (27, "Reflect-Parallax.shader", "Reflective/Parallax Diffuse");
+ RegisterShader (28, "Reflect-ParallaxSpec.shader", "Reflective/Parallax Specular");
+ RegisterShader (35, "Alpha-Parallax.shader", "Transparent/Parallax Diffuse");
+ RegisterShader (36, "Alpha-ParallaxSpec.shader", "Transparent/Parallax Specular");
+
+ RegisterShader (100, "Decal.shader", "Decal");
+ RegisterShader (101, "Flare.shader", "FX/Flare");
+ RegisterShader (103, "skybox cubed.shader", "RenderFX/Skybox Cubed");
+ RegisterShader (104, "skybox.shader", "RenderFX/Skybox");
+
+ RegisterShader (200, "Particle Add.shader", "Particles/Additive");
+ RegisterShader (201, "Particle AddMultiply.shader", "Particles/~Additive-Multiply");
+ RegisterShader (202, "Particle AddSmooth.shader", "Particles/Additive (Soft)");
+ RegisterShader (203, "Particle Alpha Blend.shader", "Particles/Alpha Blended");
+ RegisterShader (205, "Particle Multiply.shader", "Particles/Multiply");
+ RegisterShader (206, "Particle MultiplyDouble.shader", "Particles/Multiply (Double)");
+ RegisterShader (207, "Particle Premultiply Blend.shader", "Particles/Alpha Blended Premultiply");
+ RegisterShader (208, "Particle VertexLit Blended.shader", "Particles/VertexLit Blended");
+
+ RegisterShader (10512, "AlphaTest-SoftEdgeUnlit.shader", "Transparent/Cutout/Soft Edge Unlit");
+
+ RegisterShader (10500, "TerrainShaders/Details/VertexLit.shader", "Hidden/TerrainEngine/Details/Vertexlit");
+ RegisterShader (10501, "TerrainShaders/Details/WavingGrass.shader", "Hidden/TerrainEngine/Details/WavingDoublePass");
+ RegisterShader (10502, "TerrainShaders/Details/WavingGrassBillboard.shader", "Hidden/TerrainEngine/Details/BillboardWavingDoublePass");
+ RegisterShader (10505, "TerrainShaders/Splats/FirstPass.shader", "Nature/Terrain/Diffuse");
+ RegisterShader (10503, "TerrainShaders/Splats/AddPass.shader", "Hidden/TerrainEngine/Splatmap/Lightmap-AddPass");
+ RegisterShader (10507, "TerrainShaders/Trees/BillboardTree.shader", "Hidden/TerrainEngine/BillboardTree");
+
+ // Materials & textures
+ RegisterResource (10300, "Default-Particle.psd", "Texture2D");
+ RegisterResource (10301, "Default-Particle.mat", "Material");
+ RegisterResource (10302, "Default-Diffuse.mat", "Material");
+
+ m_Resources.sort();
+}
+#endif // #if WEBPLUG || UNITY_EDITOR
+
+
+
+template<class TransferFunction>
+void ResourceManager::Dependency::Transfer (TransferFunction& transfer)
+{
+ transfer.Transfer (object, "m_Object");
+ transfer.Transfer (dependencies, "m_Dependencies");
+}
+
+void BuiltinResourceManager::InitializeResources ()
+{
+ m_ResourcePath = kResourcePath;
+ m_RequiredHideFlags = Object::kHideAndDontSave | Object::kHideInspector;
+ m_AllowResourceManagerAccess = true;
+
+ AssertIf (!m_Resources.empty ());
+
+
+ // Issue RegisterResource shader calls in the expected loading order:
+ // if any shader references another (e.g. UsePass), the referenced must come first!
+
+ RegisterShader (60, "Shadow-ScreenBlur.shader", "Hidden/Shadow-ScreenBlur", false);
+ RegisterShader (61, "Camera-DepthTexture.shader", "Hidden/Camera-DepthTexture", false);
+ RegisterShader (62, "Camera-DepthNormalTexture.shader", "Hidden/Camera-DepthNormalTexture", false);
+ RegisterShader (63, "Internal-PrePassLighting.shader", "Hidden/Internal-PrePassLighting", false);
+ RegisterShader (64, "Internal-PrePassCollectShadows.shader", "Hidden/Internal-PrePassCollectShadows", false);
+ RegisterShader (65, "Internal-CombineDepthNormals.shader", "Hidden/Internal-CombineDepthNormals", false);
+ RegisterShader (66, "Internal-BlitCopy.shader", "Hidden/BlitCopy", false);
+ RegisterShader (67, "Shadow-ScreenBlurRotated.shader", "Hidden/Shadow-ScreenBlurRotated", false);
+ RegisterShader (68, "Internal-Clear.shader", "Hidden/InternalClear", false);
+
+ RegisterShader (102, "Internal-Flare.shader", "Hidden/Internal-Flare", false);
+ RegisterShader (105, "Internal-Halo.shader", "Hidden/Internal-Halo", false);
+
+ //RegisterResource (300, "FX-Water.shader", "Shader"); // obsolete
+ //RegisterResource (500, "r7000bumpmap.cgvp", "CGProgram"); // obsolete
+ //RegisterResource (501, "r7000bumpmap2.cgvp", "CGProgram"); // obsolete
+
+ // Light stuff
+ //RegisterResource (10000, "Med.psd", "Cubemap"); // obsolete
+ RegisterResource (10001, "Soft.psd", "Texture2D", false);
+
+ // internal GUI stuff
+ RegisterShader (9000, "Internal-GUITextureClip.shader", "Hidden/Internal-GUITextureClip", false);
+ RegisterShader (9001, "Internal-GUITextureClipText.shader", "Hidden/Internal-GUITextureClipText", false);
+ RegisterShader (9002, "Internal-GUITexture.shader", "Hidden/Internal-GUITexture", false);
+ RegisterShader (9003, "Internal-GUITextureBlit.shader", "Hidden/Internal-GUITextureBlit", false);
+
+ // Default font
+ RegisterResource (10100, kDefaultFontName, "Material", false);
+ RegisterShader (10101, "Font.shader", "GUI/Text Shader");
+ RegisterResource (10102, kDefaultFontName, "Font");
+ RegisterResource (10103, kDefaultFontName, "Texture2D", false);
+
+ // Default meshes
+ RegisterResource (10202, "Cube.fbx", "Mesh");
+
+ RegisterResource (10206, "New-Cylinder.fbx", "Mesh");
+ RegisterResource (10207, "New-Sphere.fbx", "Mesh");
+ RegisterResource (10208, "New-Capsule.fbx", "Mesh");
+ RegisterResource (10209, "New-Plane.fbx", "Mesh");
+ RegisterResource (10210, "Quad.fbx", "Mesh");
+ // These 3 have zero normals, because pre-4.3 light prepass shader expects it
+ RegisterResource (10211, "icosphere.fbx", "Mesh");
+ RegisterResource (10212, "icosahedron.fbx", "Mesh");
+ RegisterResource (10213, "pyramid.fbx", "Mesh");
+
+ // DEPRECATED builtin meshes (Deprecated in version 1.5 (Adjust scale (eg. capsule is 2 meters high instead of 4)))
+ RegisterResource (10200, "Sphere.fbx", "Mesh", false);
+ RegisterResource (10203, "Cylinder.fbx", "Mesh", false);
+ RegisterResource (10204, "Plane.fbx", "Mesh", false);
+ RegisterResource (10205, "Capsule.fbx", "Mesh", false);
+
+ // Watermark, splash etc.
+ RegisterResource (10400, "UnityWaterMark-small.png", "Texture2D", false);
+ RegisterResource (10401, "EscToExit_back.png", "Texture2D", false);
+ RegisterResource (10402, "EscToExit_text.png", "Texture2D", false);
+ RegisterResource (10407, "UnityWaterMark-trial.png", "Texture2D", false);
+ RegisterResource (10408, "UnityWaterMark-beta.png", "Texture2D", false);
+ RegisterResource (10409, "UnityWaterMark-edu.png", "Texture2D", false);
+ RegisterResource (10410, "UnityWaterMark-dev.png", "Texture2D", false);
+ RegisterResource (10411, "WarningSign.psd", "Texture2D", false);
+ RegisterResource (10412, "UnityWatermark-DebugFlashPlayer.png", "Texture2D", false);
+ RegisterResource (10413, "UnityWaterMark-proto.png", "Texture2D", false);
+ RegisterResource (10414, "UnityWaterMarkPlugin-beta.png", "Texture2D", false);
+
+#if !UNITY_IPHONE
+ // Standalone
+ RegisterResource (10403, "UnitySplash.png", "Texture2D", false);
+ RegisterResource (10404, "UnitySplash2.png", "Texture2D", false);
+ RegisterResource (10405, "UnitySplash3.png", "Texture2D", false);
+ RegisterResource (10406, "UnitySplashBack.png", "Texture2D", false);
+#endif
+
+#if ENABLE_SPRITES
+ RegisterShader (10753, "Sprites-Default.shader", "Sprites/Default");
+ RegisterResource (10754, "Sprites-Default.mat", "Material");
+#endif
+
+ // Built-in GUISkin
+ RegisterResource (11000, "GameSkin/GameSkin.guiskin", "MonoBehaviour", false);
+ RegisterResource (11001, "GameSkin/box.png", "Texture2D", false);
+ RegisterResource (11002, "GameSkin/button active.png", "Texture2D", false);
+ RegisterResource (11003, "GameSkin/button hover.png", "Texture2D", false);
+ RegisterResource (11004, "GameSkin/button on hover.png", "Texture2D", false);
+ RegisterResource (11005, "GameSkin/button on.png", "Texture2D", false);
+ RegisterResource (11006, "GameSkin/button.png", "Texture2D", false);
+ RegisterResource (11007, "GameSkin/horizontal scrollbar thumb.png", "Texture2D", false);
+ RegisterResource (11008, "GameSkin/horizontal scrollbar.png", "Texture2D", false);
+ RegisterResource (11009, "GameSkin/horizontalslider.png", "Texture2D", false);
+ RegisterResource (11010, "GameSkin/slider thumb active.png", "Texture2D", false);
+ RegisterResource (11011, "GameSkin/slider thumb.png", "Texture2D", false);
+ RegisterResource (11012, "GameSkin/slidert humb hover.png", "Texture2D", false);
+ RegisterResource (11013, "GameSkin/toggle active.png", "Texture2D", false);
+ RegisterResource (11014, "GameSkin/toggle hover.png", "Texture2D", false);
+ RegisterResource (11015, "GameSkin/toggle on hover.png", "Texture2D", false);
+ RegisterResource (11016, "GameSkin/toggle on.png", "Texture2D", false);
+ RegisterResource (11017, "GameSkin/toggle on active.png", "Texture2D", false);
+ RegisterResource (11018, "GameSkin/toggle.png", "Texture2D", false);
+ RegisterResource (11019, "GameSkin/vertical scrollbar thumb.png", "Texture2D", false);
+ RegisterResource (11020, "GameSkin/vertical scrollbar.png", "Texture2D", false);
+ RegisterResource (11021, "GameSkin/verticalslider.png", "Texture2D", false);
+ RegisterResource (11022, "GameSkin/window on.png", "Texture2D", false);
+ RegisterResource (11023, "GameSkin/window.png", "Texture2D", false);
+ RegisterResource (11024, "GameSkin/textfield.png", "Texture2D", false);
+ RegisterResource (11025, "GameSkin/textfield on.png", "Texture2D", false);
+ RegisterResource (11026, "GameSkin/textfield hover.png", "Texture2D", false);
+
+ // 12000 range is used by scripts in RegisterBuiltinScripts
+ RegisterResource(11998, "DeveloperConsole", "MonoScript", false);
+ RegisterResource(11999, "UserAuthorizationDialog", "MonoScript", false);
+ RegisterResource(12000, "Terrain", "MonoScript", false);
+ RegisterResource(12001, "GUISkin", "MonoScript", false);
+
+ #if UNITY_LOGIC_GRAPH
+ // 130xx are for logic graph engine side scripts
+ RegisterResource(13003, "GraphBehaviour", "MonoScript", false);
+ RegisterResource(13006, "OnAnimationEventDummy", "MonoScript", false);
+ RegisterResource(13007, "OnMouseEventDummy", "MonoScript", false);
+ RegisterResource(13008, "OnTriggerEventDummy", "MonoScript", false);
+ RegisterResource(13009, "OnCollisionEventDummy", "MonoScript", false);
+ #endif
+
+ m_Resources.sort();
+
+}
+
+void BuiltinResourceManager::InitializeAllResources ()
+{
+#if WEBPLUG
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion_OldWebResourcesAdded))
+ {
+ GetBuiltinOldWebResourceManager().InitializeOldWebResources();
+ RemapShadersToOldWebResources();
+ }
+#endif
+
+#if UNITY_EDITOR
+
+ // Setup up path remapping for builtin resources
+ GetPersistentManager().SetPathRemap(kResourcePath, AppendPathName (GetApplicationContentsPath (), kDefaultResourcesSource));
+ GetPersistentManager().SetPathRemap(kEditorResourcePath, AppendPathName (GetApplicationContentsPath (), kEditorDefaultResourcesSource));
+ GetPersistentManager().SetPathRemap(kDefaultExtraResourcesPath, AppendPathName (GetApplicationContentsPath (), kDefaultExtraResourcesPath));
+
+ // Initialize resources
+ GetBuiltinResourceManager().InitializeResources();
+ GetBuiltinExtraResourceManager().InitializeExtraResources();
+ RemapShadersToDefaultExtraResources();
+#else
+ GetBuiltinResourceManager().InitializeResources();
+#endif
+
+ s_AreResourcesInitialized = true;
+}
+
+bool BuiltinResourceManager::AreResourcesInitialized()
+{
+ return s_AreResourcesInitialized;
+}
+
+
+#if UNITY_EDITOR
+
+// Disable builtin resource loading. This is useful to ensure there are no dependencies on builtin resources when building them.
+void BuiltinResourceManager::SetAllowLoadingBuiltinResources (bool allowLoadingBuiltinResources)
+{
+ GetBuiltinResourceManager().m_AllowResourceManagerAccess = allowLoadingBuiltinResources;
+ GetBuiltinOldWebResourceManager().m_AllowResourceManagerAccess = allowLoadingBuiltinResources;
+ GetBuiltinExtraResourceManager().m_AllowResourceManagerAccess = allowLoadingBuiltinResources;
+}
+
+
+void BuiltinResourceManager::RegisterBuiltinScriptsForBuilding ()
+{
+ // Regenerate scripts from scratch and serialize them
+ for (Resources::iterator i=m_Resources.begin ();i != m_Resources.end ();i++)
+ {
+ if (i->classID == ClassID (MonoScript))
+ {
+ string path = AppendPathName("GeneratedBuiltinScripts", i->name);
+ int oldID = GetPersistentManager().GetInstanceIDFromPathAndFileID(path, 1);
+ if (dynamic_instanceID_cast<MonoScript*>(oldID) == NULL)
+ {
+ MonoScript* script = NEW_OBJECT(MonoScript);
+ script->Reset();
+ script->Init("", i->name, "UnityEngine", "UnityEngine.dll", false);
+ script->SetName(i->name);
+ script->SetPathName(i->name);
+ GetPersistentManager().MakeObjectPersistentAtFileID(script->GetInstanceID(), 1, path);
+ script->AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+ }
+ }
+}
+
+static void RegisterGraphScript (int id, const char* className, const char* _namspace)
+{
+ DoRegisterBuiltinEditorScript(id, className, _namspace, "UnityEditor.Graphs.dll", true);
+}
+
+void GenerateEditorScripts ()
+{
+ RegisterBuiltinEditorScript(12002, "TerrainInspector");
+
+ RegisterBuiltinEditorScript(12003, "ConsoleWindow");
+ RegisterBuiltinEditorScript(12004, "ContainerWindow");
+ RegisterBuiltinEditorScript(12005, "GUIView");
+ RegisterBuiltinEditorScript(12006, "DockArea");
+ RegisterBuiltinEditorScript(12007, "EditorWindow");
+ RegisterBuiltinEditorScript(12008, "MainWindow");
+ RegisterBuiltinEditorScript(12009, "PaneDragTab");
+ RegisterBuiltinEditorScript(12010, "SplitView");
+ RegisterBuiltinEditorScript(12011, "Toolbar");
+ RegisterBuiltinEditorScript(12012, "Tools");
+ RegisterBuiltinEditorScript(12013, "SceneView");
+ RegisterBuiltinEditorScript(12014, "ProjectBrowser");
+ RegisterBuiltinEditorScript(12015, "GameView");
+ RegisterBuiltinEditorScript(12016, "PopupEditor");
+ RegisterBuiltinEditorScript(12017, "ColorPicker");
+ RegisterBuiltinEditorScript(12018, "AboutWindow");
+ RegisterBuiltinEditorScript(12019, "InspectorWindow");
+ RegisterBuiltinEditorScript(12020, "ShaderInspector");
+ //RegisterBuiltinEditorScript(12021, "ServerDemo"); // was used for testing only
+// RegisterBuiltinEditorScript(12022, "GUIDebugHelper");
+ //RegisterBuiltinEditorScript(12023, "ButtonsDemo"); // was used for testing only
+ RegisterBuiltinEditorScript(12024, "AssetImporterInspector");
+ RegisterBuiltinEditorScript(12025, "TextureImporterInspector");
+ RegisterBuiltinEditorScript(12026, "MovieImporterInspector");
+ RegisterBuiltinEditorScript(12027, "TransformInspector");
+ RegisterBuiltinEditorScript(12028, "ModelImporterEditor");
+ RegisterBuiltinEditorScript(12029, "AudioImporterInspector");
+ RegisterBuiltinEditorScript(12030, "TrueTypeFontImporterInspector");
+ RegisterBuiltinEditorScript(12031, "TextureInspector");
+ RegisterBuiltinEditorScript(12032, "GameObjectInspector");
+ RegisterBuiltinEditorScript(12033, "MaterialEditor");
+ RegisterBuiltinEditorScript(12034, "SaveWindowLayout");
+ RegisterBuiltinEditorScript(12035, "DeleteWindowLayout");
+ RegisterBuiltinEditorScript(12036, "RenderTextureInspector");
+ RegisterBuiltinEditorScript(12037, "ASMainWindow");
+ RegisterBuiltinEditorScript(12038, "CubemapTextureInspector");
+ RegisterBuiltinEditorScript(12039, "RagdollBuilder");
+ RegisterBuiltinEditorScript(12040, "GenericInspector");
+ RegisterBuiltinEditorScript(12041, "ProfilerIPWindow");
+ RegisterBuiltinEditorScript(12042, "AppStatusBar");
+ RegisterBuiltinEditorScript(12043, "BuildPlayerWindow");
+ RegisterBuiltinEditorScript(12044, "PreferencesWindow");
+ RegisterBuiltinEditorScript(12045, "Settings");
+ RegisterBuiltinEditorScript(12046, "SnapSettings");
+ RegisterBuiltinEditorScript(12047, "HostView");
+
+ RegisterBuiltinEditorScript(12048, "TerrainWizard");
+ RegisterBuiltinEditorScript(12049, "LightmapWizard");
+ RegisterBuiltinEditorScript(12050, "ImportRawHeightmap");
+ RegisterBuiltinEditorScript(12051, "ExportRawHeightmap");
+ RegisterBuiltinEditorScript(12052, "SetResolutionWizard");
+ RegisterBuiltinEditorScript(12053, "TreeWizard");
+ RegisterBuiltinEditorScript(12054, "SplatWizard");
+ RegisterBuiltinEditorScript(12055, "DetailMeshWizard");
+ RegisterBuiltinEditorScript(12056, "DetailTextureWizard");
+ RegisterBuiltinEditorScript(12057, "PlaceTreeWizard");
+ RegisterBuiltinEditorScript(12058, "FlattenHeightmap");
+
+ RegisterBuiltinEditorScript(12059, "FallbackEditorWindow");
+ RegisterBuiltinEditorScript(12060, "MaximizedHostView");
+ RegisterBuiltinEditorScript(12061, "HierarchyWindow");
+ RegisterBuiltinEditorScript(12062, "PackageExport");
+ RegisterBuiltinEditorScript(12063, "PackageImport");
+ RegisterBuiltinEditorScript(12064, "EyeDropper");
+ RegisterBuiltinEditorScript(12065, "WelcomeScreen");
+ RegisterBuiltinEditorScript(12066, "MonoScriptInspector");
+ RegisterBuiltinEditorScript(12067, "AudioClipInspector");
+ RegisterBuiltinEditorScript(12068, "ModelInspector");
+ RegisterBuiltinEditorScript(12069, "TooltipView");
+ RegisterBuiltinEditorScript(12070, "ProfilerWindow");
+ RegisterBuiltinEditorScript(12071, "AnimationWindow");
+ RegisterBuiltinEditorScript(12072, "ASHistoryWindow");
+ RegisterBuiltinEditorScript(12073, "AnimationEventPopup");
+ RegisterBuiltinEditorScript(12074, "EditorSettingsInspector");
+ RegisterBuiltinEditorScript(12075, "AssetInspector");
+ RegisterBuiltinEditorScript(12076, "AnimationCleanupPopup");
+ RegisterBuiltinEditorScript(12077, "ScriptReloadProperties");
+ RegisterBuiltinEditorInternalScript(12078, "MacroWindow");
+ RegisterBuiltinEditorScript(12079, "LightmappingWindow");
+ RegisterBuiltinEditorScript(12080, "AuxWindow");
+ RegisterBuiltinEditorScript(12081, "ObjectSelector");
+ RegisterBuiltinEditorScript(12082, "LabelCompletion");
+ RegisterBuiltinEditorScript(12083, "CurveEditorWindow");
+ RegisterBuiltinEditorScript(12084, "MonoScriptImporterInspector");
+ RegisterBuiltinEditorScript(12085, "MovieTextureInspector");
+ RegisterBuiltinEditorScript(12086, "TextAssetInspector");
+ RegisterBuiltinEditorScript(12087, "LightEditor");
+ RegisterBuiltinEditorScript(12088, "AudioSourceInspector");
+ RegisterBuiltinEditorScript(12089, "AudioLowPassFilterInspector");
+ RegisterBuiltinEditorScript(12090, "OcclusionCullingWindow");
+ RegisterBuiltinEditorScript(12091, "WebCamTextureInspector");
+ RegisterBuiltinEditorScript(12092, "ShaderImporterInspector");
+
+ RegisterBuiltinEditorScript(12099, "AudioReverbZoneEditor");
+ RegisterBuiltinEditorScript(12100, "PhysicsManagerInspector");
+
+ RegisterBuiltinEditorScript(12101, "ClothInspector");
+ RegisterBuiltinEditorScript(12102, "BumpMapSettingsFixingWindow");
+ RegisterBuiltinEditorScript(12103, "PragmaFixingWindow");
+ RegisterBuiltinEditorScript(12104, "WindInspector");
+
+ DoRegisterBuiltinEditorScript (12105, "TreeEditor", "TreeEditor", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript (12106, "TreeData", "TreeEditor", "UnityEditor.dll", true);
+
+ RegisterBuiltinEditorScript(12107, "PlayerSettingsEditor");
+ RegisterBuiltinEditorScript(12108, "EditorUpdateWindow");
+
+ RegisterBuiltinEditorScript(12109, "AudioReverbFilterEditor");
+
+ RegisterBuiltinEditorScript(12110, "WebView");
+ RegisterBuiltinEditorScript(12111, "AssetStoreWindow");
+ RegisterBuiltinEditorScript(12112, "AssetStoreContext");
+
+ RegisterBuiltinEditorScript(12113, "AssembleEditorSkin");
+ RegisterBuiltinEditorScript(12114, "OcclusionAreaEditor");
+ RegisterBuiltinEditorScript(12115, "CameraEditor");
+
+ RegisterBuiltinEditorScript(12116, "WindowFocusState");
+
+ RegisterBuiltinEditorScript(12117, "AndroidKeystoreWindow");
+
+ RegisterBuiltinEditorScript(12118, "AudioDistortionFilterEditor");
+ RegisterBuiltinEditorScript(12119, "AudioEchoFilterEditor");
+ RegisterBuiltinEditorScript(12120, "AudioHighPassFilterEditor");
+ RegisterBuiltinEditorScript(12121, "AudioChorusFilterEditor");
+
+ RegisterBuiltinEditorScript(12122, "BoxColliderEditor");
+ RegisterBuiltinEditorScript(12123, "SphereColliderEditor");
+ RegisterBuiltinEditorScript(12124, "CapsuleColliderEditor");
+ RegisterBuiltinEditorScript(12125, "MeshColliderEditor");
+ RegisterBuiltinEditorScript(12126, "WheelColliderEditor");
+ RegisterBuiltinEditorScript(12127, "RigidbodyEditor");
+
+ RegisterBuiltinEditorScript(12128, "AssetSaveDialog");
+
+ RegisterBuiltinEditorScript(12129, "AnnotationWindow");
+ RegisterBuiltinEditorScript(12130, "IconSelector");
+ RegisterBuiltinEditorScript(12131, "ScriptExecutionOrderInspector");
+
+ RegisterBuiltinEditorScript(12132, "AnimationEditor");
+ RegisterBuiltinEditorScript(12133, "SkinnedMeshRendererEditor");
+ RegisterBuiltinEditorScript(12134, "PreviewWindow");
+ RegisterBuiltinEditorScript(12135, "LODGroupEditor");
+
+ RegisterBuiltinEditorScript(12136, "OffMeshLinkInspector");
+ RegisterBuiltinEditorScript(12137, "ParticleSystemWindow");
+ RegisterBuiltinEditorScript(12138, "ParticleSystemInspector");
+ RegisterBuiltinEditorScript(12139, "MeshRendererEditor");
+
+ RegisterBuiltinEditorScript(12140, "GradientPicker");
+
+ RegisterBuiltinEditorScript(12141, "NavMeshEditorWindow");
+ RegisterBuiltinEditorScript(12142, "FontInspector");
+
+ RegisterBuiltinEditorScript(12143, "BuildUploadCompletedWindow");
+ RegisterBuiltinEditorScript(12144, "UploadingBuildsMonitor");
+
+ RegisterBuiltinEditorScript(12145, "TerrainSplatEditor");
+
+ #if ENABLE_SPRITES
+ RegisterBuiltinEditorScript(12150, "SpriteRendererInspector");
+ RegisterBuiltinEditorScript(12151, "SpriteInspector");
+ #endif
+
+ RegisterBuiltinEditorScript(12153, "TagManagerInspector");
+
+ RegisterBuiltinEditorScript(12200, "SubstanceImporterInspector");
+ // RegisterBuiltinEditorScript(12201, "SubstanceArchiveInspector");
+ RegisterBuiltinEditorScript(12202, "ProceduralMaterialInspector");
+ RegisterBuiltinEditorScript(12203, "ProceduralTextureInspector");
+ RegisterBuiltinEditorScript(12204, "NavMeshAgentInspector");
+ RegisterBuiltinEditorScript(12205, "LightProbeGroupInspector");
+ RegisterBuiltinEditorScript(12206, "LightProbeGroupSelection");
+ RegisterBuiltinEditorScript(12207, "View");
+ RegisterBuiltinEditorScript(12208, "OcclusionPortalInspector");
+ RegisterBuiltinEditorScript(12209, "NavMeshObstacleInspector");
+
+ RegisterBuiltinEditorScript(12210, "PopupList");
+
+ RegisterBuiltinEditorScript(12211, "AssetStoreAssetInspector");
+ RegisterBuiltinEditorScript(12212, "AssetStoreInstaBuyWindow");
+ RegisterBuiltinEditorScript(12213, "AssetStoreLoginWindow");
+
+ DoRegisterBuiltinEditorScript(12216, "EndNameEditAction", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12217, "DoCreateNoneAsset", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12218, "DoCreateNewAsset", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12219, "DoCreateFolder", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12220, "DoCreatePrefab", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12221, "DoCreateScriptAsset", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12223, "DoCreateAnimatorController", "UnityEditor.ProjectWindowCallback", "UnityEditor.dll", true);
+
+ RegisterBuiltinEditorScript(12214, "LightProbesInspector");
+
+ RegisterBuiltinEditorScript(12215, "AddComponentWindow");
+
+ RegisterBuiltinEditorScript(12222, "LicenseManagementWindow");
+
+ DoRegisterBuiltinEditorScript(12300, "WindowResolve", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12301, "WindowChange", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12303, "WindowPending", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+ DoRegisterBuiltinEditorScript(12304, "WindowRevert", "UnityEditor.VersionControl", "UnityEditor.dll", true);
+
+ RegisterBuiltinEditorScript(12306, "CharacterControllerEditor");
+
+ RegisterBuiltinEditorScript(12307, "QualitySettingsEditor");
+ RegisterBuiltinEditorScript(12308, "SavedSearchFilters");
+ // RegisterBuiltinEditorScript(12309, ""); // Unused
+ RegisterBuiltinEditorScript(12310, "TextMeshInspector");
+ RegisterBuiltinEditorScript(12311, "Texture3DInspector");
+
+ //RegisterBuiltinEditorScript(12312, "MetroCreateTestCertificateWindow");
+ //RegisterBuiltinEditorScript(12313, "MetroCertificatePasswordWindow");
+
+ RegisterBuiltinEditorScript(12314, "ModelImporterRigEditor");
+ RegisterBuiltinEditorScript(12315, "ModelImporterModelEditor");
+ RegisterBuiltinEditorScript(12316, "ModelImporterClipEditor");
+
+ RegisterBuiltinEditorScript(12318, "AvatarPreviewSelection");
+ RegisterBuiltinEditorScript(12319, "PopupWindow");
+
+ RegisterBuiltinEditorScript(12320, "PresetLibraryManager");
+ RegisterBuiltinEditorScript(12321, "GradientPresetLibrary");
+ RegisterBuiltinEditorScript(12322, "CurvePresetLibrary");
+ RegisterBuiltinEditorScript(12323, "ColorPresetLibrary");
+ RegisterBuiltinEditorScript(12324, "DoubleCurvePresetLibrary");
+
+ #if ENABLE_SPRITES
+ RegisterBuiltinEditorScript(12325, "AtlasEditorWindow");
+ RegisterBuiltinEditorScript(12326, "SpritePackerWindow");
+ #endif
+
+ #if ENABLE_2D_PHYSICS
+ RegisterBuiltinEditorScript(12327, "PolygonColliderEditor");
+ RegisterBuiltinEditorScript(12329, "Physics2DSettingsInspector");
+ #endif
+
+ RegisterBuiltinEditorScript(12330, "GameViewSizes");
+
+ RegisterBuiltinEditorScript(12331, "ColorPresetLibraryEditor");
+ RegisterBuiltinEditorScript(12332, "GradientPresetLibraryEditor");
+ RegisterBuiltinEditorScript(12333, "CurvePresetLibraryEditor");
+ RegisterBuiltinEditorScript(12334, "DoubleCurvePresetLibraryEditor");
+
+ RegisterBuiltinEditorScript(12340, "BB10SigningAuthorityWindow");
+ RegisterBuiltinEditorScript(12341, "BB10UnregisterWarningWindow");
+
+ RegisterBuiltinEditorScript(12342, "TizenSigningAuthorityWindow");
+ RegisterBuiltinEditorScript(12343, "TizenUnregisterWarningWindow");
+
+ #if ENABLE_2D_PHYSICS
+ RegisterBuiltinEditorScript(12350, "BoxCollider2DEditor");
+ RegisterBuiltinEditorScript(12351, "CircleCollider2DEditor");
+ RegisterBuiltinEditorScript(12352, "EdgeCollider2DEditor");
+ #endif
+
+
+ // 12410-12449 are for Toolbar & GUI Editor
+
+ #if UNITY_LOGIC_GRAPH
+ // 131xx are for logic graph editor side scripts
+ RegisterGraphScript(13100, "FlowWindow", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13101, "LogicGraph", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13102, "LogicGraphGUI", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13103, "FunctionNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13104, "VariableNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13105, "FunctionNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13106, "VariableNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13107, "ClassNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13108, "ExpressionNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13109, "ExpressionNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13110, "CompareNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13111, "CompareNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13112, "IfNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13113, "IfNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13114, "RandomNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13115, "RandomNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13116, "ClassNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13117, "EventNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13118, "EventNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13119, "SetPropertyNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13120, "SetPropertyNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13121, "GetPropertyNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13122, "GetPropertyNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13123, "OnAnimationEventNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13124, "OnAnimationEventNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13125, "ExternalCallReceiver", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13126, "ExternalCallReceiverEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13127, "ExternalCallSender", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13128, "ExternalCallSenderEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13129, "EvalNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13130, "EvalNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13131, "StructExpressionNode", kLogicGraphEditorNamespace);
+
+ RegisterGraphScript(13132, "StructExpressionNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13133, "ToggleNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13134, "ToggleNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13135, "GetComponentNode", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13136, "GetComponentNodeEditor", kLogicGraphEditorNamespace);
+ RegisterGraphScript(13137, "GraphBehaviourInspector", kLogicGraphEditorNamespace);
+ #endif
+
+ RegisterGraphScript(13138, "Graph", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13139, "GraphInspector", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13140, "GraphGUI", kGraphsEditorBaseNamespace);
+ RegisterGraphScript(13141, "Node", kGraphsEditorBaseNamespace);
+ RegisterBuiltinEditorScript(12901, "AnimatorInspector");
+ RegisterBuiltinEditorScript(12902, "AnimatorOverrideControllerInspector");
+ RegisterBuiltinEditorScript(12905, "AnimationClipEditor");
+ RegisterBuiltinEditorScript(12906, "AvatarEditor");
+ RegisterGraphScript(12907, "TransitionInspector", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12914, "AnimatorControllerTool", kGraphsEditorBaseNamespace);
+ RegisterBuiltinEditorScript(12915, "AvatarMaskInspector");
+ RegisterBuiltinEditorScript(12916, "BlendTreeInspector");
+
+ RegisterBuiltinEditorScript(12917, "AvatarMappingEditor");
+ RegisterBuiltinEditorScript(12918, "AvatarMuscleEditor");
+
+ RegisterGraphScript(12919, "GraphGUI", kAnimationBlendTreeNamespace);
+ RegisterGraphScript(12920, "Graph", kAnimationBlendTreeNamespace);
+ RegisterGraphScript(12921, "Node", kAnimationBlendTreeNamespace);
+
+ RegisterGraphScript(12930, "GraphGUI", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12931, "Graph", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12932, "StateNode", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12933, "AnyStateNode", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12934, "StateMachineNode", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12935, "StateEditor", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12936, "StateMachineInspector", kAnimationStateMachineNamespace);
+ RegisterGraphScript(12937, "AnyStateNodeInspector", kAnimationStateMachineNamespace);
+
+ RegisterBuiltinEditorScript(13201, "HeapshotWindow");
+}
+
+static void DoRegisterBuiltinEditorScript (int fileID, const char* className, const char* _namespace, const char* assemblyName, bool isEditorScript)
+{
+#if ENABLE_MONO
+ MonoScript* script = NEW_OBJECT(MonoScript);
+ script->Reset();
+ script->Init("", className, _namespace, assemblyName, isEditorScript);
+ script->SetName(className);
+ script->SetPathName(className);
+
+ SInt32 oldInstanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID(kResourcePath, fileID);
+ if (Object::IDToPointer(oldInstanceID) != NULL)
+ {
+ ErrorString("Duplicate editor builtin script " + string(className));
+ }
+
+ GetPersistentManager().MakeObjectPersistentAtFileID(script->GetInstanceID(), fileID, kResourcePath);
+ script->SetHideFlags(Object::kDontSave);
+
+ if (isEditorScript)
+ GetMonoScriptManager().RegisterEditorScript(*script);
+ else
+ GetMonoScriptManager().RegisterRuntimeScript(*script);
+ script->AwakeFromLoad(kDefaultAwakeFromLoad);
+
+#endif
+}
+
+static void RegisterBuiltinEditorScript (int instanceID, const char* className)
+{
+ DoRegisterBuiltinEditorScript (instanceID, className, "UnityEditor", "UnityEditor.dll", true);
+}
+
+static void RegisterBuiltinEditorInternalScript (int instanceID, const char* className)
+{
+ DoRegisterBuiltinEditorScript (instanceID, className, "UnityEditorInternal", "UnityEditor.dll", true);
+}
+
+#if ENABLE_UNIT_TESTS
+void BuiltinResourceManager::RegisterShadersWithRegistry (ScriptMapper* registry)
+{
+ for (OrderedShaderStartup::iterator i = m_OrderedShaderStartup.begin ();
+ i != m_OrderedShaderStartup.end (); i++)
+ {
+ const ShaderInitializationData& data = *i;
+ registry->AddBuiltinShader (data.shaderClassName, PPtr<Shader> (data.cachedInstanceID));
+ }
+}
+#endif
+
+void BuiltinResourceManager::LoadDefaultResourcesFromEditor ()
+{
+ if (!m_AllowResourceManagerAccess)
+ return;
+
+ PersistentManager& pm = GetPersistentManager ();
+
+ // They need to show up in the script mapper
+ for (OrderedShaderStartup::iterator i=m_OrderedShaderStartup.begin ();i != m_OrderedShaderStartup.end ();i++)
+ {
+ const ShaderInitializationData& data = *i;
+ GetScriptMapper ().AddBuiltinShader (data.shaderClassName, PPtr<Shader>(data.cachedInstanceID));
+
+ void AddBuiltinShaderInfoForMenu (const std::string& name, SInt32 id);
+ AddBuiltinShaderInfoForMenu (data.shaderClassName, data.cachedInstanceID);
+ }
+
+ // In non-release builds, verify that the setup of all shaders is correct and no shaders have errors.
+ #if !UNITY_RELEASE
+ for (OrderedShaderStartup::iterator i=m_OrderedShaderStartup.begin ();i != m_OrderedShaderStartup.end ();i++)
+ {
+ Shader* shader = dynamic_pptr_cast<Shader*> (GetResource (ClassID (Shader), i->resourceName));
+ Assert(shader != NULL);
+ if (shader)
+ {
+ if (shader->GetScriptClassName() != i->shaderClassName)
+ {
+ AssertString("Shadername is: " + shader->GetScriptClassName() + " but hardcoded name was: " + i->shaderClassName);
+ }
+
+ Assert(shader->GetHideFlags() == m_RequiredHideFlags);
+ if (shader->GetErrors().HasErrorsOrWarnings())
+ shader->GetErrors().LogErrors(shader->GetScriptClassName().c_str(), "BUILTIN SHADER", shader, shader->GetInstanceID());
+ }
+ }
+ #endif
+
+ /////@TODO: Verify that all assets in the builtin resource file are actually being registered!
+
+ // Scripts need to register with mono manager
+ for (Resources::iterator i=m_Resources.begin ();i != m_Resources.end ();i++)
+ {
+ if (i->classID == ClassID (MonoScript))
+ {
+ int instanceID = pm.GetInstanceIDFromPathAndFileID (m_ResourcePath, i->fileID);
+ MonoScript* script = dynamic_instanceID_cast<MonoScript*> (instanceID);
+ if (script)
+ GetMonoScriptManager().RegisterRuntimeScript(*script);
+ else
+ Assert("Script can't be found");
+ }
+ }
+}
+
+
+void BuiltinResourceManager::LoadAllDefaultResourcesFromEditor ()
+{
+ GetBuiltinResourceManager().LoadDefaultResourcesFromEditor();
+ GetBuiltinExtraResourceManager().LoadDefaultResourcesFromEditor();
+
+ // Editor scripts need to be generated and registered
+ GenerateEditorScripts ();
+}
+#endif
+
+
+vector<Object*> BuiltinResourceManager::GetAllResources ()
+{
+ vector<Object*> result;
+ result.reserve (m_Resources.size ());
+
+ for (Resources::const_iterator iter = m_Resources.begin (); iter != m_Resources.end (); ++iter)
+ result.push_back (GetResource (iter->classID, iter->name));
+
+ return result;
+}
+
+// Returns the first found resource with name that is derived from classID
+Object* BuiltinResourceManager::GetResource (int classID, const string& name)
+{
+ if (!m_AllowResourceManagerAccess)
+ {
+ bool mustLoadBuiltinShader =
+ name == "Internal-Halo.shader" ||
+ name == "Internal-GUITexture.shader" ||
+ name == "Normal-Diffuse.shader";
+ // Allow for some specific exceptions!
+ if (!mustLoadBuiltinShader)
+ {
+ AssertString("Loading builtin resources is forbidden when building them: " + name + "\nIf it is really necessary you can add a exception case at this assert.");
+ return NULL;
+ }
+ }
+
+ Resource proxy;
+ proxy.classID = classID;
+ proxy.name = name.c_str();
+
+ Resources::iterator found = m_Resources.find (proxy);
+ if (found == m_Resources.end())
+ {
+ AssertString("Failed to find " + name);
+ return NULL;
+ }
+
+ AssertIf (found->cachedInstanceID == 0);
+ Object* ptr = PPtr<Object> (found->cachedInstanceID);
+ if (ptr == NULL || !ptr->IsDerivedFrom (classID))
+ {
+ ErrorString ("The resource " + name + " could not be loaded from the resource file!");
+ return NULL;
+ }
+ else
+ {
+ AssertIf(ptr->GetHideFlags() != m_RequiredHideFlags);
+ return ptr;
+ }
+}
+
+void BuiltinResourceManager::RegisterShader (LocalIdentifierInFileType fileID, const char* name, const char* shaderClassName, bool userVisible)
+{
+#if UNITY_EDITOR
+ int cachedInstanceID =
+#endif
+ RegisterResourceInternal(fileID, name, "Shader", shaderClassName, userVisible);
+
+#if UNITY_EDITOR
+ // Register shader initialization
+ ShaderInitializationData data;
+ data.shaderClassName = shaderClassName;
+ data.resourceName = name;
+ data.cachedInstanceID = cachedInstanceID;
+ m_OrderedShaderStartup.push_back(data);
+#endif
+}
+
+int BuiltinResourceManager::RegisterResourceInternal (LocalIdentifierInFileType fileID, const char* name, const char* className, const char* displayName, bool userVisible)
+{
+ RegisterDebugUsedFileID (fileID, name);
+
+ Resource r;
+ r.name = name;
+ r.classID = Object::StringToClassID (className);
+ r.fileID = fileID;
+ r.cachedInstanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID(m_ResourcePath, fileID);
+ r.userVisible = userVisible;
+ #if UNITY_EDITOR
+ if (displayName != NULL)
+ r.cachedDisplayName = displayName;
+ #endif
+ m_Resources.push_unsorted (r);
+
+ return r.cachedInstanceID;
+}
+
+void BuiltinResourceManager::RegisterResource (LocalIdentifierInFileType fileID, const char* name, const char* className, bool userVisible)
+{
+ // RegisterResource can not be used on shaders, use RegisterShader
+ Assert(strcmp(className, "Shader") != 0);
+
+ RegisterResourceInternal(fileID, name, className, NULL, userVisible);
+}
+
+static BuiltinResourceManager* gBuiltinResourceManager;
+static BuiltinResourceManager* gBuiltinOldWebResourceManager;
+
+
+void BuiltinResourceManager::StaticInitialize()
+{
+ gBuiltinResourceManager = UNITY_NEW(BuiltinResourceManager,kMemResource);
+ gBuiltinOldWebResourceManager = UNITY_NEW(BuiltinResourceManager,kMemResource);
+
+#if UNITY_EDITOR
+ gBuiltinExtraResourceManager = UNITY_NEW(BuiltinResourceManager,kMemResource);
+#endif
+
+ s_AreResourcesInitialized = false;
+}
+
+void BuiltinResourceManager::StaticDestroy()
+{
+ s_AreResourcesInitialized = false;
+
+ UNITY_DELETE(gBuiltinResourceManager,kMemResource);
+ UNITY_DELETE(gBuiltinOldWebResourceManager,kMemResource);
+#if UNITY_EDITOR
+ UNITY_DELETE(gBuiltinExtraResourceManager,kMemResource);
+#endif
+}
+
+static RegisterRuntimeInitializeAndCleanup s_BuiltinResourceManagerCallbacks(BuiltinResourceManager::StaticInitialize, BuiltinResourceManager::StaticDestroy);
+
+BuiltinResourceManager& GetBuiltinResourceManager ()
+{
+ return *gBuiltinResourceManager;
+}
+
+BuiltinResourceManager& GetBuiltinOldWebResourceManager ()
+{
+ return *gBuiltinOldWebResourceManager;
+}
+
+
+void BuiltinResourceManager::DestroyAllResources()
+{
+ for (Resources::iterator i=m_Resources.begin ();i != m_Resources.end ();i++)
+ {
+ Object* obj = Object::IDToPointer (i->cachedInstanceID);
+ if(obj)
+ {
+ DestroySingleObject(obj);
+ }
+ }
+ m_Resources.clear();
+}
+
+#if DEBUGMODE
+void BuiltinResourceManager::RegisterDebugUsedFileID (LocalIdentifierInFileType fileID, const char* name)
+{
+ if (!m_UsedFileIDs.insert (fileID).second)
+ ErrorString (Format ("File ID %d is registered twice (%s)", (int)fileID, name));
+}
+#endif
+
+#if UNITY_EDITOR
+
+BuiltinResourceManager& GetBuiltinExtraResourceManager ()
+{
+ return *gBuiltinExtraResourceManager;
+}
+
+#endif // #if UNITY_EDITOR
+
+ResourceManager::ResourceManager (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_NeedsReload = true;
+}
+
+ResourceManager::~ResourceManager ()
+{
+}
+
+ResourceManager::range ResourceManager::GetAll ()
+{
+ #if UNITY_EDITOR
+ if (m_NeedsReload)
+ RebuildResources();
+ #endif
+
+ return make_pair (m_Container.begin(), m_Container.end());
+}
+
+ResourceManager::range ResourceManager::GetPathRange (const string& path)
+{
+ #if UNITY_EDITOR
+ if (m_NeedsReload)
+ RebuildResources();
+ #endif
+
+ return m_Container.equal_range(ToLower(path));
+}
+
+#if UNITY_EDITOR
+void ResourceManager::RebuildResources ()
+{
+ m_NeedsReload = !AssetDatabase::Get().GetAllResources(m_Container);
+}
+#endif
+
+void ResourceManager::ClearDependencyInfo ()
+{
+ m_DependentAssets.clear ();
+}
+
+void ResourceManager::PreloadDependencies (SInt32 instanceId)
+{
+ std::set<SInt32> parents;
+ PreloadDependencies (instanceId, parents);
+}
+
+void ResourceManager::PreloadDependencies (SInt32 instanceId, std::set<SInt32>& visited)
+{
+ if (visited.find (instanceId) != visited.end ())
+ return;
+
+ DependencyContainer::iterator it = std::lower_bound (m_DependentAssets.begin (), m_DependentAssets.end (), instanceId, ResourceManager::Dependency::Sorter ());
+ // Check if we actually found info for the object we're looking for. As we're using lower_bound, we might get back
+ // another object that is closest smallest.
+ if (it == m_DependentAssets.end () || it->object.GetInstanceID () != instanceId)
+ return;
+
+ visited.insert (it->object.GetInstanceID ());
+
+ for (size_t i = 0; i<it->dependencies.size (); ++i)
+ {
+ PPtr<Object> pptr = it->dependencies[i];
+ *pptr; // fetch
+ PreloadDependencies (pptr.GetInstanceID (), visited);
+ }
+}
+
+template<class TransferFunc>
+void ResourceManager::Transfer (TransferFunc& transfer)
+{
+ Super::Transfer (transfer);
+ AssertIf (transfer.GetFlags() & kPerformUnloadDependencyTracking);
+
+ transfer.Transfer(m_Container, "m_Container");
+
+ // Wee need dependent-assets only in the player
+ if (transfer.IsSerializingForGameRelease ())
+ transfer.Transfer(m_DependentAssets, "m_DependentAssets");
+
+ if (transfer.IsReading ())
+ std::sort (m_DependentAssets.begin (), m_DependentAssets.end (), ResourceManager::Dependency::Sorter ());
+}
+
+bool ResourceManager::ShouldIgnoreInGarbageDependencyTracking ()
+{
+ return true;
+}
+
+IMPLEMENT_CLASS (ResourceManager)
+IMPLEMENT_OBJECT_SERIALIZE (ResourceManager)
+GET_MANAGER (ResourceManager)
+
diff --git a/Runtime/Misc/ResourceManager.h b/Runtime/Misc/ResourceManager.h
new file mode 100644
index 0000000..75b3c01
--- /dev/null
+++ b/Runtime/Misc/ResourceManager.h
@@ -0,0 +1,224 @@
+#ifndef RESOURCEMANAGER_H
+#define RESOURCEMANAGER_H
+
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Misc/Allocator.h"
+#include <list>
+#include "Runtime/Utilities/vector_set.h"
+#include <string>
+#include "Configuration/UnityConfigure.h"
+
+#define kDefaultFontName "Arial.ttf"
+
+class UnityWebStream;
+class CachedUnityWebStream;
+class ScriptMapper;
+
+/*
+ The resource manager can lookup assets by name and classID.
+
+ An resource file is generated with the editor. And is shipped with unity.
+ It provides the most basic assets necessary to run unity.
+ Eg. some shaders, some textures for lighting
+
+ If you want to add a new resource to the ResourceManager:
+ - Call RegisterResource inside InitializeResources (Use a unique id and never reuse an id)
+ - Open a project with the resources you want to add
+ All resources have to be placed inside a "Assets/DefaultResources"
+ - When you have assembled all the resources. Use BuildResources from the main menu.
+ This will Install them in the right folder and also
+ invoke installframeworks so it is installed in all versions of the editor
+*/
+
+class BuiltinResourceManager
+{
+ public:
+
+ static void StaticInitialize();
+ static void StaticDestroy();
+
+ static void InitializeAllResources ();
+ static bool AreResourcesInitialized();
+ void DestroyAllResources ();
+
+ void InitializeOldWebResources ();
+
+ #if UNITY_EDITOR
+ static void SetAllowLoadingBuiltinResources (bool allowLoadingBuiltinResources);
+ void RegisterBuiltinScriptsForBuilding ();
+ static void LoadAllDefaultResourcesFromEditor ();
+ static void GetBuiltinResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources);
+ #endif
+
+ // Returns the first found resource with name that is derived from classID
+ Object* GetResource (int classID, const std::string& name);
+
+ std::vector<Object*> GetAllResources ();
+
+ int GetRequiredHideFlags() const { return m_RequiredHideFlags; }
+
+ #if ENABLE_UNIT_TESTS
+ void RegisterShadersWithRegistry (ScriptMapper* registry);
+ #endif
+
+private:
+
+ void RegisterResource (LocalIdentifierInFileType fileID, const char* name, const char* className, bool userVisible = true);
+ int RegisterResourceInternal (LocalIdentifierInFileType fileID, const char* name, const char* className, const char* displayName, bool userVisible = true);
+
+ void RegisterShader (LocalIdentifierInFileType fileID, const char* name, const char* shaderClassName, bool userVisible = true);
+
+ void RegisterBuiltinScript (int instanceID, const char* className, bool buildResourceFiles);
+
+ void InitializeResources ();
+ void InitializeExtraResources ();
+ void LoadDefaultResourcesFromEditor ();
+
+ struct Resource
+ {
+ const char* name;
+ int classID;
+
+ LocalIdentifierInFileType fileID;
+ int cachedInstanceID;
+ bool userVisible;
+ #if UNITY_EDITOR
+ std::string cachedDisplayName;
+ #endif
+
+ friend bool operator < (const Resource& lhs, const Resource& rhs)
+ {
+ int res = strcmp(lhs.name, rhs.name);
+ if (res != 0)
+ return res < 0;
+
+ return lhs.classID < rhs.classID;
+ }
+ };
+
+ typedef vector_set<Resource> Resources;
+ Resources m_Resources;
+ std::string m_ResourcePath;
+ int m_RequiredHideFlags;
+ bool m_AllowResourceManagerAccess;
+
+ #if UNITY_EDITOR
+
+ struct ShaderInitializationData
+ {
+ const char* shaderClassName;
+ const char* resourceName;
+ int cachedInstanceID;
+ };
+
+ typedef std::vector<ShaderInitializationData> OrderedShaderStartup;
+ OrderedShaderStartup m_OrderedShaderStartup;
+
+ friend bool BuildBuiltinAssetBundle (BuiltinResourceManager& resourceManager, const std::string& targetFile, const std::string& resourceFolder, InstanceIDResolveCallback* resolveCallback, const std::set<std::string>& ignoreList, BuildTargetSelection platform, int options);
+ friend bool GenerateBuiltinAssetPreviews ();
+ void GetResourcesOfClass (int classID, std::vector< std::pair<std::string,int> >& outResources);
+ #endif
+
+ #if DEBUGMODE
+ void RegisterDebugUsedFileID (LocalIdentifierInFileType fileID, const char* name);
+ std::set<LocalIdentifierInFileType, std::less<LocalIdentifierInFileType>, STL_ALLOCATOR(kMemPermanent, LocalIdentifierInFileType) > m_UsedFileIDs;
+ #else
+ void RegisterDebugUsedFileID (LocalIdentifierInFileType fileID, const char* name) {}
+ #endif
+};
+
+BuiltinResourceManager& GetBuiltinResourceManager ();
+BuiltinResourceManager& GetBuiltinExtraResourceManager ();
+BuiltinResourceManager& GetBuiltinOldWebResourceManager ();
+
+template<class T>
+T* GetBuiltinResource (const std::string& name)
+{
+ Object* res = GetBuiltinResourceManager ().GetResource (T::GetClassIDStatic (), name);
+ return static_cast<T*> (res);
+}
+
+#if WEBPLUG
+template<class T>
+T* GetBuiltinOldWebResource (const std::string& name)
+{
+ Object* res = GetBuiltinOldWebResourceManager ().GetResource (T::GetClassIDStatic (), name);
+ return static_cast<T*> (res);
+}
+#endif // #if WEBPLUG
+
+#if UNITY_EDITOR
+template<class T>
+T* GetBuiltinExtraResource (const std::string& name)
+{
+ Object* res = GetBuiltinExtraResourceManager ().GetResource (T::GetClassIDStatic (), name);
+ return static_cast<T*> (res);
+}
+#endif // #if UNITY_EDITOR
+
+
+class ResourceManager : public GlobalGameManager
+{
+ public:
+
+ struct Dependency
+ {
+ typedef std::vector<PPtr<Object> > ChildCont;
+ DECLARE_SERIALIZE (ResourceManager_Dependency)
+
+ struct Sorter
+ {
+ bool operator() (Dependency const& a, Dependency const& b) const { return a.object.GetInstanceID () < b.object.GetInstanceID (); }
+ bool operator() (Dependency const& a, SInt32 b) const { return a.object.GetInstanceID () < b; }
+ bool operator() (SInt32 a, Dependency const& b) const { return a < b.object.GetInstanceID (); }
+ };
+
+ Dependency () {}
+ explicit Dependency (SInt32 thisInstanceId) : object (thisInstanceId) {}
+
+ PPtr<Object> object;
+ ChildCont dependencies;
+ };
+
+ typedef std::vector<Dependency> DependencyContainer;
+
+ DECLARE_OBJECT_SERIALIZE (ResourceManager)
+ REGISTER_DERIVED_CLASS (ResourceManager, GlobalGameManager)
+
+ ResourceManager (MemLabelId label, ObjectCreationMode mode);
+
+ typedef std::multimap<UnityStr, PPtr<Object> > container;
+ typedef std::multimap<UnityStr, PPtr<Object> >::iterator iterator;
+ typedef std::pair<iterator, iterator> range;
+
+ void RegisterResource (std::string& path, PPtr<Object> resource);
+ std::string GetResourcePath (const std::string& path);
+
+ range GetAll ();
+ range GetPathRange (const string& path);
+
+ void SetResourcesNeedRebuild() { m_NeedsReload = true; }
+ bool ShouldIgnoreInGarbageDependencyTracking ();
+
+ void RebuildResources ();
+
+ void ClearDependencyInfo ();
+ void PreloadDependencies (SInt32 instanceId);
+
+ DependencyContainer m_DependentAssets;
+ container m_Container;
+ bool m_NeedsReload;
+
+private:
+ void PreloadDependencies (SInt32 instanceId, std::set<SInt32>& visited);
+};
+
+ResourceManager& GetResourceManager ();
+
+extern const char* kResourcePath;
+extern const char* kOldWebResourcePath;
+extern const char* kEditorResourcePath;
+extern const char* kDefaultExtraResourcesPath;
+
+#endif
diff --git a/Runtime/Misc/ResourceManagerGUIDs.h b/Runtime/Misc/ResourceManagerGUIDs.h
new file mode 100644
index 0000000..5dba07f
--- /dev/null
+++ b/Runtime/Misc/ResourceManagerGUIDs.h
@@ -0,0 +1,5 @@
+#include "Runtime/Utilities/GUID.h"
+
+inline UnityGUID GetEditorResourcesGUID () { return UnityGUID (0, 0, 13, 0); }
+inline UnityGUID GetBuiltinResourcesGUID () { return UnityGUID (0, 0, 14, 0); }
+inline UnityGUID GetBuiltinExtraResourcesGUID () { return UnityGUID (0, 0, 15, 0); }
diff --git a/Runtime/Misc/ResourceManagerUtility.cpp b/Runtime/Misc/ResourceManagerUtility.cpp
new file mode 100644
index 0000000..0f8ad5d
--- /dev/null
+++ b/Runtime/Misc/ResourceManagerUtility.cpp
@@ -0,0 +1,55 @@
+#include "ResourceManagerUtility.h"
+
+#if ENABLE_SCRIPTING
+
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Scripting/Scripting.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/Backend/ScriptingBackendApi.h"
+#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h"
+
+ScriptingObjectPtr GetScriptingBuiltinResourceFromManager(BuiltinResourceManager& resources, ScriptingObjectPtr type, const std::string& path)
+{
+
+#if ENABLE_MONO || UNITY_WINRT
+ if(path.size() == 0) Scripting::RaiseArgumentException("Invalid path");
+#endif
+
+ ScriptingTypePtr requiredclass = GetScriptingTypeRegistry().GetType(type);
+ int classID = Scripting::GetClassIDFromScriptingClass(GetScriptingTypeRegistry().GetType(type));
+
+ Object* o = resources.GetResource (classID, path);
+
+ ScriptingObjectPtr mono = Scripting::ScriptingWrapperFor(o);
+
+#if ENABLE_MONO
+ // The third parameter 'false' doesn't match in scripting_class_is_subclass_of, need to check
+ if (mono != SCRIPTING_NULL && mono_class_is_subclass_of(mono_object_get_class(mono), requiredclass, false))
+ return mono;
+ else
+ return SCRIPTING_NULL;
+#else
+ if (mono != SCRIPTING_NULL && scripting_class_is_subclass_of(scripting_object_get_class(mono, GetScriptingTypeRegistry()), requiredclass))
+ return mono;
+ else
+ return SCRIPTING_NULL;
+#endif
+}
+
+ScriptingObjectPtr GetScriptingBuiltinResource(ScriptingObjectPtr type, const std::string& path)
+{
+ return GetScriptingBuiltinResourceFromManager(GetBuiltinResourceManager(), type, path);
+}
+
+#endif
+
+
+#if UNITY_EDITOR
+
+ScriptingObjectPtr GetMonoBuiltinExtraResource(ScriptingObjectPtr type, ScriptingStringPtr path)
+{
+ return GetScriptingBuiltinResourceFromManager(GetBuiltinExtraResourceManager(), type, scripting_cpp_string_for(path));
+}
+
+#endif
diff --git a/Runtime/Misc/ResourceManagerUtility.h b/Runtime/Misc/ResourceManagerUtility.h
new file mode 100644
index 0000000..fbec342
--- /dev/null
+++ b/Runtime/Misc/ResourceManagerUtility.h
@@ -0,0 +1,21 @@
+#ifndef RESOURCEMANAGERUTILITY_H
+#define RESOURCEMANAGERUTILITY_H
+
+#include "UnityPrefix.h"
+
+#include <string>
+
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+
+class BuiltinResourceManager;
+
+ScriptingObjectPtr GetScriptingBuiltinResourceFromManager(BuiltinResourceManager& resources, ScriptingObjectPtr type, const std::string& path);
+ScriptingObjectPtr GetScriptingBuiltinResource(ScriptingObjectPtr type, const std::string& path);
+
+#if UNITY_EDITOR
+
+ScriptingObjectPtr GetMonoBuiltinExtraResource(ScriptingObjectPtr type, ScriptingStringPtr path);
+
+#endif
+
+#endif
diff --git a/Runtime/Misc/SaveAndLoadHelper.cpp b/Runtime/Misc/SaveAndLoadHelper.cpp
new file mode 100644
index 0000000..7bc0c2d
--- /dev/null
+++ b/Runtime/Misc/SaveAndLoadHelper.cpp
@@ -0,0 +1,1106 @@
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "SaveAndLoadHelper.h"
+#include "RegisterAllClasses.h"
+
+#include "Runtime/Utilities/FileUtilities.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/BaseClasses/ManagerContextLoading.h"
+#include "Configuration/UnityConfigureRevision.h"
+#include "Runtime/Misc/BuildSettings.h"
+#if ENABLE_UNITYGUI
+#include "Runtime/IMGUI/GUIManager.h"
+#endif
+#include "Runtime/Terrain/TerrainData.h"
+#include "Runtime/Misc/PreloadManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Utilities/GUID.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/Utilities/vector_set.h"
+#include "Runtime/Input/InputManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Animation/AnimationManager.h"
+#include "QualitySettings.h"
+#include "Runtime/Animation/AnimationClip.h"
+#if ENABLE_AUDIO
+#include "Runtime/Audio/AudioClip.h"
+#endif
+#include "Runtime/Serialize/SerializedFile.h"
+#include "Runtime/Profiler/ProfilerHistory.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/MemoryProfilerStats.h"
+#include "Runtime/Profiler/ProfilerConnection.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "CaptureScreenshot.h"
+#include "Runtime/Graphics/Transform.h"
+#include "GameObjectUtility.h"
+#include "Runtime/Animation/Animation.h"
+#include "Runtime/BaseClasses/Cursor.h"
+#include "BatchDeleteObjects.h"
+
+#include "Runtime/Testing/Testing.h"
+#include "Runtime/Camera/UnityScene.h"
+#include "Runtime/Camera/RenderManager.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#if ENABLE_MONO
+#include "Runtime/Mono/MonoIncludes.h"
+#endif
+#include "Runtime/GfxDevice/GfxDeviceSetup.h"
+#include "Runtime/Misc/AssetBundle.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Input/GetInput.h"
+#include "Runtime/Mono/MonoManager.h"
+#include "Runtime/Mono/MonoAttributeHelpers.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Mono/MonoBehaviour.h"
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Graphics/ParticleSystem/ParticleSystem.h" // ParticleSystem::UpdateAll ()
+#include "Runtime/Filters/Particles/ParticleEmitter.h"
+#include "Runtime/IMGUI/TextMeshGenerator2.h"
+#include "Runtime/IMGUI/GUIClip.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "External/shaderlab/Library/pass.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "Runtime/Graphics/ScreenManager.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Utilities/Word.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+#include "Runtime/GameCode/CallDelayed.h"
+#include "Runtime/Mono/MonoScript.h"
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+#include "Runtime/Serialize/AwakeFromLoadQueue.h"
+#include "Runtime/Math/FloatExceptions.h"
+#include "Runtime/Audio/AudioManager.h"
+
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/CleanupManager.h"
+#include "Editor/Src/HierarchyState.h"
+#include "Editor/Src/EditorSettings.h"
+#include "Editor/Src/Application.h"
+#include "Editor/Src/AssetServer/ASCache.h"
+#include "Editor/Src/EditorBuildSettings.h"
+#include "Editor/Src/EditorUserBuildSettings.h"
+#include "Editor/Src/SceneInspector.h"
+#include "Editor/Src/Prefabs/Prefab.h"
+#include "Editor/Src/AssetPipeline/AssetDatabase.h"
+#include "Editor/Src/AssetPipeline/AssetInterface.h"
+ #if ENABLE_SPRITES
+ #include "Editor/Src/SpritePacker/SpritePacker.h"
+ #endif
+#include "Editor/Src/BuildPipeline/LODGroupStripping.h"
+#include "Editor/Src/GUIDPersistentManager.h"
+#include "Editor/Src/EditorAssetGarbageCollectManager.h"
+#include "Editor/Src/Prefabs/GenerateCachedTypeTree.h"
+#include "Editor/Src/AssetPipeline/MonoCompilationPipeline.h"
+#include "Player.h"
+#include "PlayerSettings.h"
+#endif
+
+#if ENABLE_WWW || ENABLE_CACHING
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "CachingManager.h"
+#include "PlatformDependent/CommonWebPlugin/CompressedFileStreamMemory.h"
+#endif
+
+
+#if SUPPORT_REPRODUCE_LOG
+#include "ReproductionLog.h"
+#endif
+
+#include "Runtime/Network/PlayerCommunicator/EditorConnection.h"
+
+const char* kMainData = "mainData";
+
+using namespace std;
+
+std::string SelectDataFolder ();
+
+static void EditorBeforeLoadingCleanup ();
+static void ResetManagersAfterLoadEditor ();
+static void CollectAllSceneObjects (InstanceIDArray& instanceIDs);
+static void CompleteAwakeSequence (const std::string& path, AwakeFromLoadQueue& awakeQueue);
+
+void DestroyWorld (bool destroySceneAssets)
+{
+ InstanceIDArray objects;
+
+ #if UNITY_EDITOR
+ if (destroySceneAssets)
+ CollectAllSceneObjects (objects);
+ else
+ CollectSceneGameObjects (objects);
+ #else
+ Assert(!destroySceneAssets);
+ CollectSceneGameObjects (objects);
+ #endif
+
+ Object* o;
+ // GameObjects first
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ GameObject* go = dynamic_pptr_cast<GameObject*> (o);
+ // Only Destroy root level GameObjects. The children will be destroyed
+ // as part of them. That way, we ensure that the hierarchy is walked correctly,
+ // and all objects in the hieararchy will be marked as deactivated when destruction happens.
+ if (go != NULL && go->GetComponent(Transform).GetParent() == NULL)
+ DestroyObjectHighLevel (o);
+ }
+
+ // normal objects whatever they might be after that
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+
+ objects.clear ();
+ CollectLevelGameManagers (objects);
+
+ // Gamemanagers & Scene last
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+ objects.clear ();
+
+ GlobalCallbacks::Get().didUnloadScene.Invoke();
+
+ #if DEBUGMODE
+ ValidateNoSceneObjectsAreLoaded (destroySceneAssets);
+ #endif
+}
+
+#if WEBPLUG
+#define GET_DATA_FOLDER ""
+#else
+#define GET_DATA_FOLDER SelectDataFolder()
+#endif
+
+bool InitializeEngineNoGraphics ()
+{
+ static bool isInitialized = false;
+ if (isInitialized == false)
+ {
+#if UNITY_EDITOR
+ EditorAssetGarbageCollectManager::StaticInitialize();
+#endif
+ #if SUPPORT_THREADS
+ Thread::mainThreadId = Thread::GetCurrentThreadID ();
+ #endif
+ #if ENABLE_PLAYERCONNECTION
+ #if UNITY_EDITOR
+ EditorConnection::Initialize();
+ #else
+ PlayerConnection::Initialize(GET_DATA_FOLDER);
+ InstallPlayerConnectionLogging(true);
+ #endif
+ #endif
+
+ #if ENABLE_PROFILER
+ InitializeMemoryProfilerStats();
+ UnityProfiler::Initialize();
+ ProfilerConnection::Initialize();
+ #if UNITY_EDITOR
+ ProfilerHistory::Initialize();
+ #endif
+ #endif
+
+#if ENABLE_PLAYERCONNECTION
+#if UNITY_EDITOR
+ EditorConnection::Get().PollWithCustomMessage();
+#else
+ // Bug: Calls GetBuildSettings() inside, but it's not yet initialized !!! At least on windows standalone player
+#if !UNITY_WIN && !UNITY_OSX && !UNITY_IPHONE
+ PlayerConnection::Get().Poll();
+#endif
+#endif
+#endif
+
+ InitializeBatchDelete();
+ RegisterAllClasses ();
+ Object::InitializeAllClasses ();
+ GameObject::InitializeMessageIdentifiers ();
+ ManagerContextInitializeClasses ();
+ RenderBufferManager::InitRenderBufferManager ();
+
+// On Linux Editor we initialize ScreenManager much earlier. Avoid doing that
+// again here so we won't zero out the members.
+#if !(UNITY_EDITOR && UNITY_LINUX)
+ InitScreenManager ();
+#endif
+
+ InitFloatExceptions();
+ #if ENABLE_UNITYGUI
+ InitGUIManager ();
+ #endif
+
+ // Init platform specific input support.
+ // Aras: on windows input must be initialized later, after the
+ // actual window is set up.
+ #if !UNITY_WIN && !UNITY_WII && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_FLASH && !UNITY_WEBGL && !UNITY_BB10 && !UNITY_TIZEN
+ InputInit();
+ #endif
+ Object::CallInitializeClass ();
+ }
+ return true;
+}
+
+#if (UNITY_OSX || (UNITY_LINUX && SUPPORT_X11)) && !UNITY_PEPPER
+void SetGraphicsBatchMode( bool batch );
+#endif
+
+bool InitializeEngineGraphics (bool batch)
+{
+ static bool isInitialized = false;
+ if (isInitialized == false)
+ {
+ printf_console( "Initialize engine version: %s\n", UNITY_VERSION_FULL_NICE );
+
+#if (UNITY_OSX || (UNITY_LINUX && SUPPORT_X11)) && !UNITY_PEPPER
+ SetGraphicsBatchMode( batch );
+#endif
+#if UNITY_OSX || UNITY_FLASH || (UNITY_LINUX && WEBPLUG) || UNITY_WEBGL || (UNITY_WIN && !UNITY_WINRT) && !UNITY_PEPPER
+ if( !InitializeGfxDevice() )
+ return false;
+#endif
+
+#if ENABLE_PERFORMANCE_TESTS
+ RUN_PERFORMANCE_TESTS();
+#endif
+
+ #if ENABLE_MULTITHREADED_CODE || ENABLE_MULTITHREADED_SKINNING
+ CreateJobScheduler();
+ #endif
+
+ ShaderLab::InitShaderLab ();
+
+ Object::CallPostInitializeClass ();
+ GameObject::InitializeMessageHandlers ();
+ BuiltinResourceManager::InitializeAllResources ();
+
+ // Make sure the default shaders are always preloaded to avoid loading them during
+ // threaded background loading
+ Shader::LoadDefaultShaders ();
+
+ isInitialized = true;
+ GlobalCallbacks::Get().initializedEngineGraphics.Invoke();
+ }
+ return true;
+}
+
+void CleanupAllObjects (bool reloadable)
+{
+ vector<SInt32> objects;
+
+ // Before each loop dealing with Objects here, we refetch all Objects. This is because
+ // deleting some game object might actually create new objects (i.e. components are
+ // dereferenced in GO destructor).
+
+ // Delete all non-temporary game objects first
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (o && o->IsDerivedFrom(ClassID(GameObject)) && !o->IsPersistent() && !o->TestHideFlag(Object::kDontSave))
+ {
+ DestroyObjectHighLevel (o);
+ }
+ }
+
+ // Delete all game objects first
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+#if WEBPLUG
+ Transform* t = GetIdentityTransform();
+#endif
+ if (o && !o->IsPersistent() && o->IsDerivedFrom(ClassID(GameObject)))
+ {
+#if WEBPLUG
+ if(reloadable && o == t->GetGameObjectPtr()) {
+ // THIS IS A MASSIVE HACK
+ // We do this because, when the Unity player is reloading, it frees all
+ // the gameobjects in memory.
+ // The Renderer object keeps a global static singleton Identity Transform,
+ // attached to an invisible game object that is created on first startup.
+ // Because the startup code is tied deeply into the initialization of
+ // the graphics engine, it's less hacky to Not Destroy that one game object.
+ // We seriously need to eliminate these global statics or place them under
+ // the management of a game manager object, which can refresh them as needed.
+ continue;
+ }
+#endif
+ DestroyObjectHighLevel (o);
+ }
+ }
+
+ LockObjectCreation();
+
+ // Do cleanup after all game objects are killed thus
+ // terrains will be deleted, and RenderTexture which are allocated by the render buffer manager will not be deleted yet.
+ TextMeshGenerator2::Flush();
+ if (GetRenderBufferManagerPtr())
+ GetRenderBufferManager().Cleanup();
+ ShaderLab::Pass::DidClearAllTempRenderTextures ();
+
+ // First of all we need to delete all non-temporary objects
+ // This is because usually there are dependencies from non-temporary objects
+ // to temporary objects
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (o && !o->IsDerivedFrom(ClassID(GameManager)))
+ {
+ if (o != NULL && o->TestHideFlag(Object::kDontSave))
+ continue;
+ delete_object_internal (o);
+ }
+ }
+
+ // Finally also cleanup all temporary objects
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (o && !o->IsDerivedFrom(ClassID(GameManager)))
+ {
+ if (reloadable)
+ {
+ if (o != NULL && o->TestHideFlag(Object::kDontSave))
+ {
+ bool forceUnload = false;
+ // Make sure AssetBundles are unloaded.
+ if (o->GetClassID() == ClassID(AssetBundle))
+ forceUnload = true;
+#if ENABLE_SCRIPTING
+ if (o->GetClassID() == ClassID(MonoBehaviour))
+ {
+ forceUnload = true;
+ if (strcmp(o->GetName(), "GameSkin") != 0)
+ AssertString(o->GetName());
+ }
+
+ if (o->GetClassID() == ClassID(MonoScript))
+ {
+ forceUnload = true;
+ }
+#endif
+ if (!forceUnload)
+ continue;
+ }
+ }
+ delete_object_internal (o);
+ }
+ }
+
+ for (int i=ManagerContext::kManagerCount-1;i != 0;i--)
+ {
+ if (GetManagerContext().m_Managers[i])
+ {
+ GetPersistentManager().MakeObjectUnpersistent(GetManagerContext().m_Managers[i]->GetInstanceID(), kDontDestroyFromFile);
+ delete_object_internal (GetManagerContext().m_Managers[i]);
+ SetManagerPtrInContext (i, NULL);
+ }
+ }
+
+ objects.clear();
+ Object::FindAllDerivedObjects (ClassID (Object), &objects);
+ for (int i=0;i<objects.size();i++)
+ {
+ Object* o = Object::IDToPointer(objects[i]);
+ if (reloadable)
+ {
+ // Don't cleanup temporary objects (Except if its a gui skin from the builtin resources file)
+ if (o != NULL && o->TestHideFlag(Object::kDontSave))
+ continue;
+ }
+
+ delete_object_internal (o);
+ }
+
+ // Clear cached properties on all materials
+ // This is because reloading the web player might delete some referenced textures (gui style default materials)
+ // but not the material. The cache then gets out of sync and In that case, OpenGL will give an invalid error and text becomes black.
+ // Thus we force reloading the textures from scratch.
+ vector<Material*> materials;
+ Object::FindObjectsOfType (&materials);
+ for (int i=0;i<materials.size();i++)
+ {
+ materials[i]->ClearProperties();
+ }
+
+ #if CAPTURE_SCREENSHOT_AVAILABLE
+ FinishAllCaptureScreenshot ();
+ #endif
+ #if SUPPORT_REPRODUCE_LOG
+ PlayerCleanupReproduction();
+ #endif
+
+#if ENABLE_PROFILER && UNITY_EDITOR
+ ProfilerHistory::Cleanup();
+#endif
+
+ UnlockObjectCreation();
+
+ CleanupBatchDelete ();
+}
+
+void CleanupEngine ()
+{
+ if (IsGfxDevice())
+ GetGfxDevice().FinishRendering();
+ TextMeshGenerator2::Flush();
+ CleanupAllObjects(false);
+ MessageIdentifier::Cleanup ();
+ Object::CleanupAllClasses ();
+ CleanupShaders ();
+ RenderBufferManager::CleanupRenderBufferManager ();
+ #if ENABLE_UNITYGUI
+ CleanupGUIManager ();
+ #endif
+ #if ENABLE_MULTITHREADED_CODE || ENABLE_MULTITHREADED_SKINNING
+ DestroyJobScheduler();
+ #endif
+ // on windows, device cleanup is done from separately (web player: called from separate thread)
+ #if UNITY_OSX
+ DestroyGfxDevice();
+ #endif
+
+ #if !UNITY_FLASH
+ Cursors::CleanupCursors ();
+ #endif
+
+ ReleaseScreenManager();
+
+ #if ENABLE_PLAYERCONNECTION && !UNITY_EDITOR
+ InstallPlayerConnectionLogging(false);
+ #endif
+#if UNITY_EDITOR
+ EditorAssetGarbageCollectManager::StaticDestroy();
+ CleanupTypeTreeCache();
+ CleanupMonoCompilationPipeline();
+#endif
+ ReleaseLogHandlers();
+#if ENABLE_PROFILER
+ CleanupMemoryProfilerStats();
+#endif
+}
+
+void PostprocessSceneGenerateGUITextureAtlas ()
+{
+#if UNITY_EDITOR && ENABLE_RETAINEDGUI
+ std::vector<Canvas*> canvas;
+ Object::FindObjectsOfType(&canvas);
+
+ for( unsigned i = 0 ; i < canvas.size() ; ++ i)
+ BuildTextureAtlasForCanvas(canvas[i]);
+#endif
+}
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+void PostprocessSceneSortTransforms()
+{
+ const Transform::VisibleRootMap& rootMap = GetSceneTracker().GetVisibleRootTransforms();
+
+ for (Transform::VisibleRootMap::const_iterator it = rootMap.begin(); it != rootMap.end(); ++it)
+ {
+ Transform* rootTrans = (*it).second;
+ rootTrans->OrderChildrenRecursively();
+ }
+}
+#endif
+
+void PostprocessScene ()
+{
+#if UNITY_EDITOR
+ // Disconnect all prefab instances.
+ // This ensures that no prefab recording will happen which can be quite expensive.
+ DisconnectAllPrefabInstances ();
+
+ DestroyRenderersFromLODGroupInOpenScene(GetQualitySettings().GetStrippedMaximumLODLevel());
+ ScriptingArguments no_arguments;
+ CallMethodsWithAttribute(MONO_COMMON.postProcessSceneAttribute, no_arguments, NULL);
+ PostprocessSceneGenerateGUITextureAtlas();
+#endif
+
+#if ENABLE_EDITOR_HIERARCHY_ORDERING
+ PostprocessSceneSortTransforms();
+#endif
+}
+
+void CleanupAfterLoad ()
+{
+ #if UNITY_EDITOR
+ GetCleanupManager ().Flush ();
+ RemoveDuplicateGameManagers ();
+ #endif
+
+ GarbageCollectSharedAssets (true);
+
+ TextMeshGenerator2::Flush();
+ GetRenderBufferManager().GarbageCollect(0);
+ GetGfxDevice().InvalidateState();
+
+ #if ENABLE_MONO
+ mono_gc_collect (mono_gc_max_generation ());
+ #endif
+
+ ParticleSystem::BeginUpdateAll ();
+ ParticleSystem::EndUpdateAll ();
+ ParticleEmitter::UpdateAllParticleSystems();
+ RenderManager::UpdateAllRenderers();
+
+ // FIXME: Do this because we can't fully initialize the physicsmanager at startup.
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kPhysicsManager, AwakeFromLoad (kDefaultAwakeFromLoad))
+
+ GetDelayedCallManager().Update(DelayedCallManager::kAfterLoadingCompleted);
+
+
+ ///@TODO: I am not sure why we do this...
+ GetQualitySettings().ApplySettings();
+}
+
+void PatchRendererLightmapIndices (AwakeFromLoadQueue& awakeQueue)
+{
+ // offset the lightmap index in the newly loaded Renderers and Terrains by the current number of lightmaps
+ int lightmapIndexOffset = GetLightmapSettings().GetLightmaps().size();
+ if (lightmapIndexOffset == 0)
+ return;
+
+ AwakeFromLoadQueue::ItemArray& rendererItems = awakeQueue.GetItemArray(kGameObjectAndComponentQueue);
+ for (int i = 0; i < rendererItems.size(); i++)
+ {
+ Object* object = Object::IDToPointer(rendererItems[i].objectPPtr.GetInstanceID());
+
+ // renderers
+ Renderer* r = dynamic_pptr_cast<Renderer*>(object);
+ if (r != NULL)
+ {
+ if (r->IsLightmappedForRendering())
+ r->SetLightmapIndexInt(r->GetLightmapIndexInt() + lightmapIndexOffset);
+ }
+ }
+
+ #if ENABLE_TERRAIN
+ PPtr<MonoScript> terrainScript = GetBuiltinResource<MonoScript>("Terrain");
+ AwakeFromLoadQueue::ItemArray& monoBehaviourItems = awakeQueue.GetItemArray(kMonoBehaviourQueue);
+ for (int i = 0; i < monoBehaviourItems.size(); i++)
+ {
+ Object* object = Object::IDToPointer(monoBehaviourItems[i].objectPPtr.GetInstanceID());
+
+ // terrains
+ MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*>(object);
+ if (behaviour && behaviour->GetScript() == terrainScript)
+ {
+ MessageData data;
+ data.SetData (lightmapIndexOffset, ClassID (int));
+ SendMessageDirect(*behaviour, kShiftLightmapIndex, data);
+ }
+ }
+ #endif
+
+}
+
+void MergeLightmapData(AwakeFromLoadQueue& awakeQueue)
+{
+ // We made sure the LightmapSettings object will get loaded along with the other stuff
+ // in PreloadLevelOperation::Perform(). Now let's update the lightmap indices of the
+ // loaded Renderers, append loaded lightmaps to the original lightmaps and destroy
+ // the loaded LightmapSettings object.
+ LightmapSettings* loadedLightmapSettings = NULL;
+
+ AwakeFromLoadQueue::ItemArray& managerItems = awakeQueue.GetItemArray(kManagersQueue);
+ for (int i = 0; i < managerItems.size(); i++)
+ {
+ loadedLightmapSettings = dynamic_instanceID_cast<LightmapSettings*> (managerItems[i].objectPPtr.GetInstanceID());
+ if (loadedLightmapSettings)
+ break;
+ }
+ if (loadedLightmapSettings == NULL)
+ return;
+
+ const std::vector<LightmapData>& loadedLightmapData = loadedLightmapSettings->GetLightmaps();
+
+ // loaded level contains lightmaps, so merge them.
+ if (loadedLightmapData.size() != 0)
+ {
+ int lightmapsMode = GetLightmapSettings ().GetLightmapsMode ();
+ int loadedLightmapsMode = loadedLightmapSettings->GetLightmapsMode ();
+
+ if (loadedLightmapsMode != lightmapsMode)
+ {
+ WarningString (Format("The loaded level has a different lightmaps mode than the current one. Current: %s. Loaded: %s. Will use: %s.",
+ LightmapSettings::kLightmapsModeNames[lightmapsMode],
+ LightmapSettings::kLightmapsModeNames[loadedLightmapsMode],
+ LightmapSettings::kLightmapsModeNames[lightmapsMode]));
+ }
+
+ // first offset lightmap indices of the loaded renderers by the current number of lightmaps
+ PatchRendererLightmapIndices(awakeQueue);
+ // then add the new lightmaps at the end of the current lightmaps array
+ GetLightmapSettings().AppendLightmaps(loadedLightmapData);
+ }
+
+ DestroyObjectHighLevel(loadedLightmapSettings);
+}
+
+void PostLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue)
+{
+ PostEditorLoadLevelAdditive(pathName, awakeQueue);
+
+ PostprocessScene();
+}
+
+void PostEditorLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue)
+{
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ CompleteAwakeSequence(pathName, awakeQueue);
+
+ MergeLightmapData(awakeQueue);
+}
+
+void VerifyNothingIsPersistentInLoadedScene (const std::string& pathName)
+{
+#if DEBUGMODE
+ set<SInt32> persistentObjectsAtPath;
+ GetPersistentManager().GetPersistentInstanceIDsAtPath(pathName, &persistentObjectsAtPath);
+ for (set<SInt32>::iterator i=persistentObjectsAtPath.begin();i!=persistentObjectsAtPath.end();i++)
+ {
+ Object* target = Object::IDToPointer(*i);
+ // Error on everything but global game managers. this handles the case where mainData contains both scene data and global game managers.
+ if (target == NULL || !Object::IsDerivedFromClassID(target->GetClassID(), ClassID(GlobalGameManager)) || UNITY_EDITOR)
+ {
+ string className = target ? target->GetClassName() : "Not loaded";
+ ErrorString("Failed to unpersist: " + className + " ID: " + IntToString(*i) + " FileID: " + IntToString(GetPersistentManager().GetLocalFileID(*i)));
+ }
+ }
+ AssertIf(GetPersistentManager().IsStreamLoaded(pathName) && !GetPersistentManager().HasMemoryOrCachedSerializedFile(pathName));
+#endif
+}
+
+void ValidateNoSceneObjectsAreLoaded (bool includeAllEditorExtensions)
+{
+#if (UNITY_EDITOR && DEBUGMODE) || !UNITY_RELEASE
+ // This happens when objects are accidentally loaded from disk again because someone still had a pointer to them.
+ InstanceIDArray objects;
+#if UNITY_EDITOR
+ if (includeAllEditorExtensions)
+ CollectAllSceneObjects (objects);
+ else
+#endif
+ CollectSceneGameObjects (objects);
+ if (!objects.empty ())
+ {
+ ErrorString ("Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)");
+ }
+
+ vector<Object*> levelManagers;
+ Object::FindObjectsOfType (ClassID (LevelGameManager), &levelManagers);
+ for (int i=0;i<levelManagers.size();i++)
+ {
+ ErrorString (Format("Manager %s is still loading after clearing scene", levelManagers[i]->GetClassName().c_str()));
+ }
+#endif
+}
+
+void CompletePreloadMainData (AwakeFromLoadQueue& awakeQueue)
+{
+ ResetInput();
+
+ // Cleanup exisiting level maangers
+ DestroyLevelManagers ();
+
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ // Load LevelManagers
+ LoadManagers(awakeQueue);
+
+ // Load everything else
+ CompleteAwakeSequence(kMainData, awakeQueue);
+
+ // FIXME: Do this because we can't fully initialize the physicsmanager at startup.
+ CALL_MANAGER_IF_EXISTS(ManagerContext::kPhysicsManager, AwakeFromLoad (kDefaultAwakeFromLoad))
+
+ GetDelayedCallManager().Update(DelayedCallManager::kAfterLoadingCompleted);
+
+ GetQualitySettings().ApplySettings();
+}
+
+void CompletePreloadManagerLoadLevel (const std::string& path, AwakeFromLoadQueue& awakeQueue)
+{
+ ResetInput();
+
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ LoadManagers(awakeQueue);
+
+ CompleteAwakeSequence(path, awakeQueue);
+
+ PostprocessScene();
+
+ CleanupAfterLoad ();
+}
+
+static void CompleteAwakeSequence (const std::string& path, AwakeFromLoadQueue& awakeQueue)
+{
+#if UNITY_EDITOR
+ // Merge all prefab instances (Requires that prefab backwards compatilibyt has been applied - AwakeFromLoad has not yet been called)
+ MergeAllPrefabInstances(&awakeQueue);
+#endif
+
+ // Unload stream
+ // - Don't unload if the stream came from an AssetBundle
+ if ( !GetPersistentManager().HasMemoryOrCachedSerializedFile( path ) )
+ GetPersistentManager().UnloadStream(path);
+
+ // Invoke AwakeFromLoad and friends.
+ GetPersistentManager().IntegrateAllThreadedObjectsStep2(awakeQueue);
+
+
+ //@TODO: write a check that all objects in the AwakeQueue are !IsPersistent()
+
+#if UNITY_EDITOR
+ GetSceneTracker().ReloadTransformHierarchyRoots();
+#endif
+
+}
+
+#if UNITY_EDITOR
+
+void LoadLevelAdditiveEditor (const std::string& level)
+{
+ PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (level, "", -1, PreloadLevelOperation::kLoadEditorAdditiveLevel, true);
+
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+
+ op->Release();
+}
+
+void CompletePreloadManagerLoadLevelEditor (const std::string& path, AwakeFromLoadQueue& awakeQueue, int preloadOperationMode)
+{
+ Assert (!path.empty ());
+
+ awakeQueue.RegisterObjectInstanceIDs();
+
+ LoadManagers(awakeQueue);
+
+ CompleteAwakeSequence(path, awakeQueue);
+
+ if (preloadOperationMode == PreloadLevelOperation::kOpenSceneEditorPlaymode)
+ PostprocessScene ();
+
+ CleanupAfterLoad ();
+
+ ResetManagersAfterLoadEditor ();
+
+ if (preloadOperationMode == PreloadLevelOperation::kOpenSceneEditorPlaymode)
+ PlayerInitState();
+}
+
+bool LoadSceneEditor (const string& pathName, std::map<LocalIdentifierInFileType, SInt32>* hintFileIDToHeapID, int options)
+{
+ AssertIf (pathName.empty ());
+
+ bool enterPlaymode = options & kEditorPlayMode;
+ PreloadLevelOperation::LoadingMode preloadOperationMode = enterPlaymode ? PreloadLevelOperation::kOpenSceneEditorPlaymode : PreloadLevelOperation::kOpenSceneEditor;
+
+ // Make sure nothing is in the queue (We are calling DestroyWorld, then doing AsyncLoad)
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+
+ // Destroy world must be called before we start loading.
+ // Otherwise objects will receive different instanceIDs in Playmode then they have in edit mode (hintFileIDToHeapID)
+ DestroyWorld(true);
+ EditorBeforeLoadingCleanup ();
+ SetIsWorldPlaying (enterPlaymode);
+
+ // Any left-over playmode load operations need to be cleared here.
+ GetPreloadManager().RemoveStopPlaymodeOperations ();
+
+ // Now load
+ if (hintFileIDToHeapID)
+ GetPersistentManager().SuggestFileIDToHeapIDs (pathName, *hintFileIDToHeapID);
+
+ PreloadLevelOperation* op = PreloadLevelOperation::LoadLevel (pathName, "", -1, preloadOperationMode, true);
+
+ GetPreloadManager().WaitForAllAsyncOperationsToComplete();
+
+ op->Release();
+
+ return true;
+}
+#endif
+
+
+static void DestroyAllAtPath (const std::string& path)
+{
+#if DEBUGMODE
+ GetPersistentManager().SetDebugAssertLoadingFromFile(path);
+#endif
+ PersistentManager::ObjectIDs ids;
+ GetPersistentManager().GetLoadedInstanceIDsAtPath(path, &ids);
+
+ LockObjectCreation();
+ for (PersistentManager::ObjectIDs::iterator i=ids.begin();i!=ids.end();i++)
+ {
+ Object* o = Object::IDToPointer (*i);
+ AssertIf(o != NULL && !o->IsPersistent());
+ delete_object_internal (o);
+ }
+ UnlockObjectCreation();
+
+#if DEBUGMODE
+ ids.clear();
+ GetPersistentManager().GetLoadedInstanceIDsAtPath(path, &ids);
+ if (!ids.empty())
+ {
+ ErrorString("UnloadAssetBundle failed");
+ }
+ GetPersistentManager().SetDebugAssertLoadingFromFile("");
+#endif
+
+ GetPersistentManager().RemoveObjectsFromPath(path);
+}
+
+static bool DoesGfxDeviceRequireAssetsToBeReloadableFromDisk ()
+{
+// For Windows Phone 8 reloading may be required at any point in time (when user pauses - resumes app, for instance)
+#if UNITY_WP8
+ return true;
+#else
+ return false;
+#endif
+}
+
+void UnloadAssetBundle (AssetBundle& file, bool unloadAllLoadedObjects)
+{
+ if (DoesGfxDeviceRequireAssetsToBeReloadableFromDisk () && !unloadAllLoadedObjects)
+ {
+ ErrorString("AssetBundle.Unload(false) shouldn't be called because used assets might have to be reloaded at any point in time. If no assets are used, call AssetBundle.Unload(true).");
+ return;
+ }
+
+ GetPreloadManager().LockPreloading();
+
+ PPtr<AssetBundle> resourceFilePPtr = &file;
+
+ // Destroy all objects loaded from the AssetBundle path (or unassociate
+ // them from the path if unloadAllLoadedObjects==false) and unload
+ // all streams associated with the bundle.
+ //
+ // Note that DestroyAllAtPath() will actually delete the AssetBundle
+ // instance itself so be careful not to reference any data from it
+ // after the call.
+
+ #if ENABLE_WWW
+ UnityWebStream* stream = file.m_UnityWebStream;
+ if (stream && stream->GetFileStream() &&
+ (stream->GetFileStream()->GetType() == kCompressedFileStreamMemoryType ||
+ stream->GetFileStream()->GetType() == kUncompressedFileStreamMemoryType))
+ {
+ FileStream* compressedFile = stream->GetFileStream();
+ FileStream::Decompressed files = compressedFile->m_Files;
+
+ for (FileStream::iterator f=files.begin();f != files.end();f++)
+ {
+ if (unloadAllLoadedObjects)
+ DestroyAllAtPath(f->name);
+ else
+ GetPersistentManager().RemoveObjectsFromPath(f->name);
+ }
+
+ DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID());
+
+ for (FileStream::iterator i=files.begin();i != files.end();i++)
+ {
+ GetPersistentManager().UnloadStream(i->name);
+ }
+ }
+ else
+ #endif // ENABLE_WWW
+ #if ENABLE_CACHING
+ if (file.m_CachedUnityWebStream)
+ {
+ // Don't const reference files, as the changed is being changed in the loop
+ vector<string> files = file.m_CachedUnityWebStream->m_Files;
+
+ for (int i=0;i<files.size();i++)
+ {
+ const string& path = files[i];
+ if (unloadAllLoadedObjects)
+ DestroyAllAtPath(path);
+ else
+ GetPersistentManager().RemoveObjectsFromPath(path);
+ }
+
+ DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID());
+
+ for (int i=0;i<files.size();i++)
+ {
+ GetPersistentManager().UnloadStream(files[i]);
+ }
+ }
+ else
+ #endif // ENABLE_CACHING
+ if (file.m_UncompressedFileInfo)
+ {
+ AssetBundle::UncompressedFileInfoContainer* uncompFileInfo = file.m_UncompressedFileInfo;
+
+ for (AssetBundle::UncompressedFileInfoContainer::iterator f = uncompFileInfo->begin();
+ f != uncompFileInfo->end();
+ f++)
+ {
+ const string& path = f->fileName;
+ if (unloadAllLoadedObjects)
+ DestroyAllAtPath(path);
+ else
+ GetPersistentManager().RemoveObjectsFromPath(path);
+ }
+
+ DestroyWithoutLoadingButDontDestroyFromFile(resourceFilePPtr.GetInstanceID());
+
+ for (AssetBundle::UncompressedFileInfoContainer::iterator f = uncompFileInfo->begin();
+ f != uncompFileInfo->end();
+ f++)
+ {
+ GetPersistentManager().UnloadStream(f->fileName);
+ }
+ UNITY_DELETE(uncompFileInfo, kMemFile);
+ }
+ else
+ {
+ ErrorString("Resource file has already been unloaded.");
+ }
+
+ GetPreloadManager().UnlockPreloading();
+}
+
+
+#if UNITY_EDITOR
+
+
+static void EditorBeforeLoadingCleanup ()
+{
+ GetDelayedCallManager ().ClearAll ();
+}
+
+static void ResetManagersAfterLoadEditor ()
+{
+ ResetInput();
+ GetTimeManager ().ResetTime ();
+ GetQualitySettings().ApplySettings();
+}
+
+void CreateWorldEditor ()
+{
+ // Create any managers that are not created yet and setup manager context
+ AwakeFromLoadQueue emptyQueue (kMemTempAlloc);
+ LoadManagers(emptyQueue);
+
+ EditorBeforeLoadingCleanup ();
+
+ GetSceneTracker().ReloadTransformHierarchyRoots();
+
+ ResetManagersAfterLoadEditor ();
+}
+#endif
+
+
+PROFILER_INFORMATION(gCollectSceneGameObjects, "CollectGameObjects", kProfilerLoading)
+
+void CollectSceneGameObjects (InstanceIDArray& outputObjects)
+{
+ PROFILER_AUTO(gCollectSceneGameObjects,NULL);
+ vector<GameObject*> gameObjects;
+ Object::FindObjectsOfType (&gameObjects);
+ for (vector<GameObject*>::iterator i= gameObjects.begin ();i != gameObjects.end ();++i)
+ {
+ GameObject& object = **i;
+
+ // Verify that game objects dont accidentally end up being active and persistent
+ #if DEBUGMODE
+ if (object.IsActive() && object.IsPersistent())
+ {
+ ErrorStringObject("Persistent object inconsistency", &object);
+ }
+ #endif
+
+ if (object.IsPersistent ())
+ continue;
+ if (object.TestHideFlag (Object::kDontSave))
+ continue;
+
+ #if UNITY_EDITOR
+ if (object.IsPrefabParent ())
+ {
+ ErrorStringObject ("A prefab somehow lost its way out of its asset file. Ignoring it. Save your scene and next time you launch the editor it will be gone.", &object);
+ continue;
+ }
+ #endif
+
+ outputObjects.push_back(object.GetInstanceID ());
+ }
+}
+#if UNITY_EDITOR
+static void CollectAllSceneObjects (InstanceIDArray& instanceIDs)
+{
+ vector<SInt32> objects;
+ set<Prefab*> prefabInstances;
+ Object::FindAllDerivedObjects (ClassID (EditorExtension), &objects);
+ for (vector<SInt32>::iterator i= objects.begin ();i != objects.end ();++i)
+ {
+ EditorExtension& object = *PPtr<EditorExtension> (*i);
+
+#if DEBUGMODE
+ GameObject* go = dynamic_pptr_cast<GameObject*> (&object);
+ if (go)
+ {
+ if (go->IsActive() && go->IsPersistent())
+ {
+ ErrorStringObject("Persistent object inconsistency", go);
+ }
+ }
+#endif
+
+ if (object.IsPersistent ())
+ continue;
+
+ if (object.IsPrefabParent())
+ {
+ ErrorStringObject ("A prefab somehow lost its way out of its asset file. Ignoring it. Save your scene and next time you launch the editor it will be gone.", &object);
+ continue;
+ }
+
+ if (object.TestHideFlag (Object::kDontSave))
+ continue;
+ if (object.IsDerivedFrom (ClassID (GameManager)))
+ continue;
+ if (object.GetClassID () >= ClassID (SmallestEditorClassID))
+ continue;
+
+ instanceIDs.push_back(object.GetInstanceID ());
+ }
+}
+
+#endif
+
diff --git a/Runtime/Misc/SaveAndLoadHelper.h b/Runtime/Misc/SaveAndLoadHelper.h
new file mode 100644
index 0000000..f9bd799
--- /dev/null
+++ b/Runtime/Misc/SaveAndLoadHelper.h
@@ -0,0 +1,58 @@
+#ifndef SAVEANDLOADHELPER_H
+#define SAVEANDLOADHELPER_H
+
+#include <map>
+#include <string>
+#include <set>
+#include <vector>
+#include "Runtime/BaseClasses/BaseObject.h"
+#include "Runtime/Utilities/GUID.h"
+#include "Runtime/Misc/GarbageCollectSharedAssets.h"
+
+class AssetBundle;
+class AwakeFromLoadQueue;
+
+typedef dynamic_array<int> InstanceIDArray;
+
+void DestroyWorld (bool destroySceneAssets);
+
+bool InitializeEngineNoGraphics ();
+bool InitializeEngineGraphics (bool batch = false);
+void CleanupEngine ();
+void CleanupAllObjects (bool reloadable);
+
+void CreateWorldEditor ();
+void CleanupAfterLoad ();
+
+/// Loads a new world from a pathname, playmode or editmode
+/// Returns true if any datatemplates were merged while loading.
+bool LoadSceneEditor (const std::string& pathName, std::map<LocalIdentifierInFileType, SInt32>* hintFileIDToHeapID, int mode);
+
+void PostLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue);
+void PostEditorLoadLevelAdditive (const std::string& pathName, AwakeFromLoadQueue& awakeQueue);
+void CompletePreloadManagerLoadLevel (const std::string& path, AwakeFromLoadQueue& awakeQueue);
+void CompletePreloadMainData (AwakeFromLoadQueue& awakeQueue);
+void CompletePreloadManagerLoadLevelEditor (const std::string& path, AwakeFromLoadQueue& awakeQueue, int preloadOperationMode);
+
+void LoadLevelAdditiveEditor (const std::string& level);
+
+void CollectSceneGameObjects (InstanceIDArray& instanceIDs);
+
+void CheckAllGOConsistency ();
+
+void PostprocessScene ();
+
+void UnloadAssetBundle (AssetBundle& file, bool unloadAllLoadedObjects);
+
+void VerifyNothingIsPersistentInLoadedScene (const std::string& pathName);
+
+/// Make there are any game objects and/or level managers still present, print an error message.
+///
+/// @param includeAllEditorExtensions If true, not only check for GameObjects but also for
+/// any object derived from EditorExtension.
+void ValidateNoSceneObjectsAreLoaded (bool includeAllEditorExtensions = false);
+
+extern const char* kMainData;
+
+
+#endif
diff --git a/Runtime/Misc/SceneUnloading.cpp b/Runtime/Misc/SceneUnloading.cpp
new file mode 100644
index 0000000..00c30c2
--- /dev/null
+++ b/Runtime/Misc/SceneUnloading.cpp
@@ -0,0 +1,69 @@
+#include "UnityPrefix.h"
+#include "SceneUnloading.h"
+#include "SaveAndLoadHelper.h"
+#include "GameObjectUtility.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/ManagerContextLoading.h"
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Profiler/TimeHelper.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+
+PROFILER_INFORMATION (gUnloadScene, "UnloadScene", kProfilerLoading);
+
+void SharkBeginRemoteProfiling ();
+void SharkEndRemoteProfiling ();
+
+void UnloadGameScene ()
+{
+ ABSOLUTE_TIME begin = START_TIME;
+
+// SharkBeginRemoteProfiling ();
+ PROFILER_AUTO(gUnloadScene, NULL)
+
+ InstanceIDArray objects;
+
+ CollectSceneGameObjects (objects);
+
+ Object* o;
+ // GameObjects first
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ GameObject* go = dynamic_pptr_cast<GameObject*> (o);
+ // Only Destroy root level GameObjects. The children will be destroyed
+ // as part of them. That way, we ensure that the hierarchy is walked correctly,
+ // and all objects in the hieararchy will be marked as deactivated when destruction happens.
+ if (go != NULL && go->GetComponent(Transform).GetParent() == NULL)
+ DestroyObjectHighLevel (o);
+ }
+
+ // normal objects whatever they might be after that
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+
+ objects.clear ();
+ CollectLevelGameManagers (objects);
+
+ // Gamemanagers & Scene last
+ for (InstanceIDArray::iterator i=objects.begin ();i != objects.end ();++i)
+ {
+ o = Object::IDToPointer (*i);
+ AssertIf (o && o->IsPersistent ());
+ DestroyObjectHighLevel (o);
+ }
+
+ GlobalCallbacks::Get().didUnloadScene.Invoke();
+
+ ValidateNoSceneObjectsAreLoaded ();
+
+ printf_console("UnloadTime: %f ms\n", AbsoluteTimeToMilliseconds(ELAPSED_TIME(begin)));
+
+// SharkEndRemoteProfiling ();
+}
diff --git a/Runtime/Misc/SceneUnloading.h b/Runtime/Misc/SceneUnloading.h
new file mode 100644
index 0000000..3657f73
--- /dev/null
+++ b/Runtime/Misc/SceneUnloading.h
@@ -0,0 +1 @@
+void UnloadGameScene (); \ No newline at end of file
diff --git a/Runtime/Misc/SystemInfo.h b/Runtime/Misc/SystemInfo.h
new file mode 100644
index 0000000..7e25cf6
--- /dev/null
+++ b/Runtime/Misc/SystemInfo.h
@@ -0,0 +1,320 @@
+#ifndef SYSTEM_INFO_H
+#define SYSTEM_INFO_H
+
+#include "Runtime/Modules/ExportModules.h"
+#include <string>
+
+enum RuntimePlatform
+{
+ // NEVER change constants of existing platforms!
+ OSXEditor = 0,
+ OSXPlayer = 1,
+ WindowsPlayer = 2,
+ OSXWebPlayer = 3,
+ OSXDashboardPlayer = 4,
+ WindowsWebPlayer = 5,
+ WiiPlayer = 6,
+ WindowsEditor = 7,
+ iPhonePlayer = 8,
+ PS3Player = 9,
+ XenonPlayer = 10,
+ AndroidPlayer = 11,
+ NaClWebPlayer = 12,
+ LinuxPlayer = 13,
+ LinuxWebPlayer = 14,
+ FlashPlayer = 15,
+ LinuxEditor = 16,
+ WebGLPlayer = 17,
+ MetroPlayerX86 = 18,
+ MetroPlayerX64 = 19,
+ MetroPlayerARM = 20,
+ WP8Player = 21,
+ BB10Player = 22,
+ TizenPlayer = 23,
+
+ RuntimePlatformCount // keep this last
+};
+
+enum DeviceType
+{
+ kDeviceTypeUnknown = 0,
+ kDeviceTypeHandheld,
+ kDeviceTypeConsole,
+ kDeviceTypeDesktop,
+ kDeviceTypeCount // keep this last
+};
+
+enum SystemLanguage {
+ SystemLanguageAfrikaans,
+ SystemLanguageArabic,
+ SystemLanguageBasque,
+ SystemLanguageBelarusian,
+ SystemLanguageBulgarian,
+ SystemLanguageCatalan,
+ SystemLanguageChinese,
+ SystemLanguageCzech,
+ SystemLanguageDanish,
+ SystemLanguageDutch,
+ SystemLanguageEnglish,
+ SystemLanguageEstonian,
+ SystemLanguageFaroese,
+ SystemLanguageFinnish,
+ SystemLanguageFrench,
+ SystemLanguageGerman,
+ SystemLanguageGreek,
+ SystemLanguageHebrew,
+ SystemLanguageHugarian,
+ SystemLanguageIcelandic,
+ SystemLanguageIndonesian,
+ SystemLanguageItalian,
+ SystemLanguageJapanese,
+ SystemLanguageKorean,
+ SystemLanguageLatvian,
+ SystemLanguageLithuanian,
+ SystemLanguageNorwegian,
+ SystemLanguagePolish,
+ SystemLanguagePortuguese,
+ SystemLanguageRomanian,
+ SystemLanguageRussian,
+ SystemLanguageSerboCroatian,
+ SystemLanguageSlovak,
+ SystemLanguageSlovenian,
+ SystemLanguageSpanish,
+ SystemLanguageSwedish,
+ SystemLanguageThai,
+ SystemLanguageTurkish,
+ SystemLanguageUkrainian,
+ SystemLanguageVietnamese,
+ SystemLanguageUnknown
+};
+
+namespace systeminfo {
+
+ std::string GetOperatingSystem();
+ std::string GetProcessorType();
+ int EXPORT_COREMODULE GetProcessorCount();
+ int GetNumberOfCores();
+ int GetPhysicalMemoryMB();
+ int GetUsedVirtualMemoryMB();
+ int GetExecutableSizeMB();
+ int GetSystemLanguage();
+ std::string GetSystemLanguageISO();
+
+#if UNITY_WIN
+ ULONG_PTR GetCoreAffinityMask(DWORD core);
+ std::string GetBIOSIdentifier();
+#endif
+
+#if UNITY_XENON
+ void SetExecutableSizeMB(UInt32 sizeMB);
+#endif
+
+#if UNITY_WIN || UNITY_OSX || UNITY_LINUX
+// Windows: 500=2000, 510=XP, 520=2003, 600=Vista
+// Mac: 1006=Snow Leopard
+// Linux: kernel version 206 = 2.6
+int GetOperatingSystemNumeric();
+
+int GetProcessorSpeed();
+std::string GetMacAddress();
+#endif
+
+#if UNITY_EDITOR && (UNITY_WIN || UNITY_OSX)
+unsigned char* GetMacAddressForBeast ();
+#endif
+
+#if WEBPLUG && UNITY_OSX
+bool IsRunningInDashboardWidget();
+#endif
+
+#if UNITY_WP8
+int GetCommitedMemoryLimitMB();
+int GetCommitedMemoryMB();
+#endif
+
+inline RuntimePlatform GetRuntimePlatform()
+{
+
+ #if UNITY_EDITOR
+ #if UNITY_OSX
+ return OSXEditor;
+ #elif UNITY_WIN
+ return WindowsEditor;
+ #elif UNITY_LINUX
+ return LinuxEditor;
+ #else
+ #error Unknown platform
+ #endif
+ #else
+ #if UNITY_OSX
+ #if WEBPLUG
+ #if !UNITY_PEPPER
+ if (systeminfo::IsRunningInDashboardWidget ())
+ return OSXDashboardPlayer;
+ else
+ #endif
+ return OSXWebPlayer;
+ #else
+ return OSXPlayer;
+ #endif
+ #elif UNITY_WIN && !UNITY_WINRT
+ #if WEBPLUG
+ return WindowsWebPlayer;
+ #else
+ return WindowsPlayer;
+ #endif
+ #elif UNITY_WP8
+ return WP8Player;
+ #elif UNITY_METRO
+ #if __arm__
+ return MetroPlayerARM;
+ #else
+ return MetroPlayerX86;
+ #endif
+ #elif UNITY_WII
+ return WiiPlayer;
+ #elif UNITY_XENON
+ return XenonPlayer;
+ #elif UNITY_PS3
+ return PS3Player;
+ #elif UNITY_IPHONE
+ return iPhonePlayer;
+ #elif UNITY_ANDROID
+ return AndroidPlayer;
+ #elif UNITY_BB10
+ return BB10Player;
+ #elif UNITY_TIZEN
+ return TizenPlayer;
+ #elif UNITY_PEPPER
+ #if UNITY_NACL_WEBPLAYER
+ // Since we want to be fully compatible with existing content
+ // we need to pretend we are a normal web player.
+ return WindowsWebPlayer;
+ #else
+ return NaClWebPlayer;
+ #endif
+ #elif UNITY_LINUX
+ #if WEBPLUG
+ return LinuxWebPlayer;
+ #else
+ return LinuxPlayer;
+ #endif
+ #elif UNITY_FLASH
+ return FlashPlayer;
+ #elif UNITY_WEBGL
+ return WebGLPlayer;
+ #else
+ #error Unknown platform
+ #endif
+ #endif
+}
+
+inline bool IsPlatformStandalone( RuntimePlatform p ) {
+ return p==WindowsPlayer || p==OSXPlayer || p == LinuxPlayer;
+}
+inline bool IsPlatformWebPlayer( RuntimePlatform p ) {
+ return p==WindowsWebPlayer || p==OSXWebPlayer || p==OSXDashboardPlayer;
+}
+
+inline std::string GetRuntimePlatformString(RuntimePlatform p)
+{
+ switch (p)
+ {
+ case OSXEditor: return "OSXEditor";
+ case OSXPlayer: return "OSXPlayer";
+ case WindowsPlayer: return "WindowsPlayer";
+ case OSXWebPlayer: return "OSXWebPlayer";
+ case OSXDashboardPlayer: return "OSXDashboardPlayer";
+ case WindowsWebPlayer: return "WindowsWebPlayer";
+ case WiiPlayer: return "WiiPlayer";
+ case WindowsEditor: return "WindowsEditor";
+ case iPhonePlayer: return "iPhonePlayer";
+ case PS3Player: return "PS3Player";
+ case XenonPlayer: return "XenonPlayer";
+ case AndroidPlayer: return "AndroidPlayer";
+ case NaClWebPlayer: return "NaClWebPlayer";
+ case LinuxPlayer: return "LinuxPlayer";
+ case LinuxWebPlayer: return "LinuxWebPlayer";
+ case FlashPlayer: return "FlashPlayer";
+ case LinuxEditor: return "LinuxEditor";
+ case WebGLPlayer: return "WebGL";
+ case MetroPlayerX86: return "MetroPlayerX86";
+ case MetroPlayerX64: return "MetroPlayerX64";
+ case MetroPlayerARM: return "MetroPlayerARM";
+ case WP8Player: return "WP8Player";
+ case BB10Player: return "BB10Player";
+ case TizenPlayer: return "TizenPlayer";
+ default:
+ #if !UNITY_EXTERNAL_TOOL
+ AssertString("Unknown platform.");
+ #endif
+ return "Unknown";
+ }
+}
+
+inline std::string GetRuntimePlatformString()
+{
+ return GetRuntimePlatformString(GetRuntimePlatform());
+}
+
+ std::string GetPersistentDataPath(); /// A path for data that can be considered "long-lived", possibly to time of un-installation.
+ std::string GetTemporaryCachePath(); /// A path for "short-term" data, that may out-live the running session.
+#if !UNITY_FLASH
+ char const* GetDeviceUniqueIdentifier ();
+ char const* GetDeviceName ();
+ char const* GetDeviceModel ();
+ char const* GetDeviceSystemName ();
+ char const* GetDeviceSystemVersion ();
+#endif
+
+ inline bool IsHandheldPlatform ()
+ {
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ return true;
+#else
+ return false;
+#endif
+ }
+
+#if UNITY_WINRT
+ bool SupportsAccelerometer ();
+#else
+ inline bool SupportsAccelerometer ()
+ {
+ return IsHandheldPlatform ();
+ }
+#endif
+
+ inline bool SupportsLocationService ()
+ {
+ return IsHandheldPlatform ();
+ }
+
+
+#if UNITY_ANDROID || UNITY_IPHONE
+ bool SupportsVibration ();
+#else
+ inline bool SupportsVibration ()
+ {
+ return IsHandheldPlatform ();
+ }
+#endif
+
+
+ inline DeviceType DeviceType ()
+ {
+#if UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_WP8 || UNITY_TIZEN
+ return kDeviceTypeHandheld;
+#elif UNITY_EDITOR || UNITY_PEPPER || UNITY_FLASH \
+ || UNITY_OSX || (UNITY_WIN && !UNITY_WP8) || UNITY_LINUX || UNITY_WEBGL
+ return kDeviceTypeDesktop;
+#elif UNITY_WII || UNITY_XENON || UNITY_PS3
+ return kDeviceTypeConsole;
+#else
+ #error Should never get here. Add your platform.
+ return kDeviceTypeUknown;
+#endif
+ }
+} // namespace
+
+#endif
diff --git a/Runtime/Misc/UTF8.cpp b/Runtime/Misc/UTF8.cpp
new file mode 100644
index 0000000..defdfea
--- /dev/null
+++ b/Runtime/Misc/UTF8.cpp
@@ -0,0 +1,376 @@
+#include "UnityPrefix.h"
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+
+
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "UTF8.h"
+#ifdef CVTUTF_DEBUG
+#include <stdio.h>
+#endif
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return false;
+ /* Everything else falls through when "true"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xED: if (a > 0x9F) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ DebugAssertIf (target >= targetEnd);
+
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && source < sourceEnd) {
+ UTF32 ch2 = *source;
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x200000) { bytesToWrite = 4;
+ } else { bytesToWrite = 2;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 1: *--target = ch | firstByteMark[bytesToWrite];
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+
+bool ConvertUTF8toUTF16 (const char* source, int srcLength, UInt16* output, int& outlength)
+{
+ UInt16* newoutput = output;
+ const UTF8* src = (UTF8*)source;
+ if (ConvertUTF8toUTF16(&src, src + srcLength, &newoutput, newoutput + srcLength, lenientConversion) != sourceIllegal)
+ {
+ outlength = newoutput - output;
+ return true;
+ }
+ else
+ {
+ outlength = 0;
+ return false;
+ }
+}
+
+bool ConvertUTF8toUTF16 (const std::string& source, dynamic_array<UInt16>& utf16)
+{
+ utf16.resize_uninitialized(source.size());
+ int length = 0;
+ bool success = ConvertUTF8toUTF16(source.data(), (int) source.size(), utf16.begin(), length);
+ utf16.resize_uninitialized(length);
+ return success;
+}
+
+bool ConvertUTF16toUTF8 (const UInt16* source, int srcLength, char* output, int& outlength)
+{
+ UTF8* newoutput = (UTF8*)output;
+ const UTF16* src = (UTF16*)source;
+ if (ConvertUTF16toUTF8(&src, src + srcLength, &newoutput, newoutput + (srcLength*4), lenientConversion) != sourceIllegal)
+ {
+ outlength = newoutput - (UTF8*)output;
+ return true;
+ }
+ else
+ {
+ outlength = 0;
+ return false;
+ }
+}
+
+
+bool ConvertUTF16toUTF8 (const dynamic_array<UnicodeChar>& source, std::string& utf8)
+{
+ utf8.resize(source.size()*4);
+ int length = 0;
+ bool success = ConvertUTF16toUTF8(source.data(), (int) source.size(), &utf8[0], length);
+ utf8.resize(length);
+ return success;
+}
+
+bool ConvertUTF16toUTF8 (const UInt16 utf16character, std::string& utf8)
+{
+ int len;
+ char character[5];
+ if (!ConvertUTF16toUTF8 (&utf16character, 1, character, len))
+ return false;
+ character[len] = 0;
+ utf8 = std::string(character);
+ return true;
+}
diff --git a/Runtime/Misc/UTF8.h b/Runtime/Misc/UTF8.h
new file mode 100644
index 0000000..c21a367
--- /dev/null
+++ b/Runtime/Misc/UTF8.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+
+typedef UInt16 UnicodeChar;
+
+bool ConvertUTF8toUTF16 (const char* source, int srcLength, UnicodeChar* output, int& outlength);
+bool ConvertUTF8toUTF16 (const std::string& source, dynamic_array<UnicodeChar>& utf16);
+bool ConvertUTF16toUTF8 (const UInt16* source, int srcLength, char* output, int& outlength);
+bool ConvertUTF16toUTF8 (const dynamic_array<UnicodeChar>& source, std::string& utf8);
+bool ConvertUTF16toUTF8 (const UInt16* source, int srcLength, char* output, int& outlength);
+bool ConvertUTF16toUTF8 (const UInt16 utf16character, std::string& utf8);
diff --git a/Runtime/Misc/UserList.cpp b/Runtime/Misc/UserList.cpp
new file mode 100644
index 0000000..ca2783e
--- /dev/null
+++ b/Runtime/Misc/UserList.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+#include "UserList.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/BaseClasses/GameObject.h"
+
+void UserListNode::Clear ()
+{
+ if (IsConnected())
+ {
+ UserList* other = static_cast<UserList*>(m_Entry.other);
+ other->RemoveIndex(m_Entry.indexInOther);
+ m_Entry = Entry();
+ }
+}
+
+void UserList::Clear ()
+{
+ for (int index=0; index<m_Entries.size(); index++)
+ {
+ Entry& user = m_Entries[index];
+ if (user.indexInOther == -1)
+ {
+ // Other is a node
+ UserListNode* other = static_cast<UserListNode*>(user.other);
+ DebugAssert(other->m_Entry.other == this);
+ DebugAssert(other->m_Entry.indexInOther == index);
+ other->m_Entry = Entry();
+ }
+ else
+ {
+ // Other is a another list
+ UserList* other = static_cast<UserList*>(user.other);
+ DebugAssert(other->m_Entries[user.indexInOther].other == this);
+ DebugAssert(other->m_Entries[user.indexInOther].indexInOther == index);
+ other->RemoveIndex(user.indexInOther);
+ }
+ }
+ m_Entries.clear();
+}
+
+void UserList::Reserve (size_t size)
+{
+ m_Entries.reserve(size);
+}
+
+void UserList::AddUser (UserListNode& other)
+{
+ other.Clear();
+ other.m_Entry.other = this;
+ other.m_Entry.indexInOther = m_Entries.size();
+ Entry& user = m_Entries.push_back();
+ user.other = &other;
+ user.indexInOther = -1;
+}
+
+void UserList::AddUser (UserList& other)
+{
+ DebugAssert(&other != this);
+ int index = m_Entries.size();
+ int indexInOther = other.m_Entries.size();
+ Entry& user = m_Entries.push_back();
+ user.other = &other;
+ user.indexInOther = indexInOther;
+ Entry& otherUser = other.m_Entries.push_back();
+ otherUser.other = this;
+ otherUser.indexInOther = index;
+}
+
+void UserList::SendMessage (const MessageIdentifier& msg)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ MessageData data;
+ // Traverse list backwards, makes it easier if an element removes itself
+ int index = (int)m_Entries.size() - 1;
+ while (index >= 0)
+ {
+ UserListBase* other = m_Entries[index].other;
+#if DEBUGMODE
+ // Verify that the link back is correct
+ GetEntryInOther(index);
+#endif
+ SendMessageDirect(*other->GetTarget(), msg, data);
+ // Make sure index is within range
+ index = std::min(index, (int)m_Entries.size());
+ index--;
+ }
+}
+
+UserList::Entry& UserList::GetEntryInOther (int index)
+{
+ Entry& user = m_Entries[index];
+ Entry* entryInOther;
+ if (user.indexInOther == -1)
+ {
+ // Other is a node
+ UserListNode* other = static_cast<UserListNode*>(user.other);
+ entryInOther = &other->m_Entry;
+ }
+ else
+ {
+ // Other is a another list
+ UserList* other = static_cast<UserList*>(user.other);
+ entryInOther = &other->m_Entries[user.indexInOther];
+ }
+ DebugAssert(entryInOther->other == this);
+ DebugAssert(entryInOther->indexInOther == index);
+ return *entryInOther;
+}
+
+void UserList::RemoveIndex (int index)
+{
+ int lastIndex = m_Entries.size() - 1;
+ if (index != lastIndex)
+ {
+ // Move last entry to index
+ Entry& lastUser = m_Entries[lastIndex];
+ m_Entries[index] = lastUser;
+ Entry& entryInOther = GetEntryInOther(lastIndex);
+ entryInOther.indexInOther = index;
+ }
+ m_Entries.pop_back();
+}
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+class TestUserList : public UserList
+{
+public:
+ TestUserList() : UserList(NULL) {}
+};
+
+class TestUserListNode : public UserListNode
+{
+public:
+ TestUserListNode() : UserListNode(NULL) {}
+};
+
+SUITE (UserListTests)
+{
+TEST(UserList_BasicUserList)
+{
+ const int kNumA = 10;
+ const int kNumB = 20;
+ const int kNumC = 30;
+ TestUserList listsA[kNumA];
+ TestUserList listsB[kNumB];
+ TestUserList listsC[kNumC];
+ TestUserListNode nodesD[kNumC];
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 3) == 1)
+ listsC[c].AddUser(nodesD[c]);
+ for (int a = 0; a < kNumA; a++)
+ for (int b = 0; b < kNumB; b++)
+ if (((a + b) % 3) == 0)
+ listsA[a].AddUser(listsB[b]);
+ for (int b = 0; b < kNumB; b++)
+ for (int c = 0; c < kNumC; c++)
+ if (((b + c) % 5) == 0)
+ listsB[b].AddUser(listsC[c]);
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 3) == 2)
+ listsC[c].AddUser(nodesD[c]);
+ for (int a = kNumA - 1; a >= 0; a--)
+ if ((a % 2) == 0)
+ listsA[a].Clear();
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 4) == 0)
+ nodesD[c].Clear();
+ for (int b = 0; b < kNumB; b++)
+ listsB[b].Clear();
+ for (int a = 0; a < kNumA; a++)
+ {
+ CHECK_EQUAL (0, listsA[a].GetSize());
+ }
+ for (int c = 0; c < kNumC; c++)
+ {
+ if ((c % 3) != 0 && (c % 4) != 0)
+ {
+ CHECK_EQUAL (1, listsC[c].GetSize());
+ CHECK_EQUAL (true, nodesD[c].IsConnected());
+ }
+ else
+ {
+ CHECK_EQUAL (0, listsC[c].GetSize());
+ CHECK_EQUAL (false, nodesD[c].IsConnected());
+ }
+ }
+}
+}
+
+#endif
diff --git a/Runtime/Misc/UserList.h b/Runtime/Misc/UserList.h
new file mode 100644
index 0000000..7999ba9
--- /dev/null
+++ b/Runtime/Misc/UserList.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/NonCopyable.h"
+#include "Runtime/Modules/ExportModules.h"
+
+class MessageIdentifier;
+
+// A UserList can be connected to other UserLists or UserListNodes.
+// A UserListNode is simply the optimized case of a single-element list.
+// The connected lists are symmetrical and can send messages in both directions.
+// Deleting a node or list will disconnect it from the graph.
+
+class EXPORT_COREMODULE UserListBase : public NonCopyable
+{
+public:
+ Object* GetTarget () { return m_Target; }
+
+protected:
+ UserListBase (Object* target) : m_Target(target) {}
+ struct Entry
+ {
+ Entry() : other(NULL), indexInOther(-1) {}
+ UserListBase* other;
+ int indexInOther;
+ };
+ Object* m_Target;
+};
+
+class UserListNode : public UserListBase
+{
+public:
+ UserListNode (Object* target) : UserListBase(target) {}
+ ~UserListNode () { Clear(); }
+
+ void Clear ();
+ bool IsConnected () const { return m_Entry.other != NULL; }
+
+private:
+ friend class UserList;
+ Entry m_Entry;
+};
+
+class EXPORT_COREMODULE UserList : public UserListBase
+{
+public:
+ UserList (Object* target) : UserListBase(target) {}
+ ~UserList () { Clear(); }
+
+ void Clear ();
+ void Reserve (size_t size);
+ void AddUser (UserListNode& other);
+ void AddUser (UserList& other);
+ void SendMessage (const MessageIdentifier& msg);
+ size_t GetSize () const { return m_Entries.size(); }
+
+private:
+ friend class UserListNode;
+ Entry& GetEntryInOther (int index);
+ void RemoveIndex (int index);
+
+ dynamic_array<Entry> m_Entries;
+};
diff --git a/Runtime/Misc/WWWCached.cpp b/Runtime/Misc/WWWCached.cpp
new file mode 100644
index 0000000..17b7e2a
--- /dev/null
+++ b/Runtime/Misc/WWWCached.cpp
@@ -0,0 +1,319 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_CACHING && ENABLE_WWW
+#include "WWWCached.h"
+#include "Runtime/Misc/CachingManager.h"
+#include "PlatformDependent/CommonWebPlugin/UnityWebStream.h"
+#include "Runtime/Misc/AssetBundleUtility.h"
+#include "Runtime/Misc/SaveAndLoadHelper.h"
+#include "Runtime/Misc/AssetBundle.h"
+
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/IsPlaying.h"
+#endif
+
+void WWWCached::StartDownload(bool cached)
+{
+ if (m_WWW != NULL)
+ {
+ m_WWW->Release();
+ m_WWW = NULL;
+ }
+
+ printf_console("starting www download: %s\n", m_URL);
+ WWWHeaders headers;
+ m_WWW = WWW::Create(m_URL, NULL, 0, headers, true, cached, m_CacheVersion, m_RequestedCRC);
+ m_WWW->SetThreadPriority(m_ThreadPriority);
+}
+
+bool WWWCached::IsDownloadingDone () const
+{
+ // Actually, IsDone() ought to be simplified, but this temporary hack
+ // alows the class to compile properly.
+ return const_cast<WWWCached*>(this)->IsDoneImpl();
+}
+
+bool WWWCached::IsDoneImpl()
+{
+ if (m_Abort)
+ return true;
+ if (m_WWW != NULL)
+ {
+ if (m_WWW->IsDone ())
+ {
+ if (!m_WWW->IsCached())
+ {
+ // downloaded to memory.
+ if (m_WWW->GetError() != NULL)
+ {
+ m_Error = m_WWW->GetError();
+ m_Abort = true;
+ }
+ // no error: success. we are done.
+ else return true;
+ }
+ else
+ {
+ // downloaded to cache
+ if (m_WWW->GetError() != NULL)
+ {
+ UnityWebStream* stream = m_WWW->GetUnityWebStream();
+
+ if (stream && !stream->GetError().empty())
+ {
+ // we might have had an error caching to disk.
+ // try memory download instead.
+ StartDownload (false);
+ return false;
+ }
+ else
+ {
+ m_Error = m_WWW->GetError();
+ m_Abort = true;
+ }
+ }
+ else
+ {
+ m_DidDownload = true;
+ printf_console("loading from cache: %s\n", m_URL);
+ m_CacheRequest = GetCachingManager().LoadCached(m_URL, m_CacheVersion, m_RequestedCRC);
+ if (m_CacheRequest == NULL)
+ {
+ // error: try memory download instead.
+ StartDownload (false);
+ return false;
+ }
+ }
+ }
+ m_WWW->Release();
+ m_WWW = NULL;
+ }
+ }
+ else if (m_CacheRequest != NULL)
+ {
+ if (m_CacheRequest->IsDone ())
+ {
+ if (m_CacheRequest->DidSucceed ())
+ {
+ AssetBundle* bundle = m_CacheRequest->GetAssetBundle ();
+
+ ////WORKAROUND!
+ //// Same workaround as in ExtractAssetBundle (unfortunately, we have two different
+ //// places where we load bundles from WWW).
+ bool performCompatibilityChecks = true;
+ #if UNITY_WEBPLAYER
+ const bool isStreamedSceneBundle =
+ bundle->m_CachedUnityWebStream->m_Files[0].find ("BuildPlayer-") == 0 ||
+ (bundle->m_CachedUnityWebStream->m_Files.size () > 1 && bundle->m_CachedUnityWebStream->m_Files[1].find ("BuildPlayer-") == 0);
+ performCompatibilityChecks = !isStreamedSceneBundle;
+ #endif
+
+ // Verify that we can actually use the bundle and
+ // abort if we don't.
+ if (performCompatibilityChecks && bundle && !TestAssetBundleCompatibility (*bundle, m_URL, m_Error))
+ {
+ m_Abort = true;
+ return false;
+ }
+
+ return true;
+ }
+ else
+ {
+ if (!m_CacheRequest->AssetBundleWithSameNameIsAlreadyLoaded ())
+ {
+ // File not in cache. Try downloading it to the cache
+ if (!m_DidDownload)
+ StartDownload (GetCachingManager().GetCurrentCache().GetIsReady());
+ else
+ // We already tried downloading to the cache without success. Try memory download instead.
+ StartDownload (false);
+ }
+ else
+ {
+ m_Error = m_CacheRequest->GetError();
+ m_Abort = true;
+ }
+ m_CacheRequest->Release();
+ m_CacheRequest = NULL;
+ }
+ }
+ }
+ return false;
+}
+
+float WWWCached::GetProgress () const
+{
+ if (m_DidDownload)
+ return 1.0f;
+ else if (m_CacheRequest != NULL && m_CacheRequest->DidSucceed ())
+ return 1.0f;
+ else if (m_WWW != NULL)
+ return m_WWW->GetProgress();
+ else
+ return 0.0f;
+}
+
+double WWWCached::GetETA () const
+{
+ if (m_DidDownload)
+ return 0.0;
+ else if (m_CacheRequest != NULL && m_CacheRequest->DidSucceed ())
+ return 0.0;
+ else if (m_WWW != NULL)
+ return m_WWW->GetETA();
+ else
+ return 0.0;
+}
+
+WWWCached::WWWCached (const char* url, int version, UInt32 crc)
+ : WWW (false, 0, crc)
+{
+ m_URL = (char*)malloc(strlen(url)+1);
+ AssertIf(!m_URL);
+ strcpy(m_URL, url);
+ m_Abort = false;
+ m_DidDownload = false;
+ m_WWW = NULL;
+ m_CacheRequest = NULL;
+ m_CacheVersion = version;
+ m_AssetBundleRetrieved = false;
+
+#if UNITY_EDITOR
+ if (!IsWorldPlaying())
+ {
+ m_Error = "WWW.LoadFromCacheOrDownload is only available in play mode.";
+ m_Abort = true;
+ return;
+ }
+#endif
+
+ if (!GetCachingManager().GetEnabled())
+ {
+ StartDownload(false);
+ return;
+ }
+ m_CacheRequest = GetCachingManager().LoadCached(m_URL, m_CacheVersion, crc);
+ if (m_CacheRequest == NULL)
+ {
+ // error: try memory download instead.
+ StartDownload (false);
+ return;
+ }
+}
+
+WWWCached::~WWWCached ()
+{
+ free (m_URL);
+ if (m_CacheRequest != NULL)
+ m_CacheRequest->Release();
+
+ if (m_WWW != NULL)
+ m_WWW->Release();
+}
+
+const char* WWWCached::GetError()
+{
+ if(!m_Error.empty())
+ return m_Error.c_str();
+ else if (m_WWW != NULL)
+ return m_WWW->GetError();
+ else
+ return NULL;
+}
+
+void WWWCached::Cancel()
+{
+ if (m_WWW != NULL)
+ m_WWW->Cancel();
+
+ if ( m_CacheRequest != NULL && !m_AssetBundleRetrieved )
+ {
+ // If a cached request was canceled early, then we have to wait until
+ // the request is complete on the other thread. Otherwise, AssetBundle
+ // collisions may occur if we don't unload the asset bundle.
+
+ while (!m_CacheRequest->IsDone ())
+ {
+ GetPreloadManager().UpdatePreloading();
+ Thread::Sleep(0.05f);
+ }
+
+ if (m_CacheRequest->DidSucceed ())
+ {
+ AssetBundle* bundle = GetAssetBundle();
+ if ( bundle )
+ UnloadAssetBundle( *bundle, true );
+ }
+ }
+ m_Abort = true;
+}
+
+void WWWCached::SetThreadPriority( ThreadPriority priority )
+{
+ if (m_WWW != NULL)
+ m_WWW->SetThreadPriority(priority);
+}
+
+void WWWCached::BlockUntilDone ()
+{
+ while (!IsDone())
+ {
+ if (m_WWW != NULL)
+ m_WWW->BlockUntilDone();
+
+ if ( m_CacheRequest != NULL )
+ GetPreloadManager().UpdatePreloading();
+
+ Thread::Sleep(0.05f);
+ }
+}
+
+bool WWWCached::HasDownloadedOrMayBlock ()
+{
+ if (GetError () != NULL)
+ {
+ ErrorString(Format("You are trying to load data from a www stream which had the following error when downloading.\n%s", GetError()));
+ return false;
+ }
+
+ if (m_WWW != NULL)
+ return m_WWW->HasDownloadedOrMayBlock();
+ return true;
+}
+
+AssetBundle* WWWCached::GetAssetBundle ()
+{
+ BlockUntilDone ();
+ if (m_WWW && !m_WWW->IsCached())
+ return ExtractAssetBundle (*m_WWW);
+ else if (m_CacheRequest != NULL)
+ {
+ m_AssetBundleRetrieved = true;
+ return m_CacheRequest->GetAssetBundle();
+ }
+ return NULL;
+}
+
+const UInt8* WWWCached::GetData()
+{
+ ErrorString(kWWWCachedAccessError);
+ return NULL;
+}
+const UInt8* WWWCached::GetPartialData() const
+{
+ ErrorString(kWWWCachedAccessError);
+ return NULL;
+}
+size_t WWWCached::GetSize()
+{
+ ErrorString(kWWWCachedAccessError);
+ return 0;
+}
+size_t WWWCached::GetPartialSize() const
+{
+ ErrorString(kWWWCachedAccessError);
+ return 0;
+}
+
+#endif //ENABLE_CACHING && ENABLE_WWW
diff --git a/Runtime/Misc/WWWCached.h b/Runtime/Misc/WWWCached.h
new file mode 100644
index 0000000..f0146c3
--- /dev/null
+++ b/Runtime/Misc/WWWCached.h
@@ -0,0 +1,59 @@
+#ifndef WWWCACHED_H
+#define WWWCACHED_H
+
+#define kWWWCachedAccessError "WWWCached data can only be accessed using the assetBundle property!"
+
+#if ENABLE_CACHING && ENABLE_WWW
+
+#include "Runtime/Export/WWW.h"
+
+
+class WWWCached : public WWW
+{
+ char* m_URL;
+ bool m_DidDownload;
+ bool m_Abort;
+ WWW* m_WWW;
+ string m_Error;
+ AsyncCachedUnityWebStream* m_CacheRequest;
+ bool m_AssetBundleRetrieved;
+
+ void StartDownload(bool cached);
+
+public:
+ WWWCached (const char* url, int version, UInt32 crc);
+ ~WWWCached ();
+
+ AssetBundle* GetAssetBundle ();
+
+ virtual const UInt8* GetData();
+ virtual const UInt8* GetPartialData() const;
+ virtual size_t GetSize();
+ virtual size_t GetPartialSize() const;
+
+ virtual double GetETA() const;
+
+ virtual void LockPartialData() {}
+ virtual void UnlockPartialData() {}
+
+ // Returns true when the download is complete or failed.
+ virtual void Cancel();
+ virtual bool IsDownloadingDone() const;
+ virtual float GetProgress() const;
+ virtual float GetUploadProgress() const { return 0.0f; }
+ virtual const char* GetError();
+ virtual const char* GetUrl() const { return m_URL; }
+
+ virtual bool HasDownloadedOrMayBlock ();
+ virtual void BlockUntilDone ();
+
+ virtual void SetThreadPriority( ThreadPriority priority );
+
+ virtual WWWType GetType () const { return kWWWTypeCached; }
+
+private:
+ bool IsDoneImpl();
+};
+
+#endif //ENABLE_CACHING
+#endif