Beispiel #1
0
            public void ProcessSkybox(PackageLoadingAssetFile skyboxAsset)
            {
                using (var skyboxYaml = skyboxAsset.AsYamlAsset())
                {
                    var root        = skyboxYaml.DynamicRootNode;
                    var rootMapping = (DynamicYamlMapping)root;

                    string cubemapReference = "null";

                    // Insert cubmap into skybox root instead of in Model
                    if (root.Model != null)
                    {
                        if (root.Model.Node.Tag == "!SkyboxCubeMapModel")
                        {
                            cubemapReference = root.Model.CubeMap;
                        }
                        rootMapping.RemoveChild("Model");
                    }
                    rootMapping.AddChild("CubeMap", cubemapReference);
                    var splitReference = cubemapReference.Split('/'); // TODO

                    // We will remove skyboxes that are only used as a background
                    if (root.Usage != null && (string)root.Usage == "Background")
                    {
                        skyboxAsset.Deleted = true;
                    }

                    bool isBackground = root.Usage == null ||
                                        (string)root.Usage == "Background" ||
                                        (string)root.Usage == "LightingAndBackground";
                    skyboxAssetInfos.Add((string)root.Id, new SkyboxAssetInfo
                    {
                        TextureReference = splitReference.Last(),
                        IsBackground     = isBackground,
                        Deleted          = skyboxAsset.Deleted,
                    });
                }
            }
        public override bool Upgrade(PackageSession session, ILogger log, Package dependentPackage, PackageDependency dependency, Package dependencyPackage, IList <PackageLoadingAssetFile> assetFiles)
        {
            if (dependency.Version.MinVersion < new PackageVersion("1.5.0-alpha01"))
            {
                RunAssetUpgradersUntilVersion(log, dependentPackage, XenkoConfig.PackageName, assetFiles, PackageVersion.Parse("1.5.0-alpha01"));
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.5.0-alpha02"))
            {
                // Ideally, this should be part of asset upgrader but we can't upgrade multiple assets at once yet

                var modelAssets = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xkm3d").Select(x => x.AsYamlAsset()).ToArray();
                var animAssets  = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xkanim").Select(x => x.AsYamlAsset()).ToArray();
                var sceneAssets = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xkscene").Select(x => x.AsYamlAsset()).ToArray();

                // Select models with at least two nodes
                var modelAssetsWithSekeleton = modelAssets
                                               .Where(model => ((IEnumerable)model.DynamicRootNode.Nodes).Cast <object>().Count() > 1).ToArray();

                var animToModelMapping = new Dictionary <PackageLoadingAssetFile.YamlAsset, PackageLoadingAssetFile.YamlAsset>();

                // Find associations in scene
                foreach (var sceneAsset in sceneAssets)
                {
                    var hierarchy = sceneAsset.DynamicRootNode.Hierarchy;
                    foreach (dynamic entity in hierarchy.Entities)
                    {
                        var components         = entity.Entity.Components;
                        var animationComponent = components["AnimationComponent.Key"];
                        var model = components["ModelComponent.Key"]?.Model;
                        if (animationComponent != null && model != null)
                        {
                            var modelReference = DynamicYamlExtensions.ConvertTo <AssetReference>(model);
                            var modelAsset     = modelAssetsWithSekeleton.FirstOrDefault(x => x.Asset.AssetPath == modelReference.Location);

                            foreach (var animation in animationComponent.Animations)
                            {
                                var animationReference = DynamicYamlExtensions.ConvertTo <AssetReference>(animation.Value);
                                var animationAsset     = animAssets.FirstOrDefault(x => x.Asset.AssetPath == animationReference.Location);

                                if (modelAsset != null && animationAsset != null)
                                {
                                    animToModelMapping[animationAsset] = modelAsset;
                                }
                            }
                        }
                    }
                }

                // Find associations when sharing same source file
                foreach (var animationAsset in animAssets)
                {
                    // Comparing absolute path of assets
                    var modelAsset = modelAssetsWithSekeleton.FirstOrDefault(
                        x => UPath.Combine(animationAsset.Asset.AssetPath.GetParent(), new UFile((string)animationAsset.DynamicRootNode.Source))
                        == UPath.Combine(x.Asset.AssetPath.GetParent(), new UFile((string)x.DynamicRootNode.Source)));
                    if (modelAsset != null)
                    {
                        animToModelMapping[animationAsset] = modelAsset;
                    }
                }

                var modelToSkeletonMapping = new Dictionary <PackageLoadingAssetFile.YamlAsset, PackageLoadingAssetFile.YamlAsset>();

                // For each model asset, create skeleton assets
                foreach (var modelAsset in modelAssetsWithSekeleton)
                {
                    var skeletonAsset = new PackageLoadingAssetFile(modelAsset.Asset.FilePath.GetFullPathWithoutExtension() + " Skeleton.xkskel", modelAsset.Asset.SourceFolder)
                    {
                        AssetContent = System.Text.Encoding.UTF8.GetBytes("!Skeleton\r\nId: " + Guid.NewGuid())
                    };

                    using (var skeletonAssetYaml = skeletonAsset.AsYamlAsset())
                    {
                        // Set source
                        skeletonAssetYaml.DynamicRootNode.Source     = modelAsset.DynamicRootNode.Source;
                        skeletonAssetYaml.DynamicRootNode.SourceHash = modelAsset.DynamicRootNode.SourceHash;

                        // To be on the safe side, mark everything as preserved
                        var nodes = modelAsset.DynamicRootNode.Nodes;
                        foreach (var node in nodes)
                        {
                            node.Preserve = true;
                        }

                        skeletonAssetYaml.DynamicRootNode.Nodes       = nodes;
                        skeletonAssetYaml.DynamicRootNode.ScaleImport = modelAsset.DynamicRootNode.ScaleImport;

                        // Update model to point to this skeleton
                        modelAsset.DynamicRootNode.Skeleton = new AssetReference(AssetId.Parse((string)skeletonAssetYaml.DynamicRootNode.Id), skeletonAsset.AssetPath.MakeRelative(modelAsset.Asset.AssetPath.GetParent()));
                        modelToSkeletonMapping.Add(modelAsset, skeletonAssetYaml);
                    }

                    assetFiles.Add(skeletonAsset);
                }

                // Update animation to point to skeleton, and set preview default model
                foreach (var animToModelEntry in animToModelMapping)
                {
                    var animationAsset = animToModelEntry.Key;
                    var modelAsset     = animToModelEntry.Value;

                    var skeletonAsset = modelToSkeletonMapping[modelAsset];
                    animationAsset.DynamicRootNode.Skeleton     = new AssetReference(AssetId.Parse((string)skeletonAsset.DynamicRootNode.Id), skeletonAsset.Asset.AssetPath.MakeRelative(animationAsset.Asset.AssetPath.GetParent()));
                    animationAsset.DynamicRootNode.PreviewModel = new AssetReference(AssetId.Parse((string)modelAsset.DynamicRootNode.Id), modelAsset.Asset.AssetPath.MakeRelative(animationAsset.Asset.AssetPath.GetParent()));
                }

                // Remove Nodes from models
                foreach (var modelAsset in modelAssets)
                {
                    modelAsset.DynamicRootNode.Nodes = DynamicYamlEmpty.Default;
                    modelAsset.DynamicRootNode["~Base"].Asset.Nodes = DynamicYamlEmpty.Default;
                }

                // Save back
                foreach (var modelAsset in modelAssets)
                {
                    modelAsset.Dispose();
                }
                foreach (var animAsset in animAssets)
                {
                    animAsset.Dispose();
                }
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.6.0-beta"))
            {
                // Delete EffectLogAsset
                foreach (var assetFile in assetFiles)
                {
                    if (assetFile.FilePath.GetFileName() == EffectLogAsset.DefaultFile)
                    {
                        assetFile.Deleted = true;
                    }
                }
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.7.0-alpha02"))
            {
                foreach (var assetFile in assetFiles)
                {
                    if (!IsYamlAsset(assetFile))
                    {
                        continue;
                    }

                    using (var assetYaml = assetFile.AsYamlAsset())
                    {
                        if (assetYaml == null)
                        {
                            continue;
                        }

                        var sourceNode     = assetYaml.DynamicRootNode.Source;
                        var sourceHashNode = assetYaml.DynamicRootNode.SourceHash;
                        if (sourceHashNode != null)
                        {
                            var source     = DynamicYamlExtensions.ConvertTo <UFile>(sourceNode);
                            var sourceHash = DynamicYamlExtensions.ConvertTo <ObjectId>(sourceHashNode);
                            var dictionary = new Dictionary <UFile, ObjectId> {
                                { source, sourceHash }
                            };
                            var yamlDic = DynamicYamlExtensions.ConvertFrom(dictionary);
                            yamlDic.Node.Tag = null;
                            assetYaml.DynamicRootNode["~SourceHashes"] = yamlDic;
                            assetYaml.DynamicRootNode.SourceHash       = DynamicYamlEmpty.Default;
                        }
                        assetYaml.DynamicRootNode.ImporterId           = DynamicYamlEmpty.Default;
                        assetYaml.DynamicRootNode.SourceKeepSideBySide = DynamicYamlEmpty.Default;

                        var assetBase = assetYaml.DynamicRootNode["~Base"];
                        if (assetBase != null)
                        {
                            if (assetBase.Location == "--import--")
                            {
                                assetYaml.DynamicRootNode["~Base"] = DynamicYamlEmpty.Default;
                            }
                        }
                    }
                }
            }

            //Audio refactor
            if (dependency.Version.MinVersion < new PackageVersion("1.7.0-alpha03"))
            {
                var audioAssets = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xksnd").Select(x => x.AsYamlAsset()).ToArray();
                foreach (var assetFile in audioAssets)
                {
                    //dispose will save back
                    using (var assetYaml = assetFile)
                    {
                        if (assetYaml == null)
                        {
                            continue;
                        }

                        if (assetYaml.RootNode.Tag == "!SoundMusic")
                        {
                            assetYaml.RootNode.Tag = "!Sound";
                            assetYaml.DynamicRootNode.Spatialized    = false;
                            assetYaml.DynamicRootNode.StreamFromDisk = true;
                        }
                        else
                        {
                            assetYaml.RootNode.Tag = "!Sound";
                            assetYaml.DynamicRootNode.Spatialized    = true;
                            assetYaml.DynamicRootNode.StreamFromDisk = false;
                        }
                    }
                }
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.7.0-alpha03"))
            {
                // Delete EffectLogAsset (now, most of it is auto generated automatically by drawing one frame of the game)
                foreach (var assetFile in assetFiles)
                {
                    if (assetFile.FilePath.GetFileName() == EffectLogAsset.DefaultFile)
                    {
                        assetFile.Deleted = true;
                    }
                }
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.8.4-beta"))
            {
                // Add new generic parameter of MaterialSurfaceNormalMap to effect logs
                var regex = new Regex(@"(?<=ClassName:\s+MaterialSurfaceNormalMap\s+GenericArguments:\s+\[[^\]]*)(?=\])");
                foreach (var assetFile in assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xkeffectlog"))
                {
                    var filePath = assetFile.FilePath;

                    // Load asset data, so the renamed file will have it's AssetContent set
                    if (assetFile.AssetContent == null)
                    {
                        assetFile.AssetContent = File.ReadAllBytes(filePath);
                    }

                    var sourceText      = System.Text.Encoding.UTF8.GetString(assetFile.AssetContent);
                    var newSourceText   = regex.Replace(sourceText, ", true");
                    var newAssetContent = System.Text.Encoding.UTF8.GetBytes(newSourceText);

                    if (newSourceText != sourceText)
                    {
                        assetFile.AssetContent = newAssetContent;
                    }

                    //File.WriteAllBytes(newFileName, newAssetContent);
                }
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.9.0-beta"))
            {
                foreach (var assetFile in assetFiles)
                {
                    if (!IsYamlAsset(assetFile))
                    {
                        continue;
                    }

                    // This upgrader will also mark every yaml asset as dirty. We want to re-save everything with the new serialization system
                    using (var assetYaml = assetFile.AsYamlAsset())
                    {
                        if (assetYaml == null)
                        {
                            continue;
                        }

                        try
                        {
                            if (assetYaml.DynamicRootNode["~Base"] != null)
                            {
                                var location = ((YamlScalarNode)assetYaml.DynamicRootNode["~Base"].Location.Node).Value;
                                if (location != "--import--")
                                {
                                    var id       = ((YamlScalarNode)assetYaml.DynamicRootNode["~Base"].Asset.Id.Node).Value;
                                    var assetUrl = $"{id}:{location}";
                                    assetYaml.DynamicRootNode["Archetype"] = assetUrl;
                                }
                                assetYaml.DynamicRootNode["~Base"] = DynamicYamlEmpty.Default;
                            }
                        }
                        catch
                        (Exception e)
                        {
                            e.Ignore();
                        }
                    }
                }
            }

            return(true);
        }
Beispiel #3
0
        private void UpgradeNavigationMeshGroups(IEnumerable <PackageLoadingAssetFile> navigationMeshAssets, PackageLoadingAssetFile gameSettingsAsset)
        {
            // Collect all unique groups from all navigation mesh assets
            Dictionary <ObjectId, YamlMappingNode> agentSettings = new Dictionary <ObjectId, YamlMappingNode>();

            foreach (var navigationMeshAsset in navigationMeshAssets)
            {
                using (var navigationMesh = navigationMeshAsset.AsYamlAsset())
                {
                    HashSet <ObjectId> selectedGroups = new HashSet <ObjectId>();
                    foreach (var setting in navigationMesh.DynamicRootNode.NavigationMeshAgentSettings)
                    {
                        var currentAgentSettings = setting.Value;
                        using (DigestStream digestStream = new DigestStream(Stream.Null))
                        {
                            BinarySerializationWriter writer = new BinarySerializationWriter(digestStream);
                            writer.Write((float)currentAgentSettings.Height);
                            writer.Write((float)currentAgentSettings.Radius);
                            writer.Write((float)currentAgentSettings.MaxClimb);
                            writer.Write((float)currentAgentSettings.MaxSlope.Radians);
                            if (!agentSettings.ContainsKey(digestStream.CurrentHash))
                            {
                                agentSettings.Add(digestStream.CurrentHash, currentAgentSettings.Node);
                            }
                            selectedGroups.Add(digestStream.CurrentHash);
                        }
                    }

                    // Replace agent settings with group reference on the navigation mesh
                    navigationMesh.DynamicRootNode.NavigationMeshAgentSettings = DynamicYamlEmpty.Default;
                    dynamic selectedGroupsMapping = navigationMesh.DynamicRootNode.SelectedGroups = new DynamicYamlMapping(new YamlMappingNode());
                    foreach (var selectedGroup in selectedGroups)
                    {
                        selectedGroupsMapping.AddChild(Guid.NewGuid().ToString("N"), selectedGroup.ToGuid().ToString("D"));
                    }
                }
            }

            // Add them to the game settings
            int groupIndex = 0;

            using (var gameSettings = gameSettingsAsset.AsYamlAsset())
            {
                var defaults = gameSettings.DynamicRootNode.Defaults;
                foreach (var setting in defaults)
                {
                    if (setting.Node.Tag == "!Xenko.Navigation.NavigationSettings,Xenko.Navigation")
                    {
                        var groups = setting.Groups as DynamicYamlArray;
                        foreach (var groupToAdd in agentSettings)
                        {
                            dynamic newGroup = new DynamicYamlMapping(new YamlMappingNode());
                            newGroup.Id            = groupToAdd.Key.ToGuid().ToString("D");
                            newGroup.Name          = $"Group {groupIndex++}";
                            newGroup.AgentSettings = groupToAdd.Value;
                            groups.Add(newGroup);
                        }
                    }
                }
            }
        }
Beispiel #4
0
        public override bool Upgrade(PackageSession session, ILogger log, Package dependentPackage, PackageDependency dependency, Package dependencyPackage, IList <PackageLoadingAssetFile> assetFiles)
        {
            // Paradox 1.1 projects didn't have their dependency properly updated (they might have been marked as 1.0).
            // We know they are 1.1 only because there is a .props file.
            // This check shouldn't be necessary from 1.2.
            var packagePath   = dependentPackage.FullPath;
            var propsFilePath = UPath.Combine(packagePath.GetParent(), (UFile)(packagePath.GetFileName() + ".props"));

            if (!File.Exists(propsFilePath) && dependency.Version.MinVersion < new PackageVersion("1.1.0-beta"))
            {
                log.Error("Can't upgrade old projects from {0} 1.0 to 1.1", dependency.Name);
                return(false);
            }

            // Nothing to do for now, most of the work is already done by individual asset upgraders
            // We can later add logic here for package-wide upgrades (i.e. GameSettingsAsset)
            if (dependency.Version.MinVersion < new PackageVersion("1.2.0-beta"))
            {
                // UIImageGroups and SpriteGroups asset have been merged into a single SpriteSheet => rename the assets and modify the tag
                var uiImageGroups = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".pdxuiimage");
                var spritesGroups = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".pdxsprite");
                RenameAndChangeTag(assetFiles, uiImageGroups, "!UIImageGroup");
                RenameAndChangeTag(assetFiles, spritesGroups, "!SpriteGroup");
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.3.0-alpha01"))
            {
                // Create GameSettingsAsset
                GameSettingsAsset.UpgraderVersion130.Upgrade(session, log, dependentPackage, dependency, dependencyPackage, assetFiles);
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.3.0-alpha02"))
            {
                // Delete EffectLogAsset
                foreach (var assetFile in assetFiles)
                {
                    if (assetFile.FilePath.GetFileName() == EffectLogAsset.DefaultFile)
                    {
                        assetFile.Deleted = true;
                    }
                }
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.4.0-beta"))
            {
                // Update file extensions with Xenko prefix
                var legacyAssets = from assetFile in assetFiles
                                   where !assetFile.Deleted
                                   let extension = assetFile.FilePath.GetFileExtension()
                                                   where extension.StartsWith(".pdx")
                                                   select new { AssetFile = assetFile, NewExtension = ".xk" + extension.Substring(4) };

                foreach (var legacyAsset in legacyAssets.ToArray())
                {
                    var assetFile = legacyAsset.AssetFile;
                    var filePath  = assetFile.FilePath;

                    // Load asset data, so the renamed file will have it's AssetContent set
                    if (assetFile.AssetContent == null)
                    {
                        assetFile.AssetContent = File.ReadAllBytes(filePath);
                    }

                    // Change legacy namespaces and default effect names in all shader source files
                    // TODO: Use syntax analysis? What about shaders referenced in other assets?
                    if (legacyAsset.NewExtension == ".xksl" || legacyAsset.NewExtension == ".xkfx" || legacyAsset.NewExtension == ".xkeffectlog")
                    {
                        var sourceText      = System.Text.Encoding.UTF8.GetString(assetFile.AssetContent);
                        var newSourceText   = sourceText.Replace("Paradox", "Xenko");
                        var newAssetContent = System.Text.Encoding.UTF8.GetBytes(newSourceText);

                        if (newSourceText != sourceText)
                        {
                            assetFile.AssetContent = newAssetContent;
                        }

                        // Write SourceCodeAssets to new file, as they are serialized differently
                        // TODO: Handle SourceCodeAssets properly (should probably force saving)
                        var newFileName = new UFile(filePath.FullPath.Replace(filePath.GetFileExtension(), legacyAsset.NewExtension));
                        File.WriteAllBytes(newFileName, newAssetContent);
                    }

                    // Create asset copy with new extension
                    ChangeFileExtension(assetFiles, assetFile, legacyAsset.NewExtension);
                }

                // Force loading of user settings with old extension
                var userSettings = dependentPackage.UserSettings;

                // Change package extension
                dependentPackage.FullPath = new UFile(dependentPackage.FullPath.GetFullPathWithoutExtension(), Package.PackageFileExtension);

                // Make sure all assets are upgraded
                RunAssetUpgradersUntilVersion(log, dependentPackage, XenkoConfig.PackageName, assetFiles, PackageVersion.Parse("1.4.0-beta"));
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.5.0-alpha01"))
            {
                RunAssetUpgradersUntilVersion(log, dependentPackage, XenkoConfig.PackageName, assetFiles, PackageVersion.Parse("1.5.0-alpha01"));
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.5.0-alpha02"))
            {
                // Ideally, this should be part of asset upgrader but we can't upgrade multiple assets at once yet

                var modelAssets = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xkm3d").Select(x => x.AsYamlAsset()).ToArray();
                var animAssets  = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xkanim").Select(x => x.AsYamlAsset()).ToArray();
                var sceneAssets = assetFiles.Where(f => f.FilePath.GetFileExtension() == ".xkscene").Select(x => x.AsYamlAsset()).ToArray();

                // Select models with at least two nodes
                var modelAssetsWithSekeleton = modelAssets
                                               .Where(model => ((IEnumerable)model.DynamicRootNode.Nodes).Cast <object>().Count() > 1).ToArray();

                var animToModelMapping = new Dictionary <PackageLoadingAssetFile.YamlAsset, PackageLoadingAssetFile.YamlAsset>();

                // Find associations in scene
                foreach (var sceneAsset in sceneAssets)
                {
                    var hierarchy = sceneAsset.DynamicRootNode.Hierarchy;
                    foreach (dynamic entity in hierarchy.Entities)
                    {
                        var components         = entity.Entity.Components;
                        var animationComponent = components["AnimationComponent.Key"];
                        var model = components["ModelComponent.Key"]?.Model;
                        if (animationComponent != null && model != null)
                        {
                            var modelReference = DynamicYamlExtensions.ConvertTo <AssetReference <Asset> >(model);
                            var modelAsset     = modelAssetsWithSekeleton.FirstOrDefault(x => x.Asset.AssetPath == modelReference.Location);

                            foreach (var animation in animationComponent.Animations)
                            {
                                var animationReference = DynamicYamlExtensions.ConvertTo <AssetReference <Asset> >(animation.Value);
                                var animationAsset     = animAssets.FirstOrDefault(x => x.Asset.AssetPath == animationReference.Location);

                                if (modelAsset != null && animationAsset != null)
                                {
                                    animToModelMapping[animationAsset] = modelAsset;
                                }
                            }
                        }
                    }
                }

                // Find associations when sharing same source file
                foreach (var animationAsset in animAssets)
                {
                    // Comparing absolute path of assets
                    var modelAsset = modelAssetsWithSekeleton.FirstOrDefault(
                        x => UPath.Combine(animationAsset.Asset.AssetPath.GetParent(), new UFile((string)animationAsset.DynamicRootNode.Source))
                        == UPath.Combine(x.Asset.AssetPath.GetParent(), new UFile((string)x.DynamicRootNode.Source)));
                    if (modelAsset != null)
                    {
                        animToModelMapping[animationAsset] = modelAsset;
                    }
                }

                var modelToSkeletonMapping = new Dictionary <PackageLoadingAssetFile.YamlAsset, PackageLoadingAssetFile.YamlAsset>();

                // For each model asset, create skeleton assets
                foreach (var modelAsset in modelAssetsWithSekeleton)
                {
                    var skeletonAsset = new PackageLoadingAssetFile(modelAsset.Asset.FilePath.GetFullPathWithoutExtension() + " Skeleton.xkskel", modelAsset.Asset.SourceFolder)
                    {
                        AssetContent = System.Text.Encoding.UTF8.GetBytes("!Skeleton\r\nId: " + Guid.NewGuid())
                    };

                    using (var skeletonAssetYaml = skeletonAsset.AsYamlAsset())
                    {
                        // Set source
                        skeletonAssetYaml.DynamicRootNode.Source     = modelAsset.DynamicRootNode.Source;
                        skeletonAssetYaml.DynamicRootNode.SourceHash = modelAsset.DynamicRootNode.SourceHash;

                        // To be on the safe side, mark everything as preserved
                        var nodes = modelAsset.DynamicRootNode.Nodes;
                        foreach (var node in nodes)
                        {
                            node.Preserve = true;
                        }

                        skeletonAssetYaml.DynamicRootNode.Nodes       = nodes;
                        skeletonAssetYaml.DynamicRootNode.ScaleImport = modelAsset.DynamicRootNode.ScaleImport;

                        // Update model to point to this skeleton
                        modelAsset.DynamicRootNode.Skeleton = new AssetReference <Asset>(Guid.Parse((string)skeletonAssetYaml.DynamicRootNode.Id), skeletonAsset.AssetPath.MakeRelative(modelAsset.Asset.AssetPath.GetParent()));
                        modelToSkeletonMapping.Add(modelAsset, skeletonAssetYaml);
                    }

                    assetFiles.Add(skeletonAsset);
                }

                // Update animation to point to skeleton, and set preview default model
                foreach (var animToModelEntry in animToModelMapping)
                {
                    var animationAsset = animToModelEntry.Key;
                    var modelAsset     = animToModelEntry.Value;

                    var skeletonAsset = modelToSkeletonMapping[modelAsset];
                    animationAsset.DynamicRootNode.Skeleton     = new AssetReference <Asset>(Guid.Parse((string)skeletonAsset.DynamicRootNode.Id), skeletonAsset.Asset.AssetPath.MakeRelative(animationAsset.Asset.AssetPath.GetParent()));
                    animationAsset.DynamicRootNode.PreviewModel = new AssetReference <Asset>(Guid.Parse((string)modelAsset.DynamicRootNode.Id), modelAsset.Asset.AssetPath.MakeRelative(animationAsset.Asset.AssetPath.GetParent()));
                }

                // Remove Nodes from models
                foreach (var modelAsset in modelAssets)
                {
                    modelAsset.DynamicRootNode.Nodes = DynamicYamlEmpty.Default;
                    modelAsset.DynamicRootNode[Asset.BaseProperty].Asset.Nodes = DynamicYamlEmpty.Default;
                }

                // Save back
                foreach (var modelAsset in modelAssets)
                {
                    modelAsset.Dispose();
                }
                foreach (var animAsset in animAssets)
                {
                    animAsset.Dispose();
                }
            }

            if (dependency.Version.MinVersion < new PackageVersion("1.6.0-beta"))
            {
                // Delete EffectLogAsset
                foreach (var assetFile in assetFiles)
                {
                    if (assetFile.FilePath.GetFileName() == EffectLogAsset.DefaultFile)
                    {
                        assetFile.Deleted = true;
                    }
                }
            }

            return(true);
        }