public override void OnImportAsset(AssetImportContext ctx) { try { ctx.DependsOnCustomDependency("EntityBinaryFileFormatVersion"); var sceneWithBuildConfiguration = SceneWithBuildConfigurationGUIDs.ReadFromFile(ctx.assetPath); // Ensure we have as many dependencies as possible registered early in case an exception is thrown var scenePath = AssetDatabase.GUIDToAssetPath(sceneWithBuildConfiguration.SceneGUID.ToString()); ctx.DependsOnSourceAsset(scenePath); if (sceneWithBuildConfiguration.BuildConfiguration.IsValid) { var buildConfigurationPath = AssetDatabase.GUIDToAssetPath(sceneWithBuildConfiguration.BuildConfiguration.ToString()); ctx.DependsOnSourceAsset(buildConfigurationPath); var buildConfigurationDependencies = AssetDatabase.GetDependencies(buildConfigurationPath); foreach (var dependency in buildConfigurationDependencies) { ctx.DependsOnSourceAsset(dependency); } } var dependencies = AssetDatabase.GetDependencies(scenePath); foreach (var dependency in dependencies) { if (dependency.ToLower().EndsWith(".prefab")) { ctx.DependsOnSourceAsset(dependency); } } var config = BuildConfiguration.LoadAsset(sceneWithBuildConfiguration.BuildConfiguration); var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive); try { var settings = new GameObjectConversionSettings(); settings.SceneGUID = sceneWithBuildConfiguration.SceneGUID; settings.BuildConfiguration = config; settings.AssetImportContext = ctx; var sectionRefObjs = new List <ReferencedUnityObjects>(); var sectionData = EditorEntityScenes.ConvertAndWriteEntityScene(scene, settings, sectionRefObjs); WriteRefGuids(sectionRefObjs, sectionData, ctx); } finally { EditorSceneManager.CloseScene(scene, true); } } // Currently it's not acceptable to let the asset database catch the exception since it will create a default asset without any dependencies // This means a reimport will not be triggered if the scene is subsequently modified catch (Exception e) { Debug.Log($"Exception thrown during SubScene import: {e}"); } }
public override void OnImportAsset(AssetImportContext ctx) { try { var sceneWithBuildSettings = ReadSceneWithBuildSettings(ctx.assetPath); // Ensure we have as many dependencies as possible registered early in case an exception is thrown var scenePath = AssetDatabase.GUIDToAssetPath(sceneWithBuildSettings.SceneGUID.ToString()); ctx.DependsOnSourceAsset(scenePath); if (sceneWithBuildSettings.BuildSettings.IsValid) { var buildSettingPath = AssetDatabase.GUIDToAssetPath(sceneWithBuildSettings.BuildSettings.ToString()); ctx.DependsOnSourceAsset(buildSettingPath); var buildSettingDependencies = AssetDatabase.GetDependencies(buildSettingPath); foreach (var dependency in buildSettingDependencies) { ctx.DependsOnSourceAsset(dependency); } } var dependencies = AssetDatabase.GetDependencies(scenePath); foreach (var dependency in dependencies) { if (dependency.ToLower().EndsWith(".prefab")) { ctx.DependsOnSourceAsset(dependency); } } var buildSettings = BuildSettings.LoadBuildSettings(sceneWithBuildSettings.BuildSettings); var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive); try { var settings = new GameObjectConversionSettings(); settings.SceneGUID = sceneWithBuildSettings.SceneGUID; settings.BuildSettings = buildSettings; settings.AssetImportContext = ctx; EditorEntityScenes.WriteEntityScene(scene, settings); } finally { EditorSceneManager.CloseScene(scene, true); } } // Currently it's not acceptable to let the asset database catch the exception since it will create a default asset without any dependencies // This means a reimport will not be triggered if the scene is subsequently modified catch (Exception e) { Debug.Log($"Exception thrown during SubScene import: {e.Message}"); } }
static void RegisterDependencies(AssetImportContext importContext, ConversionDependencies dependencies) { using (var assets = dependencies.AssetDependentsByInstanceId.GetKeyArray(Allocator.Temp)) { for (int i = 0; i < assets.Length; i++) { var asset = EditorUtility.InstanceIDToObject(assets[i]); importContext.DependsOnSourceAsset(AssetDatabase.GetAssetPath(asset)); } } }
static void EnsureFileIsWritableOrThrow(string path, AssetImportContext ctx) { if (ctx != null) { return; } // We're going to do file writing manually, so make sure to do version control dance if needed if (Provider.isActive && File.Exists(path) && !AssetDatabase.IsOpenForEdit(path, StatusQueryOptions.UseCachedIfPossible)) { var task = Provider.Checkout(path, CheckoutMode.Asset); task.Wait(); if (!task.success) { throw new System.Exception($"Failed to checkout entity cache file {path}"); } } }
/// <summary> /// Describe how to create an Prefab for the current SRP, have to be reimplemented for each SRP. /// </summary> /// <param name="ctx">Context used from the asset importer</param> /// <param name="iesFileName">Filename of the current IES file</param> /// <param name="useIESMaximumIntensity">True if uses the internal Intensity from the file</param> /// <param name="iesMaximumIntensityUnit">The string of the units described by the intensity</param> /// <param name="iesMaximumIntensity">Intensity</param> /// <param name="light">Light used for the prefab</param> /// <param name="ies">Texture used for the prefab</param> /// <returns></returns> static public void CreateRenderPipelinePrefabLight(UnityEditor.Experimental.AssetImporters.AssetImportContext ctx, string iesFileName, bool useIESMaximumIntensity, string iesMaximumIntensityUnit, float iesMaximumIntensity, Light light, Texture ies) { HDLightTypeAndShape hdLightTypeAndShape = (light.type == LightType.Point) ? HDLightTypeAndShape.Point : HDLightTypeAndShape.ConeSpot; HDAdditionalLightData hdLight = GameObjectExtension.AddHDLight(light.gameObject, hdLightTypeAndShape); if (useIESMaximumIntensity) { LightUnit lightUnit = (iesMaximumIntensityUnit == "Lumens") ? LightUnit.Lumen : LightUnit.Candela; hdLight.SetIntensity(iesMaximumIntensity, lightUnit); if (light.type == LightType.Point) { hdLight.IESPoint = ies; } else { hdLight.IESSpot = ies; } } // The light object will be automatically converted into a prefab. ctx.AddObjectToAsset(iesFileName + "-HDRP", light.gameObject); }
static void RegisterDependencies(AssetImportContext importContext, ConversionDependencies dependencies) { using (var assets = dependencies.AssetDependentsByInstanceId.GetKeyArray(Allocator.Temp)) { for (int i = 0; i < assets.Length; i++) { var asset = EditorUtility.InstanceIDToObject(assets[i]); if (asset == null) { var dependents = FormatDependents(assets[i]); string errorMsg = $"Invalid asset dependency on instance ID {assets[i]} - this instance ID does not correspond to an object.\n" + "This dependency was registered by: " + dependents; Debug.LogWarning(errorMsg); continue; } var path = AssetDatabase.GetAssetPath(asset); if (string.IsNullOrEmpty(path)) { var dependents = FormatDependents(assets[i]); string errorMsg = $"Invalid asset dependency on object {asset.name}. This object does not have a valid asset path.\n" + "This dependency was registered by: " + dependents; Debug.LogWarning(errorMsg, asset); continue; } var guid = new GUID(AssetDatabase.AssetPathToGUID(path)); if (GUIDHelper.IsBuiltinAsset(in guid)) { // AssetImportContext does not support dependencies on inbuilt assets continue; } if (guid.Empty()) { // This should never happen var dependents = FormatDependents(assets[i]); string errorMsg = $"Invalid asset dependency on object {asset.name} at path {path}. It doesn't have a valid GUID.\n" + "This dependency was registered by: " + dependents; Debug.LogWarning(errorMsg, asset); continue; } importContext.DependsOnSourceAsset(path); } } string FormatDependents(int assetInstance) { var iter = dependencies.AssetDependentsByInstanceId.GetValuesForKey(assetInstance); string deps = ""; while (iter.MoveNext()) { if (deps.Length > 0) { deps += ", "; } var obj = EditorUtility.InstanceIDToObject(iter.Current); deps += $"{(obj == null ? "NULL" : obj.name)}"; } return(deps); } }
internal static string GetSceneWritePath(EntityScenesPaths.PathType type, string subsectionName, AssetImportContext ctx) { var prefix = string.IsNullOrEmpty(subsectionName) ? "" : subsectionName + "."; var path = ctx.GetResultPath(prefix + EntityScenesPaths.GetExtension(type)); return(path); }
public static SceneSectionData[] WriteEntityScene(EntityManager entityManager, Hash128 sceneGUID, string sceneName, AssetImportContext importContext, int framesToRetainBlobAssets = 0, List <ReferencedUnityObjects> sectionRefObjs = null) { if (importContext != null) { using (var allTypes = new NativeHashMap <ComponentType, int>(100, Allocator.Temp)) using (var archetypes = new NativeList <EntityArchetype>(Allocator.Temp)) { entityManager.GetAllArchetypes(archetypes); foreach (var archetype in archetypes) { using (var componentTypes = archetype.GetComponentTypes()) foreach (var componentType in componentTypes) { if (allTypes.TryAdd(componentType, 0)) { TypeDependencyCache.AddDependency(importContext, componentType); } } } } TypeDependencyCache.AddAllSystemsDependency(importContext); } var sceneSections = new List <SceneSectionData>(); var subSectionList = new List <SceneSection>(); entityManager.GetAllUniqueSharedComponentData(subSectionList); //Order sections by section id subSectionList.Sort(Comparer <SceneSection> .Create((a, b) => a.Section.CompareTo(b.Section))); var extRefInfoEntities = new NativeArray <Entity>(subSectionList.Count, Allocator.Temp); NativeArray <Entity> entitiesInMainSection; var sectionQuery = entityManager.CreateEntityQuery( new EntityQueryDesc { All = new[] { ComponentType.ReadWrite <SceneSection>() }, Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled } ); var sectionBoundsQuery = entityManager.CreateEntityQuery( new EntityQueryDesc { All = new[] { ComponentType.ReadWrite <SceneBoundingVolume>(), ComponentType.ReadWrite <SceneSection>() }, Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled } ); { var section = new SceneSection { SceneGUID = sceneGUID, Section = 0 }; sectionQuery.SetSharedComponentFilter(new SceneSection { SceneGUID = sceneGUID, Section = 0 }); sectionBoundsQuery.SetSharedComponentFilter(new SceneSection { SceneGUID = sceneGUID, Section = 0 }); entitiesInMainSection = sectionQuery.ToEntityArray(Allocator.TempJob); var bounds = GetBoundsAndRemove(entityManager, sectionBoundsQuery); // Each section will be serialized in its own world, entities that don't have a section are part of the main scene. // An entity that holds the array of external references to the main scene is required for each section. // We need to create them all before we start moving entities to section scenes, // otherwise they would reuse entities that have been moved and mess up the remapping tables. for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex) { if (subSectionList[sectionIndex].Section == 0) { // Main section, the only one that doesn't need an external ref array continue; } var extRefInfoEntity = entityManager.CreateEntity(); entityManager.AddSharedComponentData(extRefInfoEntity, subSectionList[sectionIndex]); extRefInfoEntities[sectionIndex] = extRefInfoEntity; } // Public references array, only on the main section. var refInfoEntity = entityManager.CreateEntity(); entityManager.AddBuffer <PublicEntityRef>(refInfoEntity); entityManager.AddSharedComponentData(refInfoEntity, section); var publicRefs = entityManager.GetBuffer <PublicEntityRef>(refInfoEntity); // entityManager.Debug.CheckInternalConsistency(); //@TODO do we need to keep this index? doesn't carry any additional info for (int i = 0; i < entitiesInMainSection.Length; ++i) { PublicEntityRef.Add(ref publicRefs, new PublicEntityRef { entityIndex = i, targetEntity = entitiesInMainSection[i] }); } UnityEngine.Debug.Assert(publicRefs.Length == entitiesInMainSection.Length); // Save main section var sectionWorld = new World("SectionWorld"); var sectionManager = sectionWorld.EntityManager; var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob); sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping); AddRetainBlobAssetsEntity(sectionManager, framesToRetainBlobAssets); // The section component is only there to break the conversion world into different sections // We don't want to store that on the disk //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why. //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection)); var sectionFileSize = WriteEntitySceneSection(sectionManager, "0", importContext, out var objectRefCount, out var objRefs); sectionRefObjs?.Add(objRefs); sceneSections.Add(new SceneSectionData { FileSize = sectionFileSize, SceneGUID = sceneGUID, ObjectReferenceCount = objectRefCount, SubSectionIndex = 0, BoundingVolume = bounds }); entityRemapping.Dispose(); sectionWorld.Dispose(); } { // Index 0 is the default value of the shared component, not an actual section for (int subSectionIndex = 0; subSectionIndex < subSectionList.Count; ++subSectionIndex) { var subSection = subSectionList[subSectionIndex]; if (subSection.Section == 0) { continue; } sectionQuery.SetSharedComponentFilter(subSection); sectionBoundsQuery.SetSharedComponentFilter(subSection); var bounds = GetBoundsAndRemove(entityManager, sectionBoundsQuery); var entitiesInSection = sectionQuery.ToEntityArray(Allocator.TempJob); if (entitiesInSection.Length > 0) { // Fetch back the external reference entity we created earlier to not disturb the mapping var refInfoEntity = extRefInfoEntities[subSectionIndex]; entityManager.AddBuffer <ExternalEntityRef>(refInfoEntity); var externRefs = entityManager.GetBuffer <ExternalEntityRef>(refInfoEntity); // Store the mapping to everything in the main section //@TODO maybe we don't need all that? is this worth worrying about? for (int i = 0; i < entitiesInMainSection.Length; ++i) { ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef { entityIndex = i }); } var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob); // Entities will be remapped to a contiguous range in the section world, but they will // also come with an unpredictable amount of meta entities. We have the guarantee that // the entities in the main section won't be moved over, so there's a free range of that // size at the end of the remapping table. So we use that range for external references. var externEntityIndexStart = entityRemapping.Length - entitiesInMainSection.Length; entityManager.AddComponentData(refInfoEntity, new ExternalEntityRefInfo { SceneGUID = sceneGUID, EntityIndexStart = externEntityIndexStart }); var sectionWorld = new World("SectionWorld"); var sectionManager = sectionWorld.EntityManager; // Insert mapping for external references, conversion world entity to virtual index in section for (int i = 0; i < entitiesInMainSection.Length; ++i) { EntityRemapUtility.AddEntityRemapping(ref entityRemapping, entitiesInMainSection[i], new Entity { Index = i + externEntityIndexStart, Version = 1 }); } sectionManager.MoveEntitiesFrom(entityManager, sectionQuery, entityRemapping); AddRetainBlobAssetsEntity(sectionManager, framesToRetainBlobAssets); // Now that all the required entities have been moved over, we can get rid of the gap between // real entities and external references. This allows remapping during load to deal with a // smaller remap table, containing only useful entries. int highestEntityIndexInUse = 0; for (int i = 0; i < externEntityIndexStart; ++i) { var targetIndex = entityRemapping[i].Target.Index; if (targetIndex < externEntityIndexStart && targetIndex > highestEntityIndexInUse) { highestEntityIndexInUse = targetIndex; } } var oldExternEntityIndexStart = externEntityIndexStart; externEntityIndexStart = highestEntityIndexInUse + 1; sectionManager.SetComponentData ( EntityRemapUtility.RemapEntity(ref entityRemapping, refInfoEntity), new ExternalEntityRefInfo { SceneGUID = sceneGUID, EntityIndexStart = externEntityIndexStart } ); // When writing the scene, references to missing entities are set to Entity.Null by default // (but only if they have been used, otherwise they remain untouched) // We obviously don't want that to happen to our external references, so we add explicit mapping // And at the same time, we put them back at the end of the effective range of real entities. for (int i = 0; i < entitiesInMainSection.Length; ++i) { var src = new Entity { Index = i + oldExternEntityIndexStart, Version = 1 }; var dst = new Entity { Index = i + externEntityIndexStart, Version = 1 }; EntityRemapUtility.AddEntityRemapping(ref entityRemapping, src, dst); } // The section component is only there to break the conversion world into different sections // We don't want to store that on the disk //@TODO: Component should be removed but currently leads to corrupt data file. Figure out why. //sectionManager.RemoveComponent(sectionManager.UniversalQuery, typeof(SceneSection)); var fileSize = WriteEntitySceneSection(sectionManager, subSection.Section.ToString(), importContext, out var objectRefCount, out var objRefs, entityRemapping); sectionRefObjs?.Add(objRefs); sceneSections.Add(new SceneSectionData { FileSize = fileSize, SceneGUID = sceneGUID, ObjectReferenceCount = objectRefCount, SubSectionIndex = subSection.Section, BoundingVolume = bounds }); entityRemapping.Dispose(); sectionWorld.Dispose(); } entitiesInSection.Dispose(); } } { var noSectionQuery = entityManager.CreateEntityQuery( new EntityQueryDesc { None = new[] { ComponentType.ReadWrite <SceneSection>() }, Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled } ); var sectionEntityQuery = entityManager.CreateEntityQuery( new EntityQueryDesc { None = new[] { ComponentType.ReadWrite <SectionMetadataSetup>() }, Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabled } ); var notSerializedCount = noSectionQuery.CalculateEntityCount() - sectionEntityQuery.CalculateEntityCount(); if (notSerializedCount != 0) { Debug.LogWarning($"{notSerializedCount} entities in the scene '{sceneName}' had no SceneSection and as a result were not serialized at all."); } } // Save the new header var sceneSectionsArray = sceneSections.ToArray(); WriteSceneHeader(sceneGUID, sceneSectionsArray, sceneName, importContext, entityManager); sectionQuery.Dispose(); sectionBoundsQuery.Dispose(); entitiesInMainSection.Dispose(); return(sceneSectionsArray); }
static void WriteHeader(Entities.Hash128 sceneGUID, SceneSectionData[] sections, string sceneName, AssetImportContext ctx) { k_ProfileEntitiesSceneSaveHeader.Begin(); string headerPath = GetSceneWritePath(sceneGUID, EntityScenesPaths.PathType.EntitiesHeader, "", ctx); EnsureFileIsWritableOrThrow(headerPath, ctx); var builder = new BlobBuilder(Allocator.TempJob); ref var metaData = ref builder.ConstructRoot <SceneMetaData>();
public static void AddAllSystemsDependency(AssetImportContext ctx) { ctx.DependsOnCustomDependency(SystemsVersion); }
public static void AddDependency(AssetImportContext ctx, ComponentType type) { var typeString = TypeString(type.GetManagedType()); ctx.DependsOnCustomDependency(typeString); }
static int WriteEntityScene(EntityManager scene, Hash128 sceneGUID, string subsection, AssetImportContext ctx, out int objectReferenceCount, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos = default) { k_ProfileEntitiesSceneSave.Begin(); var entitiesBinaryPath = GetSceneWritePath(sceneGUID, EntityScenesPaths.PathType.EntitiesBinary, subsection, ctx); var objRefsPath = GetSceneWritePath(sceneGUID, EntityScenesPaths.PathType.EntitiesUnityObjectReferences, subsection, ctx); ReferencedUnityObjects objRefs; objectReferenceCount = 0; EnsureFileIsWritableOrThrow(entitiesBinaryPath, ctx); // Write binary entity file int entitySceneFileSize = 0; using (var writer = new StreamBinaryWriter(entitiesBinaryPath)) { if (entityRemapInfos.IsCreated) { SerializeUtilityHybrid.Serialize(scene, writer, out objRefs, entityRemapInfos); } else { SerializeUtilityHybrid.Serialize(scene, writer, out objRefs); } entitySceneFileSize = (int)writer.Length; // Write object references k_ProfileEntitiesSceneWriteObjRefs.Begin(); if (objRefs != null) { var serializedObjectArray = new List <UnityObject>(); serializedObjectArray.Add(objRefs); for (int i = 0; i != objRefs.Array.Length; i++) { var obj = objRefs.Array[i]; if (obj != null && !EditorUtility.IsPersistent(obj)) { if ((obj.hideFlags & HideFlags.DontSaveInBuild) != 0) { serializedObjectArray.Add(obj); } else { objRefs.Array[i] = null; } } } UnityEditorInternal.InternalEditorUtility.SaveToSerializedFileAndForget(serializedObjectArray.ToArray(), objRefsPath, false); objectReferenceCount = objRefs.Array.Length; } k_ProfileEntitiesSceneWriteObjRefs.End(); } k_ProfileEntitiesSceneSave.End(); return(entitySceneFileSize); }
static void WriteRefGuids(List <ReferencedUnityObjects> referencedUnityObjects, SceneSectionData[] sectionData, AssetImportContext ctx) { for (var index = 0; index < referencedUnityObjects.Count; index++) { var sectionIndex = sectionData[index].SubSectionIndex; var objRefs = referencedUnityObjects[index]; if (objRefs == null) { continue; } var refGuidsPath = ctx.GetResultPath($"{sectionIndex}.{EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectRefGuids)}"); var runtimeGlobalObjectIds = ReferencedUnityObjectsToRuntimeGlobalObjectIds(objRefs); using (var refGuidWriter = new StreamBinaryWriter(refGuidsPath)) { refGuidWriter.Write(runtimeGlobalObjectIds.Length); refGuidWriter.WriteArray(runtimeGlobalObjectIds.AsArray()); } runtimeGlobalObjectIds.Dispose(); } }