/// <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 collectionDescriptor = (CollectionDescriptor)TypeDescriptorFactory.Default.Find(targetRootObject.GetType()); var collection = data as IList <AssetItem>; if (collection == null) { collection = (IList <AssetItem>)Activator.CreateInstance(collectionDescriptor.Type, true); collectionDescriptor.Add(collection, data); } for (var i = 0; i < collection.Count; i++) { var assetItem = collection[i]; // If the asset already exists, clone it with new identifiers if (session.GetAssetById(assetItem.Id) != null) { // Create a derived asset and restore archetype to handle asset-specific cloning process. Dictionary <Guid, Guid> idRemapping; var clone = AssetCloner.Clone(assetItem.Asset, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, out idRemapping); var assetType = assetItem.Asset.GetType(); if (assetType.HasInterface(typeof(AssetCompositeHierarchy <,>))) { try { // TODO: Find a way to fallback to the asset or generalize for all asset composite dynamic assetComposite = clone; // Remap indices of parts in Hierarchy.Part var path = basePath.Clone(); path.PushItemId(CollectionItemIdHelper.GetCollectionItemIds(collection)[i]); AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping, path); AssetPartsAnalysis.GenerateNewBaseInstanceIds(assetComposite.Hierarchy); } catch (RuntimeBinderException e) { e.Ignore(); } } // FIXME: rework this var postProcessor = session.ServiceProvider.Get <ICopyPasteService>().PostProcessors.FirstOrDefault(p => p.Accept(assetType)); postProcessor?.PostPasteDeserialization(clone); collection[i] = new AssetItem(assetItem.Location, clone); } } // Get the fixed-up value data = collection; return(true); }
public EntityViewModel Duplicate() { var flags = SubHierarchyCloneFlags.GenerateNewIdsForIdentifiableObjects; var clonedHierarchy = EntityHierarchyPropertyGraph.CloneSubHierarchies(Asset.Session.AssetNodeContainer, Asset.Asset, AssetSideEntity.Id.Yield(), flags, out Dictionary <Guid, Guid> idRemapping); AssetPartsAnalysis.GenerateNewBaseInstanceIds(clonedHierarchy); var addedRoot = clonedHierarchy.Parts[clonedHierarchy.RootParts.Single().Id]; addedRoot.Folder = (Parent as EntityFolderViewModel)?.Path; // rename the entity to avoid having the same names if (Parent == null) { throw new InvalidOperationException($"{nameof(Parent)} cannot be null"); } addedRoot.Entity.Name = EntityFactory.ComputeNewName(Parent, addedRoot.Entity.Name); Asset.AssetHierarchyPropertyGraph.AddPartToAsset(clonedHierarchy.Parts, addedRoot, (Parent.Owner as EntityViewModel)?.AssetSideEntity, Parent.Owner.IndexOfEntity(this) + 1); var cloneId = addedRoot.Entity.Id; // The view model should already exist at that point var partId = new AbsoluteId(Asset.Id, cloneId); var viewModel = (EntityViewModel)Editor.FindPartViewModel(partId); // TODO: Offset a bit (by 1 scene unit horizontally?) the cloned entity so it appears distincly from the source entity return(viewModel); }
/// <summary> /// Clones a sub-hierarchy of this asset. /// </summary> /// <param name="sourceRootId">The id of the root of the sub-hierarchy to clone</param> /// <param name="cleanReference">If true, any reference to a part external to the cloned hierarchy will be set to null.</param> /// <param name="idRemapping">A dictionary containing the mapping of ids from the source parts to the new parts.</param> /// <returns>A <see cref="AssetCompositeHierarchyData{UIElementDesign, UIElement}"/> corresponding to the cloned parts.</returns> public override AssetCompositeHierarchyData <UIElementDesign, UIElement> CloneSubHierarchy(Guid sourceRootId, bool cleanReference, out Dictionary <Guid, Guid> idRemapping) { if (!Hierarchy.Parts.ContainsKey(sourceRootId)) { throw new ArgumentException(@"The source root part must be an part of this asset.", nameof(sourceRootId)); } // 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. var subTreeRoot = Hierarchy.Parts[sourceRootId]; var subTreeHierarchy = new AssetCompositeHierarchyData <UIElementDesign, UIElement> { Parts = { subTreeRoot }, RootPartIds = { sourceRootId } }; foreach (var subTreeDesign in EnumerateChildParts(subTreeRoot, Hierarchy, true)) { subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreeDesign.UIElement.Id]); } // clone the parts of the sub-tree var clonedHierarchy = (AssetCompositeHierarchyData <UIElementDesign, UIElement>)AssetCloner.Clone(subTreeHierarchy); //clonedHierarchy.Parts[sourceRootEntity].UIElement.Parent = null; //if (cleanReference) //{ // // set to null reference outside of the sub-tree // var tempAsset = new PrefabAsset { Hierarchy = clonedHierarchy }; // tempAsset.FixupPartReferences(); //} // temporary nullify the hierarchy to avoid to clone it var sourceHierarchy = Hierarchy; Hierarchy = null; // revert the source hierarchy Hierarchy = sourceHierarchy; // Generate part mapping idRemapping = new Dictionary <Guid, Guid>(); foreach (var partDesign in clonedHierarchy.Parts) { // Generate new Id var newPartId = Guid.NewGuid(); // Update mappings idRemapping.Add(partDesign.UIElement.Id, newPartId); // Update part with new id partDesign.UIElement.Id = newPartId; } // Rewrite part references // Should we nullify invalid references? AssetPartsAnalysis.RemapPartsId(clonedHierarchy, idRemapping); return(clonedHierarchy); }
/// <inheritdoc /> public override bool ProcessDeserializedData(AssetPropertyGraphContainer graphContainer, object targetRootObject, Type targetMemberType, ref object data, bool isRootDataObjectReference, AssetId?sourceId, YamlAssetMetadata <OverrideType> overrides, YamlAssetPath basePath) { var asset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)targetRootObject; var hierarchy = data as AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>; if (hierarchy == null) { return(false); } // Create a temporary asset to host the hierarchy to paste, so we have a property graph to manipulate it. var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(asset.GetType()); tempAsset.Hierarchy = hierarchy; // Use temporary containers so that any created nodes are discarded after the processing. var tempNodeContainer = new AssetNodeContainer { NodeBuilder = { NodeFactory = new AssetNodeFactory() } }; var definition = AssetQuantumRegistry.GetDefinition(asset.GetType()); var rootNode = tempNodeContainer.GetOrCreateNode(tempAsset); // If different asset or if at least one part already exists, create a custom clone. if (asset.Id != sourceId || hierarchy.Parts.Values.Any(part => asset.ContainsPart(part.Part.Id))) { // Clone again to create new ids for any IIdentifiable, but keep references to external object intact. var cloneExternalReferences = ExternalReferenceCollector.GetExternalReferences(definition, rootNode); hierarchy = AssetCloner.Clone(hierarchy, AssetClonerFlags.GenerateNewIdsForIdentifiableObjects, cloneExternalReferences, out var idRemapping); // Remap indices of parts in Hierarchy.Part AssetCloningHelper.RemapIdentifiablePaths(overrides, idRemapping, basePath); // Make new base instances ids in case the part are inherited. AssetPartsAnalysis.GenerateNewBaseInstanceIds(hierarchy); // Update the temporary asset with this cloned hierarchy. rootNode[nameof(AssetCompositeHierarchy <TAssetPartDesign, TAssetPart> .Hierarchy)].Update(hierarchy); } // Collect all referenceable objects from the target asset (where we're pasting) var targetPropertyGraph = graphContainer.TryGetGraph(asset.Id); var referenceableObjects = IdentifiableObjectCollector.Collect(targetPropertyGraph.Definition, targetPropertyGraph.RootNode); // Replace references in the hierarchy being pasted by the real objects from the target asset. var externalReferences = new HashSet <Guid>(ExternalReferenceCollector.GetExternalReferences(definition, rootNode).Select(x => x.Id)); var visitor = new ObjectReferencePathGenerator(definition) { ShouldOutputReference = x => externalReferences.Contains(x) }; visitor.Visit(rootNode); FixupObjectReferences.FixupReferences(tempAsset, visitor.Result, referenceableObjects, true); data = hierarchy; return(true); }
/// <summary> /// Clones a sub-hierarchy of this asset. /// </summary> /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies to clone.</param> /// <param name="cleanReference">If true, any reference to a part external to the cloned hierarchy will be set to null.</param> /// <param name="idRemapping">A dictionary containing the mapping of ids from the source parts to the new parts.</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 AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies(IEnumerable <Guid> sourceRootIds, bool cleanReference, out Dictionary <Guid, Guid> idRemapping) { // 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. var subTreeHierarchy = new AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>(); foreach (var rootId in sourceRootIds) { if (!Hierarchy.Parts.ContainsKey(rootId)) { throw new ArgumentException(@"The source root parts must be parts of this asset.", nameof(sourceRootIds)); } subTreeHierarchy.RootPartIds.Add(rootId); subTreeHierarchy.Parts.Add(Hierarchy.Parts[rootId]); foreach (var subTreePart in EnumerateChildParts(Hierarchy.Parts[rootId].Part, true)) { subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreePart.Id]); } } // clone the parts of the sub-tree var clonedHierarchy = AssetCloner.Clone(subTreeHierarchy); foreach (var rootEntity in clonedHierarchy.RootPartIds) { PostClonePart(clonedHierarchy.Parts[rootEntity].Part); } if (cleanReference) { ClearPartReferences(clonedHierarchy); } // Generate part mapping idRemapping = new Dictionary <Guid, Guid>(); foreach (var partDesign in clonedHierarchy.Parts) { // Generate new Id var newId = Guid.NewGuid(); // Update mappings idRemapping.Add(partDesign.Part.Id, newId); // Update part with new id partDesign.Part.Id = newId; } // Rewrite part references // Should we nullify invalid references? AssetPartsAnalysis.RemapPartsId(clonedHierarchy, idRemapping); return(clonedHierarchy); }
public override Asset CreateDerivedAsset(string baseLocation, out Dictionary <Guid, Guid> idRemapping) { var newAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)base.CreateDerivedAsset(baseLocation, out idRemapping); var instanceId = Guid.NewGuid(); foreach (var part in Hierarchy.Parts) { var newPart = newAsset.Hierarchy.Parts[idRemapping[part.Part.Id]]; newPart.Base = new BasePart(new AssetReference(Id, baseLocation), part.Part.Id, instanceId); } AssetPartsAnalysis.RemapPartsId(newAsset.Hierarchy, idRemapping); return(newAsset); }
public override Asset CreateDerivedAsset(string baseLocation, IDictionary <Guid, Guid> idRemapping = null) { var newAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)base.CreateDerivedAsset(baseLocation, idRemapping); var remappingDictionary = idRemapping ?? new Dictionary <Guid, Guid>(); var instanceId = Guid.NewGuid(); foreach (var part in newAsset.Hierarchy.Parts) { part.Base = new BasePart(new AssetReference(Id, baseLocation), part.Part.Id, instanceId); // Create and register a new id for this part var newId = Guid.NewGuid(); remappingDictionary.Add(part.Part.Id, newId); // Apply the new Guid part.Part.Id = newId; } AssetPartsAnalysis.RemapPartsId(newAsset.Hierarchy, remappingDictionary); return(newAsset); }
public override Asset CreateChildAsset(string baseLocation, IDictionary<Guid, Guid> idRemapping = null) { var newAsset = (AssetCompositeHierarchy<TAssetPartDesign, TAssetPart>)base.CreateChildAsset(baseLocation); var remappingDictionary = idRemapping ?? new Dictionary<Guid, Guid>(); foreach (var part in newAsset.Hierarchy.Parts) { // Store the baseid of the new version part.BaseId = part.Part.Id; // Make sure that we don't replicate the base part InstanceId part.BasePartInstanceId = null; // Create and register a new id for this part var newId = Guid.NewGuid(); remappingDictionary.Add(part.Part.Id, newId); // Apply the new Guid part.Part.Id = newId; } AssetPartsAnalysis.RemapPartsId(newAsset.Hierarchy, remappingDictionary); return newAsset; }
/// <summary> /// Clones a sub-hierarchy of this asset. /// </summary> /// <param name="sourceRootIds">The ids that are the roots of the sub-hierarchies to clone.</param> /// <param name="cleanReference">If true, any reference to a part external to the cloned hierarchy will be set to null.</param> /// <param name="generateNewIdsForIdentifiableObjects">If true, the cloned objects that implement <see cref="IIdentifiable"/> will have new ids.</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 AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart> CloneSubHierarchies(IEnumerable <Guid> sourceRootIds, bool cleanReference, bool generateNewIdsForIdentifiableObjects, bool generateNewBaseInstanceIds, out Dictionary <Guid, Guid> idRemapping) { // 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. var subTreeHierarchy = new AssetCompositeHierarchyData <TAssetPartDesign, TAssetPart>(); foreach (var rootId in sourceRootIds) { if (!Hierarchy.Parts.ContainsKey(rootId)) { throw new ArgumentException(@"The source root parts must be parts of this asset.", nameof(sourceRootIds)); } subTreeHierarchy.RootPartIds.Add(rootId); subTreeHierarchy.Parts.Add(Hierarchy.Parts[rootId]); foreach (var subTreePart in EnumerateChildParts(Hierarchy.Parts[rootId].Part, true)) { subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreePart.Id]); } } // clone the parts of the sub-tree var clonedHierarchy = AssetCloner.Clone(subTreeHierarchy, generateNewIdsForIdentifiableObjects ? AssetClonerFlags.GenerateNewIdsForIdentifiableObjects : AssetClonerFlags.None, out idRemapping); // Remap ids from the root id collection to the new ids generated during cloning AssetPartsAnalysis.RemapPartsId(clonedHierarchy, idRemapping); foreach (var rootEntity in clonedHierarchy.RootPartIds) { PostClonePart(clonedHierarchy.Parts[rootEntity].Part); } if (cleanReference) { // set to null reference outside of the sub-tree var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(GetType()); tempAsset.Hierarchy = clonedHierarchy; tempAsset.FixupPartReferences(); } else { // restore initial ids for reference outside of the subtree, so they can be fixed up later. var tempAsset = (AssetCompositeHierarchy <TAssetPartDesign, TAssetPart>)Activator.CreateInstance(GetType()); tempAsset.Hierarchy = clonedHierarchy; var visitor = new AssetCompositePartReferenceCollector(); visitor.VisitAsset(tempAsset); var references = visitor.Result; var revertedIdMapping = idRemapping.ToDictionary(x => x.Value, x => x.Key); foreach (var referencedPart in references.Select(x => x.AssetPart).OfType <IIdentifiable>()) { var realPart = tempAsset.ResolvePartReference(referencedPart); if (realPart == null) { referencedPart.Id = revertedIdMapping[referencedPart.Id]; } } } if (generateNewBaseInstanceIds) { AssetPartsAnalysis.GenerateNewBaseInstanceIds(clonedHierarchy); } return(clonedHierarchy); }
/// <summary> /// Clones a sub-hierarchy of this asset. /// </summary> /// <param name="sourceRootEntities">The entities that are the roots of the sub-hierarchies to clone</param> /// <param name="cleanReference">If true, any reference to an entity external to the cloned hierarchy will be set to null.</param> /// <param name="entityMapping">A dictionary containing the mapping of ids from the source entites to the new entities.</param> /// <returns>A <see cref="AssetCompositeHierarchyData{EntityDesign, Entity}"/> corresponding to the cloned entities.</returns> /// <remarks>The entities passed to this methods must be independent in the hierarchy.</remarks> public AssetCompositeHierarchyData <EntityDesign, Entity> CloneSubHierarchies(IEnumerable <Guid> sourceRootEntities, bool cleanReference, out Dictionary <Guid, Guid> entityMapping) { // 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. var subTreeHierarchy = new AssetCompositeHierarchyData <EntityDesign, Entity>(); foreach (var sourceRootEntity in sourceRootEntities) { if (!Hierarchy.Parts.ContainsKey(sourceRootEntity)) { throw new ArgumentException(@"The source root entities must be entities of this asset.", nameof(sourceRootEntities)); } var subTreeRoot = Hierarchy.Parts[sourceRootEntity].Entity; subTreeHierarchy.Parts.Add(new EntityDesign(subTreeRoot)); subTreeHierarchy.RootPartIds.Add(sourceRootEntity); foreach (var subTreeEntity in EnumerateChildParts(subTreeRoot, true)) { subTreeHierarchy.Parts.Add(Hierarchy.Parts[subTreeEntity.Id]); } } // clone the entities of the sub-tree var clonedHierarchy = (AssetCompositeHierarchyData <EntityDesign, Entity>)AssetCloner.Clone(subTreeHierarchy); foreach (var rootEntity in clonedHierarchy.RootPartIds) { clonedHierarchy.Parts[rootEntity].Entity.Transform.Parent = null; } if (cleanReference) { // set to null reference outside of the sub-tree var tempAsset = new PrefabAsset { Hierarchy = clonedHierarchy }; tempAsset.FixupPartReferences(); } // temporary nullify the hierarchy to avoid to clone it var sourceHierarchy = Hierarchy; Hierarchy = null; // revert the source hierarchy Hierarchy = sourceHierarchy; // Generate entity mapping entityMapping = new Dictionary <Guid, Guid>(); foreach (var entityDesign in clonedHierarchy.Parts) { // Generate new Id var newEntityId = Guid.NewGuid(); // Update mappings entityMapping.Add(entityDesign.Entity.Id, newEntityId); // Update entity with new id entityDesign.Entity.Id = newEntityId; } // Rewrite entity references // Should we nullify invalid references? AssetPartsAnalysis.RemapPartsId(clonedHierarchy, entityMapping); return(clonedHierarchy); }