protected virtual bool ConstructRootElements(out ICollection <UIElement> rootElements, out UIAssetBase.UIDesign editorSettings) { var uiAsset = (UIAssetBase)AssetCloner.Clone(Asset.Asset, AssetClonerFlags.ReferenceAsNull); editorSettings = uiAsset.Design; var elements = new List <UIElement>(); foreach (var part in uiAsset.Hierarchy.RootParts) { UIElementDesign elementDesign; if (uiAsset.Hierarchy.Parts.TryGetValue(part.Id, out elementDesign)) { elements.Add(elementDesign.UIElement); } } rootElements = elements; return(true); }
/// <summary> /// This method is called when an asset needs to be tracked /// </summary> /// <returns>AssetDependencies.</returns> private AssetDependencies TrackAsset(AssetId assetId) { lock (ThisLock) { AssetDependencies dependencies; if (Dependencies.TryGetValue(assetId, out dependencies)) { return(dependencies); } // TODO provide an optimized version of TrackAsset method // taking directly a well known asset (loaded from a Package...etc.) // to avoid session.FindAsset var assetItem = session.FindAsset(assetId); if (assetItem == null) { return(null); } // Clone the asset before using it in this instance to make sure that // we have some kind of immutable state // TODO: This is not handling shadow registry // No need to clone assets from readonly package var assetItemCloned = assetItem.Package.IsSystem ? assetItem : new AssetItem(assetItem.Location, AssetCloner.Clone(assetItem.Asset), assetItem.Package) { SourceFolder = assetItem.SourceFolder, }; dependencies = new AssetDependencies(assetItemCloned); // Adds to global list Dependencies.Add(assetId, dependencies); // Update dependencies UpdateAssetDependencies(dependencies); CheckAllDependencies(); return(dependencies); } }
public static EntityGroupAssetBase ExtractSceneClone(EntityGroupAssetBase source, Guid sourceRootEntity) { if (source == null) { throw new ArgumentNullException("source"); } // Note: Instead of copying the whole asset (with its potentially big hierarchy), we first copy the asset only (without the hierarchy), then the sub-hierarchy to extract. // create the hierarchy of the sub-tree var subTreeRoot = source.Hierarchy.Entities[sourceRootEntity].Entity; var subTreeHierarchy = new EntityHierarchyData { Entities = { subTreeRoot }, RootEntities = { sourceRootEntity } }; foreach (var subTreeEntity in subTreeRoot.EnumerateChildren(true)) { subTreeHierarchy.Entities.Add(source.Hierarchy.Entities[subTreeEntity.Id]); } // clone the entities of the sub-tree var clonedHierarchy = (EntityHierarchyData)AssetCloner.Clone(subTreeHierarchy); clonedHierarchy.Entities[sourceRootEntity].Entity.Transform.Parent = null; // set to null reference outside of the sub-tree EntityAnalysis.FixupEntityReferences(clonedHierarchy); // temporary nullify the hierarchy to avoid to clone it var sourceHierarchy = source.Hierarchy; source.Hierarchy = null; // clone asset without hierarchy var clonedAsset = (EntityGroupAssetBase)AssetCloner.Clone(source); clonedAsset.Hierarchy = clonedHierarchy; // revert the source hierarchy source.Hierarchy = sourceHierarchy; return(clonedAsset); }
private bool MergeAsset(AssetItem item, AssetItem existingBase, List <AssetBase> existingBaseParts) { // No need to clone existingBaseParts as they are already cloned var baseCopy = (Asset)AssetCloner.Clone(item.Asset.Base?.Asset); var newBaseCopy = (Asset)AssetCloner.Clone(existingBase?.Asset); // Computes the hash on the clone (so that we don't have a .Base/.BaseParts in them // TODO: We might want to store the hash in the asset in order to avoid a recompute at load time // (but would require a compute at save time) var baseId = AssetHash.Compute(baseCopy); var newBaseCopyId = AssetHash.Compute(newBaseCopy); // If the old base and new base are similar (including ids and overrides), we don't need to perform a merge if (baseId == newBaseCopyId) { return(true); } // Delegates actual merge to the asset implem var result = item.Asset.Merge(baseCopy, newBaseCopy, existingBaseParts); if (result.HasErrors) { result.CopyTo(log); return(false); } item.Asset = (Asset)result.Asset; if (item.Asset.Base != null) { item.Asset.Base = newBaseCopy != null ? new AssetBase(item.Asset.Base.Location, newBaseCopy) : null; } // Use new existing base parts if (existingBaseParts != null) { item.Asset.BaseParts = existingBaseParts; } item.IsDirty = true; return(true); }
public void TestWithNewTypeFromChild() { var baseAsset = new TestDiffAsset() { Name = "Red", Value = 1, Dynamic = new DiffValueTypeA() { Text = "Test1" } }; var newBaseAsset = (TestDiffAsset)AssetCloner.Clone(baseAsset); var baseItem = new AssetItem("/base", baseAsset); var childAsset = (TestDiffAsset)baseItem.CreateChildAsset(); // Make New on Name value on first element var objDesc = TypeDescriptorFactory.Default.Find(typeof(TestDiffAsset)); var memberDesc = objDesc.Members.First(t => t.Name == "Dynamic"); childAsset.SetOverride(memberDesc, OverrideType.New); // Override Dynamic and change type childAsset.Dynamic = new DiffValueTypeB() { Value = 2 }; var diff = new AssetDiff(baseAsset, childAsset, newBaseAsset) { UseOverrideMode = true }; var diffResult = diff.Compute(); var diffResultStripped = diffResult.FindLeafDifferences().ToList(); // Check at least one field has merge from asset 1 (Dynamic) Assert.True(diffResultStripped.Any(node => node.ChangeType == Diff3ChangeType.MergeFromAsset1)); // Check that merged result on Dynamic property is instance from asset2 var mergeResult = AssetMerge.Merge(diff, AssetMergePolicies.MergePolicyAsset2AsNewBaseOfAsset1); Assert.True(childAsset.Dynamic is DiffValueTypeB); Assert.AreEqual(2, ((DiffValueTypeB)childAsset.Dynamic).Value); }
public void TestHash() { var obj1 = new TestAssetClonerObject { Name = "Test1", SubObject = new TestAssetClonerObject() { Name = "Test2" }, ObjectWithAttachedReference = new TestObjectReference() }; // Create a fake reference to make sure that the attached reference will not be serialized var attachedReference = AttachedReferenceManager.GetOrCreateAttachedReference(obj1.ObjectWithAttachedReference); attachedReference.Url = "just_for_test"; attachedReference.Id = AssetId.New(); var obj2 = AssetCloner.Clone(obj1); var hash1 = AssetHash.Compute(obj1); var hash2 = AssetHash.Compute(obj2); Assert.AreEqual(hash1, hash2); obj1.Name = "Yes"; var hash11 = AssetHash.Compute(obj1); Assert.AreNotEqual(hash11, hash2); obj1.Name = "Test1"; var hash12 = AssetHash.Compute(obj1); Assert.AreEqual(hash12, hash2); obj2 = AssetCloner.Clone(obj1); var hash1WithOverrides = AssetHash.Compute(obj1); var hash2WithOverrides = AssetHash.Compute(obj2); Assert.AreEqual(hash1WithOverrides, hash2WithOverrides); }
private void Session_AssetDirtyChanged(Asset asset, bool oldValue, bool newValue) { // Don't update the dependency manager while saving (setting dirty flag to false) if (!isSessionSaving) { lock (ThisLock) { AssetDependencies dependencies; if (Dependencies.TryGetValue(asset.Id, out dependencies)) { dependencies.Item.Asset = AssetCloner.Clone(asset); UpdateAssetDependencies(dependencies); // Notify an asset changed OnAssetChanged(dependencies.Item, oldValue, newValue); } } CheckAllDependencies(); } }
public void TestMergeDictionaryNewKeyValue() { var baseDic = new DictionaryContainer() { Items = new Dictionary <string, string>() { { "A", "AValue" }, { "B", "BValue" }, { "C", "CValue" }, } }; var newDic = new DictionaryContainer() { Items = new Dictionary <string, string>(baseDic.Items) }; var newBaseDic = new DictionaryContainer() { Items = new Dictionary <string, string>() { { "A", "AValue" }, { "B", "BValue" }, { "C", "CValue" }, { "D", "DValue" }, } }; var diff = new AssetDiff(AssetCloner.Clone(baseDic), newDic, AssetCloner.Clone(newBaseDic)) { UseOverrideMode = true }; var result = AssetMerge.Merge(diff, AssetMergePolicies.MergePolicyAsset2AsNewBaseOfAsset1); Assert.False(result.HasErrors); Assert.AreEqual(4, newDic.Items.Count); }
public void TestCloneAssetWithRemoveOverrides() { var obj = new TestAssetClonerObject { Name = "Test1", SubObject = new TestAssetClonerObject() { Name = "Test2" } }; var objDesc = TypeDescriptorFactory.Default.Find(typeof(TestAssetClonerObject)); var memberDesc = objDesc.Members.First(t => t.Name == "Name"); obj.SetOverride(memberDesc, OverrideType.New); obj.SubObject.SetOverride(memberDesc, OverrideType.Sealed); var newInstance = (TestAssetClonerObject)AssetCloner.Clone(obj, AssetClonerFlags.RemoveOverrides); // Check that we are not overriding anything Assert.AreEqual(OverrideType.Base, newInstance.GetOverride(memberDesc)); Assert.AreEqual(OverrideType.Base, newInstance.SubObject.GetOverride(memberDesc)); }
public void TestWithNewTypeFromNewBase() { var baseAsset = new TestDiffAsset() { Name = "Red", Value = 1, Dynamic = new DiffValueTypeA() { Text = "Test1" } }; var newBaseAsset = (TestDiffAsset)AssetCloner.Clone(baseAsset); newBaseAsset.Dynamic = new DiffValueTypeB() { Value = 1 }; var baseItem = new AssetItem("/base", baseAsset); var childAsset = (TestDiffAsset)baseItem.CreateChildAsset(); var diff = new AssetDiff(baseAsset, childAsset, newBaseAsset) { UseOverrideMode = true }; var diffResult = diff.Compute(); var diffResultStripped = diffResult.FindLeafDifferences().ToList(); // Check that everything is merging from asset2 Assert.True(diffResultStripped.All(node => node.ChangeType == Diff3ChangeType.MergeFromAsset2)); // Check that merged result on Dynamic property is instance from asset2 var mergeResult = AssetMerge.Merge(diff, AssetMergePolicies.MergePolicyAsset2AsNewBaseOfAsset1); Assert.True(childAsset.Dynamic is DiffValueTypeB); Assert.AreEqual(1, ((DiffValueTypeB)childAsset.Dynamic).Value); }
private void MapEntities(EntityHierarchyData hierarchyData, Dictionary <GroupPartKey, EntityRemapEntry> entities, Guid?instancePartIdArg = null) { if (hierarchyData == null) { return; } // Important, if the hierarchy is coming from a part, we need to clone it entirely // and associate correctly the instancePartId with each entities that it is composed of. if (instancePartIdArg.HasValue) { hierarchyData = (EntityHierarchyData)AssetCloner.Clone(hierarchyData); } foreach (var entityDesign in hierarchyData.Entities) { if (instancePartIdArg.HasValue) { entityDesign.Design.BasePartInstanceId = instancePartIdArg; } var key = new GroupPartKey(instancePartIdArg, entityDesign.Entity.Id); if (entities.ContainsKey(key)) { continue; } var remap = new EntityRemapEntry(entityDesign, hierarchyData); // We are removing children from transform component for the diff remap.PushChildren(); entities[key] = remap; } }
public void TestPackageAnalysis() { var package = new Package(); var baseAsset = new TestDiffAsset() { Name = "Red", Value = 1 }; var baseItem = new AssetItem("base", baseAsset); var childAsset = (TestDiffAsset)baseItem.CreateChildAsset(); var childItem = new AssetItem("child", childAsset); var newBaseAsset = (TestDiffAsset)AssetCloner.Clone(baseAsset); newBaseAsset.Name = "Green"; var newBaseItem = new AssetItem("base", newBaseAsset); package.Assets.Add(newBaseItem); package.Assets.Add(childItem); var session = new PackageSession(); session.Packages.Add(package); var result = new LoggerResult(); var analysis = new PackageAssetTemplatingAnalysis(package, result); analysis.Run(); Assert.False(result.HasErrors); var assetModified = (TestDiffAsset)package.Assets.Find("child").Asset; Assert.AreEqual("Green", assetModified.Name); }
protected override Task <ResultStatus> DoCommandOverride(ICommandContext commandContext) { // Reduce trees on CPU //var materialReducer = new MaterialTreeReducer(material); //materialReducer.ReduceTrees(); //foreach (var reducedTree in materialReducer.ReducedTrees) //{ // material.Nodes[reducedTree.Key] = reducedTree.Value; //} // Reduce on GPU // TODO: Adapt GPU reduction so that it is compatible Android color/alpha separation // TODO: Use the build engine processed output textures instead of the imported one (not existing any more) // TODO: Set the reduced texture output format // TODO: The graphics device cannot be shared with the Previewer //var graphicsDevice = (GraphicsDevice)context.Attributes.GetOrAdd(CompilerContext.GraphicsDeviceKey, key => GraphicsDevice.New(DeviceCreationFlags.None, GraphicsProfile.Level_11_0)); //using (var materialTextureLayerFlattener = new MaterialTextureLayerFlattener(material, graphicsDevice)) //{ // materialTextureLayerFlattener.PrepareForFlattening(new UDirectory(assetUrl.Directory)); // if (materialTextureLayerFlattener.HasCommands) // { // var compiler = EffectCompileCommand.GetOrCreateEffectCompiler(context); // materialTextureLayerFlattener.Run(compiler); // // store Material with modified textures // material = materialTextureLayerFlattener.Material; // } //} // Check with Ben why DoCommandOverride is called without going through the constructor? var assetManager = new ContentManager(MicrothreadLocalDatabases.ProviderService); var materialContext = new MaterialGeneratorContext { GraphicsProfile = graphicsProfile, Content = assetManager, ColorSpace = colorSpace }; materialContext.AddLoadingFromSession(AssetFinder); var materialClone = AssetCloner.Clone(Parameters); var result = MaterialGenerator.Generate(new MaterialDescriptor { MaterialId = materialClone.Id, Attributes = materialClone.Attributes, Layers = materialClone.Layers }, materialContext, string.Format("{0}:{1}", materialClone.Id, assetUrl)); if (result.HasErrors) { result.CopyTo(commandContext.Logger); return(Task.FromResult(ResultStatus.Failed)); } // Separate the textures into color/alpha components on Android to be able to use native ETC1 compression //if (context.Platform == PlatformType.Android) //{ // var alphaComponentSplitter = new TextureAlphaComponentSplitter(assetItem.Package.Session); // material = alphaComponentSplitter.Run(material, new UDirectory(assetUrl.GetDirectory())); // store Material with alpha substituted textures //} // Create the parameters //var materialParameterCreator = new MaterialParametersCreator(material, assetUrl); //if (materialParameterCreator.CreateParameterCollectionData(commandContext.Logger)) // return Task.FromResult(ResultStatus.Failed); assetManager.Save(assetUrl, result.Material); return(Task.FromResult(ResultStatus.Successful)); }
private void EnsureClonedSceneAndHash() { if (!sceneCloned) { // Hash relevant scene objects if (asset.Scene != null) { string sceneUrl = AttachedReferenceManager.GetUrl(asset.Scene); var sceneAsset = (SceneAsset)AssetFinder.FindAsset(sceneUrl)?.Asset; // Clone scene asset because we update the world transformation matrices clonedSceneAsset = (SceneAsset)AssetCloner.Clone(sceneAsset); // Turn the entire entity hierarchy into a single list var sceneEntities = clonedSceneAsset.Hierarchy.Parts.Select(x => x.Value.Entity).ToList(); sceneHash = 0; foreach (var entity in sceneEntities) { // Collect bounding box entities NavigationBoundingBoxComponent boundingBoxComponent = entity.Get <NavigationBoundingBoxComponent>(); // Collect static collider entities StaticColliderComponent colliderComponent = entity.Get <StaticColliderComponent>(); if (boundingBoxComponent == null && colliderComponent == null) { continue; } // Update world transform entity.Transform.UpdateWorldMatrix(); if (boundingBoxComponent != null) { Vector3 scale; Quaternion rotation; Vector3 translation; boundingBoxComponent.Entity.Transform.WorldMatrix.Decompose(out scale, out rotation, out translation); var boundingBox = new BoundingBox(translation - boundingBoxComponent.Size * scale, translation + boundingBoxComponent.Size * scale); boundingBoxes.Add(boundingBox); // Hash collider for ComputeParameterHash sceneHash = (sceneHash * 397) ^ boundingBox.GetHashCode(); } if (colliderComponent != null) { staticColliderDatas.Add(new StaticColliderData { Component = colliderComponent }); if (colliderComponent.Enabled && !colliderComponent.IsTrigger && ((int)asset.IncludedCollisionGroups & (int)colliderComponent.CollisionGroup) != 0) { // Load collider shape assets since the scene asset is being used, which does not have these loaded by default foreach (var desc in colliderComponent.ColliderShapes) { var shapeAssetDesc = desc as ColliderShapeAssetDesc; if (shapeAssetDesc?.Shape != null) { var assetReference = AttachedReferenceManager.GetAttachedReference(shapeAssetDesc.Shape); PhysicsColliderShape loadedColliderShape; if (!loadedColliderShapes.TryGetValue(assetReference.Url, out loadedColliderShape)) { loadedColliderShape = contentManager.Load <PhysicsColliderShape>(assetReference.Url); loadedColliderShapes.Add(assetReference.Url, loadedColliderShape); // Store where we loaded the shapes from } shapeAssetDesc.Shape = loadedColliderShape; } } } // Hash collider for ComputeParameterHash sceneHash = (sceneHash * 397) ^ Xenko.Navigation.NavigationMeshBuildUtils.HashEntityCollider(colliderComponent, asset.IncludedCollisionGroups); } } } sceneCloned = true; } }
private bool MergeAsset(AssetItem item, AssetItem existingBase, List <AssetBase> existingBaseParts) { // No need to clone existingBaseParts as they are already cloned var baseCopy = (Asset)AssetCloner.Clone(item.Asset.Base?.Asset); var newBaseCopy = (Asset)AssetCloner.Clone(existingBase?.Asset); // Check base parts bool basePartsAreEqual = true; if (item.Asset != null && item.Asset.BaseParts != null) { foreach (var assetBasePart in item.Asset.BaseParts) { var existingBasePart = existingBaseParts.First(e => (e.Id == assetBasePart.Id || e.Location == assetBasePart.Location)); if (!CompareAssets(assetBasePart.Asset, existingBasePart.Asset)) { basePartsAreEqual = false; } } } // If the old base and new base are similar (including ids and overrides), we don't need to perform a merge if (CompareAssets(baseCopy, newBaseCopy) && basePartsAreEqual) { return(true); } // Delegates actual merge to the asset implem var result = item.Asset.Merge(baseCopy, newBaseCopy, existingBaseParts, item.Location); if (result.HasErrors) { result.CopyTo(log); return(false); } item.Asset = (Asset)result.Asset; if (item.Asset.Base != null) { item.Asset.Base = newBaseCopy != null ? new AssetBase(item.Asset.Base.Location, newBaseCopy) : null; } // Use new existing base parts if (existingBaseParts != null) { item.Asset.BaseParts = existingBaseParts; } // Set this variable to true at debug time to check what is the output of the merge bool writeToDebug = false; if (writeToDebug) { var writer = new MemoryStream(); YamlSerializer.Serialize(writer, item.Asset); writer.Flush(); writer.Position = 0; var text = Encoding.UTF8.GetString(writer.ToArray()); Debug.WriteLine(text); } item.IsDirty = true; return(true); }
/// <summary> /// This method is responsible to merge assets when loading. A 3-way merge of the asset is necessary because concurrent changes /// can happen on assets (people working with a SCM and different branches). This method will collect all assets that are /// using asset templating (including prefabs) and will perform the merge iteratively (incremental merge from the most inherited /// assets to the most derived asset) /// </summary> /// <param name="assetItem"></param> /// <param name="beingProcessed"></param> /// <returns></returns> private bool ProcessMergeAssetItem(AssetItem assetItem, HashSet <Guid> beingProcessed) { if (beingProcessed.Contains(assetItem.Id)) { log.Error(package, assetItem.Asset.Base, AssetMessageCode.AssetForPackageNotFound, assetItem.Asset.Base, package.FullPath.GetFileName()); return(false); } beingProcessed.Add(assetItem.Id); AssetItem existingAssetBase = null; List <AssetBase> existingBaseParts = null; // Process asset base if (assetItem.Asset.Base != null) { if (!ProcessMergeAssetBase(assetItem.Asset.Base, beingProcessed, out existingAssetBase)) { return(false); } } // Process asset base parts if (assetItem.Asset.BaseParts != null && assetItem.Asset.BaseParts.Count > 0) { existingBaseParts = new List <AssetBase>(); foreach (var basePart in assetItem.Asset.BaseParts) { AssetItem existingAssetBasePart; if (!ProcessMergeAssetBase(basePart, beingProcessed, out existingAssetBasePart)) { return(false); } // Replicate the group with the list of ids var newBasePart = new AssetBase(existingAssetBasePart.Location, (Asset)AssetCloner.Clone(existingAssetBasePart.Asset)); existingBaseParts.Add(newBasePart); } } // Don't process an asset that has been already processed if (!assetsProcessed.ContainsKey(assetItem.Id)) { // For simple merge (base, newAsset, newBase) => newObject // For multi-part prefabs merge (base, newAsset, newBase) + baseParts + newBaseParts => newObject if (!MergeAsset(assetItem, existingAssetBase, existingBaseParts)) { return(false); } assetsProcessed.Add(assetItem.Id, assetItem); assetsToProcess.Remove(assetItem.Id); } return(true); }
/// <summary> /// Notifies this object that the asset has been modified. /// </summary> /// <remarks>This method will trigger the re-evaluation of properties containing the path to a source file.</remarks> public void NotifyAssetChanged() { clonedAsset = AssetCloner.Clone(sessionAsset); UpdateAssetImportPathsTracked(true); }
public void TestCascadedInheritance() { // Test with: // a1: an asset with 2 entities // a2: an asset using a1 by composition with 2 instances // a3: an asset based on a2 // // Add one entity to a1. Check that a2 and a3 will get correctly the entities replicated // Before Merge // a1: a2: (baseParts: a1, 2 instances) a3: (base: a2) // | ea | ea1 (base: ea) | ea1' (base: ea1) // | eb | eb1 (base: eb) | eb1' (base: eb1) // | ea2 (base: ea) | ea2' (base: ea2) // | eb2 (base: eb) | eb2' (base: eb2) // After Merge // We add one entity to the base a1 // a1: a2: (baseParts: a1, 2 instances) a3: (base: a2) // | ea | ea1 (base: ea) | ea1' (base: ea1) // | eb | eb1 (base: eb) | eb1' (base: eb1) // | ec | ec1 (base: ec) | ec1' (base: ec1) // | ea2 (base: ea) | ea2' (base: ea2) // | eb2 (base: eb) | eb2' (base: eb2) // | ec2 (base: ec) | ec2' (base: ec2) var a1 = new EntityGroupAsset(); var ea = new Entity("ea"); var eb = new Entity("eb"); a1.Hierarchy.Entities.Add(ea); a1.Hierarchy.Entities.Add(eb); a1.Hierarchy.RootEntities.Add(ea.Id); a1.Hierarchy.RootEntities.Add(eb.Id); var a2 = new EntityGroupAsset(); var aPartInstance1 = (EntityGroupAsset)a1.CreateChildAsset("a1"); var aPartInstance2 = (EntityGroupAsset)a1.CreateChildAsset("a1"); a2.AddPart(aPartInstance1); a2.AddPart(aPartInstance2); // Modify a1 to add entity ec var ec = new Entity("ec"); a1.Hierarchy.Entities.Add(ec); a1.Hierarchy.RootEntities.Add(ec.Id); var a3 = (EntityGroupAsset)a2.CreateChildAsset("a2"); // Merge a2 var result2 = a2.Merge(null, null, new List <AssetBase>() { new AssetBase("a1", (Asset)AssetCloner.Clone(a1)) }); Assert.False(result2.HasErrors); Assert.AreEqual(6, a2.Hierarchy.RootEntities.Count); Assert.True(a2.Hierarchy.Entities.All(it => it.Design.BaseId.HasValue && it.Design.BasePartInstanceId.HasValue)); // Merge a3 var result3 = a3.Merge((Asset)AssetCloner.Clone(a3.Base.Asset), (Asset)AssetCloner.Clone(a2), null); Assert.False(result3.HasErrors); Assert.AreEqual(6, a3.Hierarchy.RootEntities.Count); Assert.True(a3.Hierarchy.Entities.All(it => !it.Design.BasePartInstanceId.HasValue)); Assert.True(a3.Hierarchy.Entities.All(it => it.Design.BaseId.HasValue && a2.Hierarchy.Entities.ContainsKey(it.Design.BaseId.Value))); }
public void TestMergeSimpleWithBasePartsAndLinks() { // part1: part2: newAsset (BaseParts: part1): newAssetMerged (BaseParts: part2): // EA EA EA1 (base: EA) [0] EA1' (base: EA) // EB EB EB1 (base: EB) [1] EB1' (base: EB) // EC + link: EA EC + link: EA EC1 (base: EC) + link: EA1 [2] EC1' (base: EC) + link: EA1' // ED + link: EB EA2 (base: EA) [3] EA2' (base: EA) // EB2 (base: EB) [4] EB2' (base: EB) // EC2 (base: EC) + link: EA2 [5] EC2' (base: EC) + link: EA2' // [6] ED1' (base: ED) + link: EB1' // [7] ED2' (base: ED) + link: EB2' var entityA = new Entity() { Name = "A" }; var entityB = new Entity() { Name = "B" }; var entityC = new Entity() { Name = "C" }; // EC + link: EA entityC.Add(new TestEntityComponent() { EntityLink = entityA }); // part1 Asset var basePart = new EntityGroupAsset(); basePart.Hierarchy.Entities.Add(new EntityDesign(entityA, new EntityDesignData())); basePart.Hierarchy.Entities.Add(new EntityDesign(entityB, new EntityDesignData())); basePart.Hierarchy.Entities.Add(new EntityDesign(entityC, new EntityDesignData())); basePart.Hierarchy.RootEntities.Add(entityA.Id); basePart.Hierarchy.RootEntities.Add(entityB.Id); basePart.Hierarchy.RootEntities.Add(entityC.Id); // Create part1 asset var part1 = (EntityGroupAsset)basePart.CreateChildAsset("part"); var entityB1 = part1.Hierarchy.Entities.First(it => it.Entity.Name == "B").Entity; var part12 = (EntityGroupAsset)basePart.CreateChildAsset("part"); var entityB2 = part12.Hierarchy.Entities.First(it => it.Entity.Name == "B").Entity; // create part2 assset var entityD = new Entity() { Name = "D" }; basePart.Hierarchy.Entities.Add(new EntityDesign(entityD, new EntityDesignData())); basePart.Hierarchy.RootEntities.Add(entityD.Id); // ED + link: EB var entityBFrom2 = basePart.Hierarchy.Entities.Where(it => it.Entity.Name == "B").Select(it => it.Entity).First(); entityD.Add(new TestEntityComponent() { EntityLink = entityBFrom2 }); // originalAsset: Add a new instanceId for this part var asset = new EntityGroupAsset(); asset.AddPart(part1); asset.AddPart(part12); // Merge entities (NOTE: it is important to clone baseAsset/newBaseAsset) var entityMerge = asset.Merge(null, null, new List <AssetBase>() { new AssetBase("part", (Asset)AssetCloner.Clone(basePart)) }); Assert.False(entityMerge.HasErrors); // EntityD must be now part of the new asset Assert.AreEqual(8, asset.Hierarchy.RootEntities.Count); Assert.AreEqual(8, asset.Hierarchy.Entities.Count); Assert.AreEqual(2, asset.Hierarchy.Entities.Count(it => it.Entity.Name == "D")); foreach (var entity in asset.Hierarchy.Entities.Where(it => it.Entity.Name == "D")) { // Check that we have the correct baesId and basePartInstanceId Assert.True(entity.Design.BasePartInstanceId.HasValue); Assert.True(entity.Design.BaseId.HasValue); Assert.AreEqual(entityD.Id, entity.Design.BaseId.Value); // Make sure that the entity is in the RootEntities Assert.True(asset.Hierarchy.RootEntities.Contains(entity.Entity.Id)); } var entityDesignD1 = asset.Hierarchy.Entities.FirstOrDefault(it => it.Entity.Name == "D" && it.Design.BasePartInstanceId == part1.Id); Assert.NotNull(entityDesignD1); var entityDesignD2 = asset.Hierarchy.Entities.FirstOrDefault(it => it.Entity.Name == "D" && it.Design.BasePartInstanceId == part12.Id); Assert.NotNull(entityDesignD2); // Check components var testComponentD1 = entityDesignD1.Entity.Get <TestEntityComponent>(); Assert.NotNull(testComponentD1); Assert.AreEqual(entityB1, testComponentD1.EntityLink); var testComponentD2 = entityDesignD2.Entity.Get <TestEntityComponent>(); Assert.NotNull(testComponentD2); Assert.AreEqual(entityB2, testComponentD2.EntityLink); }
/// <summary> /// Merges the specified assets from <c>base</c> and <c>from2</c> into <c>from1</c>. /// </summary> /// <param name="assetBase">The asset base.</param> /// <param name="assetFrom1">The asset from1.</param> /// <param name="assetFrom2">The asset from2.</param> /// <param name="mergePolicy">The merge policy. See <see cref="AssetMergePolicies" /> for default policies.</param> /// <returns>The result of the merge.</returns> /// <exception cref="System.ArgumentNullException"> /// assetFrom1 /// or /// mergePolicy /// </exception> public static MergeResult Merge(Asset assetBase, Asset assetFrom1, Asset assetFrom2, MergePolicyDelegate mergePolicy) { if (assetFrom1 == null) { throw new ArgumentNullException("assetFrom1"); } if (mergePolicy == null) { throw new ArgumentNullException("mergePolicy"); } return(Merge(new AssetDiff((Asset)AssetCloner.Clone(assetBase), (Asset)AssetCloner.Clone(assetFrom1), (Asset)AssetCloner.Clone(assetFrom2)), mergePolicy)); }
/// <summary> /// 3-way merge assets using an external diff tool. /// </summary> /// <param name="assetBase0">The asset base0.</param> /// <param name="assetFrom1">The asset from1.</param> /// <param name="assetFrom2">The asset from2.</param> /// <returns>The result of the merge.</returns> public static MergeResult MergeWithExternalTool(Asset assetBase0, Asset assetFrom1, Asset assetFrom2) { var result = new MergeResult(); // If they are all null, nothing to do if (assetBase0 == null && assetFrom1 == null && assetFrom2 == null) { return(result); } if (!File.Exists(DefaultMergeTool)) { result.Error("Unable to use external diff3 merge tool [{0}]. File not found", DefaultMergeTool); return(result); } var assetBase = (Asset)AssetCloner.Clone(assetBase0); var asset1 = (Asset)AssetCloner.Clone(assetFrom1); var asset2 = (Asset)AssetCloner.Clone(assetFrom2); // Clears base as we are not expecting to work with them directly // The real base must be passed by the assetBase0 parameter if (assetBase != null) { assetBase.Base = null; } if (asset1 != null) { asset1.Base = null; } if (asset2 != null) { asset2.Base = null; } var assetBasePath = Path.GetTempFileName(); var asset1Path = Path.GetTempFileName(); var asset2Path = Path.GetTempFileName(); try { AssetSerializer.Save(assetBasePath, assetBase); AssetSerializer.Save(asset1Path, asset1); AssetSerializer.Save(asset2Path, asset2); } catch (Exception exception) { result.Error("Unexpected error while serializing assets on disk before using diff tool", exception); return(result); } var outputPath = Path.GetTempFileName(); try { // TODO We need to access different diff tools command line // kdiff3.exe file1 file2 file3 -o outputfile var process = Process.Start(DefaultMergeTool, string.Format("{0} {1} {2} -o {3}", assetBasePath, asset1Path, asset2Path, outputPath)); if (process == null) { result.Error("Unable to launch diff3 tool exe from [{0}]", DefaultMergeTool); } else { process.WaitForExit(); if (process.ExitCode != 0) { result.Error("Error, failed to merge files"); } } } catch (Exception ex) { result.Error("Unable to launch diff3 tool exe from [{0}]", ex, DefaultMergeTool); } if (!result.HasErrors) { try { var mergedAsset = (Asset)AssetSerializer.Load(outputPath); if (mergedAsset != null) { if (assetFrom1 == null) { mergedAsset.Base = assetFrom2 == null ? assetBase0.Base : assetFrom2.Base; } else { mergedAsset.Base = assetFrom1.Base; } } result.Asset = mergedAsset; } catch (Exception ex) { result.Error("Unexpected exception while loading merged assets from [{0}]", ex, outputPath); } } return(result); }
protected override void Compile(AssetCompilerContext context, string urlInStorage, UFile assetAbsolutePath, SpriteFontAsset asset, AssetCompilerResult result) { var colorSpace = context.GetColorSpace(); if (asset.FontType == SpriteFontType.SDF) { // TODO Build scalable (SDF) font texture // copy the asset and transform the source and character set file path to absolute paths var assetClone = (SpriteFontAsset)AssetCloner.Clone(asset); var assetDirectory = assetAbsolutePath.GetParent(); assetClone.Source = !string.IsNullOrEmpty(asset.Source) ? UPath.Combine(assetDirectory, asset.Source) : null; assetClone.CharacterSet = !string.IsNullOrEmpty(asset.CharacterSet) ? UPath.Combine(assetDirectory, asset.CharacterSet) : null; result.BuildSteps = new AssetBuildStep(AssetItem) { new SignedDistanceFieldFontCommand(urlInStorage, assetClone) }; } else if (asset.FontType == SpriteFontType.Dynamic) { UFile fontPathOnDisk; if (!string.IsNullOrEmpty(asset.Source)) { var assetDirectory = assetAbsolutePath.GetParent(); fontPathOnDisk = UPath.Combine(assetDirectory, asset.Source); if (!File.Exists(fontPathOnDisk)) { result.Error("The font source '{0}' does not exist on the PC.", asset.FontName); return; } // set the source filename as font name instead of the font family. asset.FontName = fontPathOnDisk.GetFileName(); } else { fontPathOnDisk = GetFontPath(asset, result); if (fontPathOnDisk == null) { result.Error("The font named '{0}' could not be located on the PC.", asset.FontName); return; } } var fontImportLocation = FontHelper.GetFontPath(asset.FontName, asset.Style); result.BuildSteps = new AssetBuildStep(AssetItem) { new ImportStreamCommand { SourcePath = fontPathOnDisk, Location = fontImportLocation }, new DynamicFontCommand(urlInStorage, asset) }; } else { // copy the asset and transform the source and character set file path to absolute paths var assetClone = (SpriteFontAsset)AssetCloner.Clone(asset); var assetDirectory = assetAbsolutePath.GetParent(); assetClone.Source = !string.IsNullOrEmpty(asset.Source) ? UPath.Combine(assetDirectory, asset.Source): null; assetClone.CharacterSet = !string.IsNullOrEmpty(asset.CharacterSet) ? UPath.Combine(assetDirectory, asset.CharacterSet): null; result.BuildSteps = new AssetBuildStep(AssetItem) { new StaticFontCommand(urlInStorage, assetClone, colorSpace) }; } }
public void TestMergeEntityWithChildren() { // Test merging an EntityGroupAsset with a root entity EA, and 3 child entities // - Add a child entity to NewBase // - Remove a child entity from NewAsset // // Base NewBase NewAsset NewAsset (Merged) // // EA EA EA'(base: EA) EA'(base: EA) // |-EA1 |-EA1 |-EA1'(base: EA1) |-EA1'(base: EA1) // |-EA2 |-EA2 | | // |-EA3 |-EA3 |-EA3'(base: EA3) |-EA3'(base: EA3) // |-EA4 |-EA4'(base: EA4) // var eA = new Entity() { Name = "A" }; var eA1 = new Entity() { Name = "A1" }; var eA2 = new Entity() { Name = "A2" }; var eA3 = new Entity() { Name = "A3" }; eA.Transform.Children.Add(eA1.Transform); eA.Transform.Children.Add(eA2.Transform); eA.Transform.Children.Add(eA3.Transform); // Create Base Asset var baseAsset = new EntityGroupAsset(); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA1, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA2, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA3, new EntityDesignData())); baseAsset.Hierarchy.RootEntities.Add(eA.Id); var baseAssetItem = new AssetItem("base", baseAsset); // Create new Base Asset var newBaseAsset = (EntityGroupAsset)AssetCloner.Clone(baseAsset); var eA2FromNewBase = newBaseAsset.Hierarchy.Entities.First(item => item.Entity.Id == eA2.Id); newBaseAsset.Hierarchy.Entities[eA.Id].Entity.Transform.Children.Remove(eA2FromNewBase.Entity.Transform); // Create new Asset (from base) var newAsset = (EntityGroupAsset)baseAssetItem.CreateChildAsset(); var eA4 = new Entity() { Name = "A4" }; newAsset.Hierarchy.Entities.Add(new EntityDesign(eA4, new EntityDesignData())); newAsset.Hierarchy.Entities[newAsset.Hierarchy.RootEntities.First()].Entity.Transform.Children.Add(eA4.Transform); // Merge entities (NOTE: it is important to clone baseAsset/newBaseAsset) var result = newAsset.Merge((EntityGroupAssetBase)AssetCloner.Clone(baseAsset), (EntityGroupAssetBase)AssetCloner.Clone(newBaseAsset), null); Assert.False(result.HasErrors); Assert.AreEqual(1, newAsset.Hierarchy.RootEntities.Count); Assert.AreEqual(4, newAsset.Hierarchy.Entities.Count); // EA, EA1', EA3', EA4' var rootEntity = newAsset.Hierarchy.Entities[newAsset.Hierarchy.RootEntities.First()]; Assert.AreEqual(3, rootEntity.Entity.Transform.Children.Count); Assert.AreEqual("A1", rootEntity.Entity.Transform.Children[0].Entity.Name); Assert.AreEqual("A3", rootEntity.Entity.Transform.Children[1].Entity.Name); Assert.AreEqual("A4", rootEntity.Entity.Transform.Children[2].Entity.Name); }
/// <inheritdoc /> public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath) { if (targetRootObject == null) { throw new ArgumentNullException(nameof(targetRootObject)); } if (data == null) { throw new ArgumentNullException(nameof(data)); } var asset = (Asset)targetRootObject; var targetPropertyGraph = graphContainer.TryGetGraph(asset.Id); // We use a container object in case the data itself is an object reference var container = isRootDataObjectReference ? new FixupContainer { Data = data } : data; var rootNode = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container); var externalReferences = ExternalReferenceCollector.GetExternalReferences(targetPropertyGraph.Definition, rootNode); try { // Clone to create new ids for any IIdentifiable, except passed external references that will be maintained Dictionary <Guid, Guid> idRemapping; data = AssetCloner.Clone <object>(data, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, externalReferences, out idRemapping); } // TODO: have a proper exception type for serialization failure catch (Exception) { // Note: this can fail if the type doesn't have a binary serializer. return(false); } var targetTypeDescriptor = TypeDescriptorFactory.Default.Find(targetMemberType); bool result; switch (targetTypeDescriptor.Category) { case DescriptorCategory.Collection: result = ConvertForCollection((CollectionDescriptor)targetTypeDescriptor, ref data); break; case DescriptorCategory.Dictionary: result = ConvertForDictionary((DictionaryDescriptor)targetTypeDescriptor, ref data); break; case DescriptorCategory.Primitive: case DescriptorCategory.Object: case DescriptorCategory.NotSupportedObject: case DescriptorCategory.Nullable: result = ConvertForProperty(targetTypeDescriptor.Type, ref data); break; case DescriptorCategory.Array: case DescriptorCategory.Custom: throw new NotSupportedException(); default: throw new ArgumentOutOfRangeException(); } if (!result) { return(false); } // Collect all referenceable objects from the target asset (where we're pasting) var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode); // We use a container object in case the data itself is an object reference container = isRootDataObjectReference ? new FixupContainer { Data = data } : data; rootNode = targetPropertyGraph.Container.NodeContainer.GetOrCreateNode(container); // Generate YAML paths for the external reference so we can go through the normal deserialization fixup method. var externalReferenceIds = new HashSet <Guid>(externalReferences.Select(x => x.Id)); var visitor = new ObjectReferencePathGenerator(targetPropertyGraph.Definition) { ShouldOutputReference = x => externalReferenceIds.Contains(x) }; visitor.Visit(rootNode); // Fixup external references FixupObjectReferences.FixupReferences(container, visitor.Result, referenceableObjects, true); data = (container as FixupContainer)?.Data ?? data; return(true); }
public void TestMergeAddEntityWithLinks2() { // Test merging an EntityGroupAsset with a root entity EA, and 3 child entities // - Add a child entity to NewBase that has a link to an another entity + a link to the component of another entity // // Base NewBase NewAsset NewAsset (Merged) // // EA EA EA'(base: EA) EA'(base: EA) // |-EA1 |-EA1 |-EA1'(base: EA1) |-EA1'(base: EA1) // |-EA2 |-EA2 + link EA4 |-EA2'(base: EA2) |-EA2'(base: EA2) + link EA4' // |-EA3 |-EA3 |-EA3'(base: EA3) |-EA3'(base: EA3) // |-EA4 |-EA4'(base: EA4) // var eA = new Entity() { Name = "A" }; var eA1 = new Entity() { Name = "A1" }; var eA2 = new Entity() { Name = "A2" }; var eA3 = new Entity() { Name = "A3" }; eA.Transform.Children.Add(eA1.Transform); eA.Transform.Children.Add(eA2.Transform); eA.Transform.Children.Add(eA3.Transform); // Create Base Asset var baseAsset = new EntityGroupAsset(); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA1, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA2, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(eA3, new EntityDesignData())); baseAsset.Hierarchy.RootEntities.Add(eA.Id); var baseAssetItem = new AssetItem("base", baseAsset); // Create new Base Asset var newBaseAsset = (EntityGroupAsset)AssetCloner.Clone(baseAsset); var eA4 = new Entity() { Name = "A4" }; var rootInNewBase = newBaseAsset.Hierarchy.Entities[newBaseAsset.Hierarchy.RootEntities.First()]; var eA2InNewBaseTransform = rootInNewBase.Entity.Transform.Children.FirstOrDefault(item => item.Entity.Id == eA2.Id); Assert.NotNull(eA2InNewBaseTransform); // Add EA4 with link to EA1 entity and EA2 component var testComponent = new TestEntityComponent { EntityLink = eA4, }; eA2InNewBaseTransform.Entity.Add(testComponent); newBaseAsset.Hierarchy.Entities.Add(new EntityDesign(eA4, new EntityDesignData())); rootInNewBase.Entity.Transform.Children.Add(eA4.Transform); // Create new Asset (from base) var newAsset = (EntityGroupAsset)baseAssetItem.CreateChildAsset(); // Merge entities (NOTE: it is important to clone baseAsset/newBaseAsset) var result = newAsset.Merge((EntityGroupAssetBase)AssetCloner.Clone(baseAsset), (EntityGroupAssetBase)AssetCloner.Clone(newBaseAsset), null); Assert.False(result.HasErrors); Assert.AreEqual(1, newAsset.Hierarchy.RootEntities.Count); Assert.AreEqual(5, newAsset.Hierarchy.Entities.Count); // EA, EA1', EA2', EA3', EA4' var rootEntity = newAsset.Hierarchy.Entities[newAsset.Hierarchy.RootEntities.First()]; Assert.AreEqual(4, rootEntity.Entity.Transform.Children.Count); var eA1Merged = rootEntity.Entity.Transform.Children[0].Entity; var eA2Merged = rootEntity.Entity.Transform.Children[1].Entity; var eA4Merged = rootEntity.Entity.Transform.Children[3].Entity; Assert.AreEqual("A1", eA1Merged.Name); Assert.AreEqual("A2", eA2Merged.Name); Assert.AreEqual("A3", rootEntity.Entity.Transform.Children[2].Entity.Name); Assert.AreEqual("A4", eA4Merged.Name); var testComponentMerged = eA2Merged.Get <TestEntityComponent>(); Assert.NotNull(testComponentMerged); Assert.AreEqual(eA4Merged, testComponentMerged.EntityLink); }
/// <summary> /// Clones a sub-hierarchy of a composite hierarchical asset. /// </summary> /// <param name="sourceNodeContainer">The container in which are the nodes of the hierarchy to clone, used to extract metadata (overrides, etc.) if needed.</param> /// <param name="targetNodeContainer">The container in which the nodes of the cloned hierarchy should be created, used to re-apply metadata (overrides, etc.) if needed.</param> /// <param name="asset">The asset from which to clone sub-hierarchies.</param> /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies to clone.</param> /// <param name="flags">The flags customizing the cloning operation.</param> /// <param name="idRemapping">A dictionary containing the remapping of <see cref="IIdentifiable.Id"/> if <see cref="AssetClonerFlags.GenerateNewIdsForIdentifiableObjects"/> has been passed to the cloner.</param> /// <returns>A <see cref="AssetCompositeHierarchyData{TAssetPartDesign, TAssetPart}"/> corresponding to the cloned parts.</returns> /// <remarks>The parts passed to this methods must be independent in the hierarchy.</remarks> public static AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies([NotNull] AssetNodeContainer sourceNodeContainer, [NotNull] AssetNodeContainer targetNodeContainer, [NotNull] AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> asset, [NotNull] IEnumerable <Guid> sourceRootIds, SubHierarchyCloneFlags flags, [NotNull] out Dictionary <Guid, Guid> idRemapping) { if (sourceNodeContainer == null) { throw new ArgumentNullException(nameof(sourceNodeContainer)); } if (targetNodeContainer == null) { throw new ArgumentNullException(nameof(targetNodeContainer)); } if (asset == null) { throw new ArgumentNullException(nameof(asset)); } if (sourceRootIds == null) { throw new ArgumentNullException(nameof(sourceRootIds)); } // Extract the actual sub hierarchies to clone from the asset into a new instance of AssetCompositeHierarchyData var subTreeHierarchy = new AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>(); foreach (var rootId in sourceRootIds) { if (!asset.Hierarchy.Parts.ContainsKey(rootId)) { throw new ArgumentException(@"The source root parts must be parts of this asset.", nameof(sourceRootIds)); } subTreeHierarchy.RootParts.Add(asset.Hierarchy.Parts[rootId].Part); subTreeHierarchy.Parts.Add(asset.Hierarchy.Parts[rootId]); foreach (var subTreePart in asset.EnumerateChildParts(asset.Hierarchy.Parts[rootId].Part, true)) { subTreeHierarchy.Parts.Add(asset.Hierarchy.Parts[subTreePart.Id]); } } var assetType = asset.GetType(); // Create a new empty asset of the same type, and assign the sub hierachies to clone to it var cloneAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(assetType); cloneAsset.Hierarchy = subTreeHierarchy; var assetDefinition = AssetQuantumRegistry.GetDefinition(assetType); // We get the node corresponding to the new asset in the source NodeContainer, to be able to generate metadata (overrides, object references) needed for cloning. var rootNode = sourceNodeContainer.GetOrCreateNode(cloneAsset); var externalReferences = ExternalReferenceCollector.GetExternalReferences(assetDefinition, rootNode); var overrides = (flags & SubHierarchyCloneFlags.RemoveOverrides) == 0 ? GenerateOverridesForSerialization(rootNode) : null; // Now we ready to clone, let's just translate the flags and pass everything to the asset cloner. var clonerFlags = AssetClonerFlags.None; if ((flags & SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects) != 0) { clonerFlags |= AssetClonerFlags.GenerateNewIdsForIdentifiableObjects; } if ((flags & SubHierarchyCloneFlags.CleanExternalReferences) != 0) { clonerFlags |= AssetClonerFlags.ClearExternalReferences; } // We don't need to clone the asset itself, just the hierarchy. The asset itself is just useful so the property graph is in a normal context to do what we need. var clonedHierarchy = AssetCloner.Clone(subTreeHierarchy, clonerFlags, externalReferences, out idRemapping); if ((flags & SubHierarchyCloneFlags.RemoveOverrides) == 0) { // We need to propagate the override information to the nodes of the cloned objects into the target node container. // Let's reuse our temporary asset, and get its node in the target node container. rootNode = targetNodeContainer.GetOrCreateNode(cloneAsset); // Replace the initial hierarchy by the cloned one (through the Update method, in case the target container is the same as the source one). rootNode[nameof(AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> .Hierarchy)].Update(clonedHierarchy); // Remap the paths to overriden properties in case we generated new ids for identifiable objects. AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping); // Finally apply the overrides that come from the source parts. ApplyOverrides((IAssetNode)rootNode, overrides); } return(clonedHierarchy); }
public void TestMergeSimpleWithBasePartsAndLinksForChildren() { // Similar to TestMergeSimpleWithBasePartsAndLinks, but perform merge on Entity.Transform.Children instead // Also Check that an asset that was removed is not added and links are actually removed as well // part1: part2: newAsset (BaseParts: part1): newAssetMerged (BaseParts: part2): // ERoot ERoot ERoot1 ERoot1 // EA EA EA1 (base: EA) EA1' (base: EA) // EB EB EB1 (base: EB) EB1' (base: EB) // EC + link: EA EC + link: EA EC1 (base: EC) + link: EA1 EC1' (base: EC) + link: EA1' // ED + link: EB ED1' (base: ED) + link: EB1' // ERoot2 ERoot2 // EA2 (base: EA) EA2' (base: EA) // // EC2 (base: EC) + link: EA2 EC2' (base: EC) + link: EA2' // ED2' (base: ED) + noLink var eRoot = new Entity("Root"); var entityA = new Entity() { Name = "A" }; var entityB = new Entity() { Name = "B" }; var entityC = new Entity() { Name = "C" }; // EC + link: EA entityC.Add(new TestEntityComponent() { EntityLink = entityA }); eRoot.Transform.Children.Add(entityA.Transform); eRoot.Transform.Children.Add(entityB.Transform); eRoot.Transform.Children.Add(entityC.Transform); // part1 Asset var part1 = new EntityGroupAsset(); part1.Hierarchy.Entities.Add(new EntityDesign(eRoot, new EntityDesignData())); part1.Hierarchy.Entities.Add(new EntityDesign(entityA, new EntityDesignData())); part1.Hierarchy.Entities.Add(new EntityDesign(entityB, new EntityDesignData())); part1.Hierarchy.Entities.Add(new EntityDesign(entityC, new EntityDesignData())); part1.Hierarchy.RootEntities.Add(eRoot.Id); // part2 Asset var part2 = (EntityGroupAsset)AssetCloner.Clone(part1); var eRootPart2 = part2.Hierarchy.Entities.Where(it => it.Entity.Name == "Root").Select(it => it.Entity).First(); var entityD = new Entity() { Name = "D" }; eRootPart2.Transform.Children.Add(entityD.Transform); // ED + link: EB var entityBFrom2 = part2.Hierarchy.Entities.Where(it => it.Entity.Name == "B").Select(it => it.Entity).First(); entityD.Add(new TestEntityComponent() { EntityLink = entityBFrom2 }); part2.Hierarchy.Entities.Add(new EntityDesign(entityD, new EntityDesignData())); // originalAsset: Add a new instanceId for this part var asset = new EntityGroupAsset(); // Create derived parts var eRoot1Asset = (EntityGroupAsset)part1.CreateChildAsset("part"); var eRoot2Asset = (EntityGroupAsset)part1.CreateChildAsset("part"); asset.AddPart(eRoot1Asset); asset.AddPart(eRoot2Asset); var eRoot2 = asset.Hierarchy.Entities[eRoot2Asset.Hierarchy.RootEntities[0]]; var entityToRemove = eRoot2.Entity.Transform.Children.First(it => it.Entity.Name == "B"); eRoot2.Entity.Transform.Children.Remove(entityToRemove); asset.Hierarchy.Entities.Remove(entityToRemove.Entity.Id); // Merge entities (NOTE: it is important to clone baseAsset/newBaseAsset) var entityMerge = asset.Merge(null, null, new List <AssetBase>() { new AssetBase("part", part2) }); Assert.False(entityMerge.HasErrors); // EntityD must be now part of the new asset Assert.AreEqual(2, asset.Hierarchy.RootEntities.Count); Assert.AreEqual(9, asset.Hierarchy.Entities.Count); Assert.AreEqual(2, asset.Hierarchy.Entities.Count(it => it.Entity.Name == "D")); foreach (var entity in asset.Hierarchy.Entities.Where(it => it.Entity.Name == "D")) { // Check that we have the correct baesId and basePartInstanceId Assert.True(entity.Design.BasePartInstanceId.HasValue); Assert.True(entity.Design.BaseId.HasValue); Assert.AreEqual(entityD.Id, entity.Design.BaseId.Value); } var entityDesignD1 = asset.Hierarchy.Entities[asset.Hierarchy.Entities[asset.Hierarchy.RootEntities[0]].Entity.Transform.Children.Where(it => it.Entity.Name == "D").Select(it => it.Entity.Id).FirstOrDefault()]; Assert.NotNull(entityDesignD1); Assert.AreEqual(eRoot1Asset.Id, entityDesignD1.Design.BasePartInstanceId); var testComponentD1 = entityDesignD1.Entity.Get <TestEntityComponent>(); Assert.NotNull(testComponentD1); var entityB1 = asset.Hierarchy.Entities[asset.Hierarchy.RootEntities[0]].Entity.Transform.Children.Where(it => it.Entity.Name == "B").Select(it => it.Entity).First(); Assert.AreEqual(entityB1, testComponentD1.EntityLink); var entityDesignD2 = asset.Hierarchy.Entities[asset.Hierarchy.Entities[asset.Hierarchy.RootEntities[1]].Entity.Transform.Children.Where(it => it.Entity.Name == "D").Select(it => it.Entity.Id).FirstOrDefault()]; Assert.NotNull(entityDesignD2); Assert.AreEqual(eRoot2Asset.Id, entityDesignD2.Design.BasePartInstanceId); var testComponentD2 = entityDesignD2.Entity.Get <TestEntityComponent>(); Assert.NotNull(testComponentD2); Assert.AreEqual(null, testComponentD2.EntityLink); }
public void TestListDiffWithIdsOrderChanged() { var baseAsset = new TestDiffAsset() { Name = "Red", Value = 1 }; baseAsset.List.Add(new DiffComponent() { Name = "Test1", Position = new Vector4(1, 0, 0, 0) }); baseAsset.List.Add(new DiffComponent() { Name = "Test2", Position = new Vector4(1, 0, 0, 0) }); baseAsset.List.Add(new DiffComponent() { Name = "Test3", Position = new Vector4(1, 0, 0, 0) }); var newBaseAsset = (TestDiffAsset)AssetCloner.Clone(baseAsset); var baseItem = new AssetItem("/base", baseAsset); var childAsset = (TestDiffAsset)baseItem.CreateChildAsset(); // Swap elements in child asset var temp = childAsset.List[0]; childAsset.List[0] = childAsset.List[1]; childAsset.List[1] = temp; childAsset.List[0].Name = "Test21"; // Make New on Name value on first element var objDesc = TypeDescriptorFactory.Default.Find(typeof(DiffComponent)); var memberDesc = objDesc.Members.First(t => t.Name == "Name"); childAsset.List[0].SetOverride(memberDesc, OverrideType.New); // Perform the diff var diff = new AssetDiff(baseAsset, childAsset, newBaseAsset) { UseOverrideMode = true }; var diffResult = diff.Compute(); var diffResultStripped = diffResult.FindLeafDifferences().ToList(); // Expecting only one field to be merged from asset1 (the new on Name property) var mergeFromAsset1 = diffResultStripped.Where(item => item.ChangeType == Diff3ChangeType.MergeFromAsset1).ToList(); Assert.AreEqual(1, mergeFromAsset1.Count); var nameMember = mergeFromAsset1[0].Asset1Node as DataVisitMember; Assert.NotNull(nameMember); Assert.AreEqual("Name", nameMember.MemberDescriptor.Name); // Check that DiffComponent are swapped for Asset1 but diff is able to recover this foreach (var node in diffResultStripped.Where(item => item.BaseNode.Parent.Instance is DiffComponent)) { var base1 = (DiffComponent)node.BaseNode.Parent.Instance; var asset1 = (DiffComponent)node.Asset1Node.Parent.Instance; var asset2 = (DiffComponent)node.Asset2Node.Parent.Instance; var baseIndex = baseAsset.List.IndexOf(base1); var asset1Index = childAsset.List.IndexOf(asset1); var asset2Index = newBaseAsset.List.IndexOf(asset2); Assert.AreEqual(baseIndex, asset2Index); switch (baseIndex) { // element 0 and 1 are swapped case 0: Assert.AreEqual(1, asset1Index); break; case 1: Assert.AreEqual(0, asset1Index); break; default: Assert.AreEqual(baseIndex, asset1Index); break; } } }
public void TestMergeSimpleHierarchy() { // Test merging a simple Entity Asset that has 3 entities // // base: EA, EB, EC // newBase: EA, EB, EC, ED // newAsset: EA, EB, EC // // Result Merge: EA, EB, EC, ED var entityA = new Entity() { Name = "A" }; var entityB = new Entity() { Name = "B" }; var entityC = new Entity() { Name = "C" }; // Create Base Asset var baseAsset = new EntityGroupAsset(); baseAsset.Hierarchy.Entities.Add(new EntityDesign(entityA, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(entityB, new EntityDesignData())); baseAsset.Hierarchy.Entities.Add(new EntityDesign(entityC, new EntityDesignData())); baseAsset.Hierarchy.RootEntities.Add(entityA.Id); baseAsset.Hierarchy.RootEntities.Add(entityB.Id); baseAsset.Hierarchy.RootEntities.Add(entityC.Id); var baseAssetItem = new AssetItem("base", baseAsset); // Create new Base Asset var entityD = new Entity() { Name = "D" }; var newBaseAsset = (EntityGroupAsset)AssetCloner.Clone(baseAsset); newBaseAsset.Hierarchy.Entities.Add(new EntityDesign(entityD, new EntityDesignData())); newBaseAsset.Hierarchy.RootEntities.Add(entityD.Id); // Create new Asset (from base) var newAsset = (EntityGroupAsset)baseAssetItem.CreateChildAsset(); // Merge entities (NOTE: it is important to clone baseAsset/newBaseAsset) var result = newAsset.Merge((EntityGroupAssetBase)AssetCloner.Clone(baseAsset), (EntityGroupAssetBase)AssetCloner.Clone(newBaseAsset), null); Assert.False(result.HasErrors); // Both root and entities must be the same Assert.AreEqual(4, newAsset.Hierarchy.RootEntities.Count); Assert.AreEqual(4, newAsset.Hierarchy.Entities.Count); // All entities must have a base value Assert.True(newAsset.Hierarchy.Entities.All(item => item.Design.BaseId.HasValue)); var entityAInNewAsset = newAsset.Hierarchy.Entities.Where(item => item.Design.BaseId.Value == entityA.Id).Select(item => item.Entity).FirstOrDefault(); Assert.NotNull(entityAInNewAsset); var entityBInNewAsset = newAsset.Hierarchy.Entities.Where(item => item.Design.BaseId.Value == entityB.Id).Select(item => item.Entity).FirstOrDefault(); Assert.NotNull(entityBInNewAsset); var entityCInNewAsset = newAsset.Hierarchy.Entities.Where(item => item.Design.BaseId.Value == entityC.Id).Select(item => item.Entity).FirstOrDefault(); Assert.NotNull(entityCInNewAsset); var entityDInNewAsset = newAsset.Hierarchy.Entities.Where(item => item.Design.BaseId.Value == entityD.Id).Select(item => item.Entity).FirstOrDefault(); Assert.NotNull(entityDInNewAsset); // Hierarchy must be: EA, EB, EC, ED Assert.AreEqual(entityAInNewAsset.Id, newAsset.Hierarchy.RootEntities[0]); Assert.AreEqual(entityBInNewAsset.Id, newAsset.Hierarchy.RootEntities[1]); Assert.AreEqual(entityCInNewAsset.Id, newAsset.Hierarchy.RootEntities[2]); Assert.AreEqual(entityDInNewAsset.Id, newAsset.Hierarchy.RootEntities[3]); }
protected override void Compile(AssetCompilerContext context, AssetItem assetItem, string targetUrlInStorage, AssetCompilerResult result) { var asset = (SpriteFontAsset)assetItem.Asset; UFile assetAbsolutePath = assetItem.FullPath; var colorSpace = context.GetColorSpace(); if (asset.FontType is SignedDistanceFieldSpriteFontType) { var fontTypeSDF = asset.FontType as SignedDistanceFieldSpriteFontType; // copy the asset and transform the source and character set file path to absolute paths var assetClone = AssetCloner.Clone(asset); var assetDirectory = assetAbsolutePath.GetParent(); assetClone.FontSource = asset.FontSource; fontTypeSDF.CharacterSet = !string.IsNullOrEmpty(fontTypeSDF.CharacterSet) ? UPath.Combine(assetDirectory, fontTypeSDF.CharacterSet) : null; result.BuildSteps = new AssetBuildStep(assetItem) { new SignedDistanceFieldFontCommand(targetUrlInStorage, assetClone) }; } else if (asset.FontType is RuntimeRasterizedSpriteFontType) { UFile fontPathOnDisk = asset.FontSource.GetFontPath(result); if (fontPathOnDisk == null) { result.Error($"Runtime rasterized font compilation failed. Font {asset.FontSource.GetFontName()} was not found on this machine."); result.BuildSteps = new AssetBuildStep(assetItem) { new FailedFontCommand() }; return; } var fontImportLocation = FontHelper.GetFontPath(asset.FontSource.GetFontName(), asset.FontSource.Style); result.BuildSteps = new AssetBuildStep(assetItem) { new ImportStreamCommand { SourcePath = fontPathOnDisk, Location = fontImportLocation }, new RuntimeRasterizedFontCommand(targetUrlInStorage, asset) }; } else { var fontTypeStatic = asset.FontType as OfflineRasterizedSpriteFontType; if (fontTypeStatic == null) { throw new ArgumentException("Tried to compile a non-offline rasterized sprite font with the compiler for offline resterized fonts!"); } // copy the asset and transform the source and character set file path to absolute paths var assetClone = AssetCloner.Clone(asset); var assetDirectory = assetAbsolutePath.GetParent(); assetClone.FontSource = asset.FontSource; fontTypeStatic.CharacterSet = !string.IsNullOrEmpty(fontTypeStatic.CharacterSet) ? UPath.Combine(assetDirectory, fontTypeStatic.CharacterSet): null; result.BuildSteps = new AssetBuildStep(assetItem) { new OfflineRasterizedFontCommand(targetUrlInStorage, assetClone, colorSpace) }; } }