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); }
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); } } } } }
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); }